1. Introduction

The React UI library, Material-UI, is similar to Angular’s MaterialUI. When I saw the water wave effect on its button:

In my heart I was thinking:

I’m going to make one happen!

2. Implement

2.1 Analysis Process

The process is still relatively clear, continuous click to generate water waves. But keep the final state of the wave spread out for a long time, but note that each wave is independent. Also, for smooth water wave animation, js requestAnimationFrame is used to implement high brush and Scale is used to implement diffusion.

2.2 code

Next, write the code using Vue3.0 and typescript, which is written as an instruction for ease of use:

const ripple: TRadiumDirective<HTMLElement, boolean> = {
    name: 'ripple'.mounted(el, binding){}};export default ripple;
  
Copy the code

You can see there’s a TRadiumDirective class, which is actually my custom class:

export type TRadiumDirective<T, Y> = Directive<T, Y> & { name: string } & {
  [key: string]: unknown;
};
Copy the code

The life cycle of the instruction used here is Mounted, and we need to perform a series of initialization operations when it is mounted. Then it is to determine the diameter of the water wave, which I did not have a good idea at the beginning, but later I found that the diagonal line is the best diameter to ensure absolute coverage of dom. Then create a DOM container to hold the waves:

const ripple: TRadiumDirective<HTMLElement, boolean> = {
    name: 'ripple'.mounted(el, binding) {
        if(binding.value ! = =undefined && !binding.value) {
          return;
        }
        const rippleContainer = document.createElement('div');
        let diameter = 0;
        rippleContainer.className = 'ra-ripple'; el.appendChild(rippleContainer); }};export default ripple;

Copy the code

Monitor the mousedown of the container and initialize the water wave when the left mouse button is clicked:

const ripple: TRadiumDirective<HTMLElement, boolean> = {
    name: 'ripple'.mounted(el, binding) {
        if(binding.value ! = =undefined && !binding.value) {
          return;
        }
        const rippleContainer = document.createElement('div');
        let diameter = 0;
        rippleContainer.className = 'ra-ripple';
        el.appendChild(rippleContainer);
        
        const startFadeIn = (event: MouseEvent) = > {
          if (event.button === 2) {
            return;
          }
          const clientReact = el.getBoundingClientRect();
          const rippleEl = document.createElement('div');
          diameter = RadiumSqrt(el.clientHeight, el.clientWidth) * 2;
          rippleEl.style.top = event.clientY - clientReact.y + 'px';
          rippleEl.style.left = event.clientX - clientReact.x + 'px';
          setTheRippleEL(rippleEl);
          setTheRippleElSize(rippleEl);
          rippleContainer.appendChild(rippleEl);
          change(rippleEl);
        };
        
        function setTheRippleEL(rippleEl: HTMLElement) {
          if (isNull(rippleEl.onmouseup)) {
            rippleEl.onmouseup = () = > {
              Reflect.set(rippleEl, 'isMouseUp'.true);
            };
            rippleEl.onmouseout = () = > {
              Reflect.set(rippleEl, 'isMouseUp'.true);
            };
          }
          Reflect.set(rippleEl, 'isMouseUp'.false);
          rippleEl.style.transition = `opacity ${translationDuration /
            1000}s  ease-in-out`;
          rippleEl.style.transform = 'translate(-50%,-50%) scale(0) ';
          rippleEl.classList.add('ra-ripple__item');
        }

        function setTheRippleElSize(rippleEl: HTMLElement) {
          rippleEl.style.height = diameter + 'px';
          rippleEl.style.width = diameter + 'px';
        }
        
         on(rippleContainer, 'mousedown', startFadeIn); }};export default ripple;
Copy the code

And finally, when mouseup and mouseout, to end the wave:

const ripple: TRadiumDirective<HTMLElement, boolean> = {
    name: 'ripple'.mounted(el, binding) {
        if(binding.value ! = =undefined && !binding.value) {
          return;
        }
        const rippleContainer = document.createElement('div');
        let diameter = 0;
        rippleContainer.className = 'ra-ripple';
        el.appendChild(rippleContainer);
        
        const startFadeIn = (event: MouseEvent) = > {
          if (event.button === 2) {
            return;
          }
          const clientReact = el.getBoundingClientRect();
          const rippleEl = document.createElement('div');
          diameter = RadiumSqrt(el.clientHeight, el.clientWidth) * 2;
          rippleEl.style.top = event.clientY - clientReact.y + 'px';
          rippleEl.style.left = event.clientX - clientReact.x + 'px';
          setTheRippleEL(rippleEl);
          setTheRippleElSize(rippleEl);
          rippleContainer.appendChild(rippleEl);
          change(rippleEl);
        };
        
        function setTheRippleEL(rippleEl: HTMLElement) {
          if (isNull(rippleEl.onmouseup)) {
            rippleEl.onmouseup = () = > {
              Reflect.set(rippleEl, 'isMouseUp'.true);
            };
            rippleEl.onmouseout = () = > {
              Reflect.set(rippleEl, 'isMouseUp'.true);
            };
          }
          Reflect.set(rippleEl, 'isMouseUp'.false);
          rippleEl.style.transition = `opacity ${translationDuration /
            1000}s  ease-in-out`;
          rippleEl.style.transform = 'translate(-50%,-50%) scale(0) ';
          rippleEl.classList.add('ra-ripple__item');
        }

        function setTheRippleElSize(rippleEl: HTMLElement) {
          rippleEl.style.height = diameter + 'px';
          rippleEl.style.width = diameter + 'px';
        }
        
        function endFadeOut(rippleEl: HTMLElement) {
          rippleEl.style.opacity = '0';
          const timerSign = setTimeout(() = > {
                rippleContainer.removeChild(rippleEl);
                rippleEl.onmouseup = null;
                clearTimeout(timerSign);
      }, 400);
    }

        
         on(rippleContainer, 'mousedown', startFadeIn); }};export default ripple;
Copy the code

After the water wave effect is complete, have a look at the effect:

SCSS in the theme- Brush. SCSS in the gitee address :gitee.com/Agrement/ra… .