Create Better Divi Headers

Liquid Hover Effect for Divi Button Module

by | May 25, 2017 | 10 comments

Using canvas and JavaScript you can achieve really awesome effects. Recently I’ve come across this cool liquid hover effect and I tried to apply it to Divi button module. It might look like too much code for a single button, but it is experimental and I just wanted to show how it can be implemented for Divi.
Demo:
To implement this effect we’ll have to assign a CSS class to the Button Module and apply neccessary settings, then use Javascript to wrap the button text with span and insert the canvas inside the button and apply the liquid hover effect. Also we’ll use some CSS for canvas and button text.
Step 1: Apply the following button settings. Important ones are the border radius and transparent background color.
Step 2: Add the btn-liquid CSS class to Button Settings -> Advanced -> CSS Class field.
Step 3: This is the JS code snippet for the liquid hover effect. You can set the color stops for the button dynamic gradient and the background color (see highlighted lines in the code below).
<script type="text/javascript">
/* Liquid Button */
(function($) {

               $( ".btn-liquid" ).wrapInner(function() {
                           return "<span class='inner'></span>";
               });

$(function() {
  // Vars
  var pointsA = [],
    pointsB = [],
    $canvas = null,
    canvas = null,
    context = null,
    vars = null,
    points = 8,
    viscosity = 20,
    mouseDist = 70,
    damping = 0.05,
    showIndicators = false;
    mouseX = 0,
    mouseY = 0,
    relMouseX = 0,
    relMouseY = 0,
    mouseLastX = 0,
    mouseLastY = 0,
    mouseDirectionX = 0,
    mouseDirectionY = 0,
    mouseSpeedX = 0,
    mouseSpeedY = 0;

  /**
   * Get mouse direction
   */
  function mouseDirection(e) {
    if (mouseX < e.pageX)
      mouseDirectionX = 1;
    else if (mouseX > e.pageX)
      mouseDirectionX = -1;
    else
      mouseDirectionX = 0;

    if (mouseY < e.pageY)
      mouseDirectionY = 1;
    else if (mouseY > e.pageY)
      mouseDirectionY = -1;
    else
      mouseDirectionY = 0;

    mouseX = e.pageX;
    mouseY = e.pageY;

    relMouseX = (mouseX - $canvas.offset().left);
    relMouseY = (mouseY - $canvas.offset().top);
  }
  $(document).on('mousemove', mouseDirection);

  /**
   * Get mouse speed
   */
  function mouseSpeed() {
    mouseSpeedX = mouseX - mouseLastX;
    mouseSpeedY = mouseY - mouseLastY;

    mouseLastX = mouseX;
    mouseLastY = mouseY;

    setTimeout(mouseSpeed, 50);
  }
  mouseSpeed();

  /**
   * Init button
   */
  function initButton() {
    // Get button
    var button = $('.btn-liquid');
    var buttonWidth = button.outerWidth();
    var buttonHeight = button.outerHeight();

    // Create canvas
    $canvas = $('<canvas></canvas>');
    button.prepend($canvas);

    canvas = $canvas.get(0);
    canvas.width = buttonWidth+100;
    canvas.height = buttonHeight+100;
    context = canvas.getContext('2d');

    // Add points

    var x = buttonHeight/2;
    for(var j = 1; j < points; j++) {
      addPoints((x+((buttonWidth-buttonHeight)/points)*j), 0);
    }
    addPoints(buttonWidth-buttonHeight/5, 0);
    addPoints(buttonWidth+buttonHeight/10, buttonHeight/2);
    addPoints(buttonWidth-buttonHeight/5, buttonHeight);
    for(var j = points-1; j > 0; j--) {
      addPoints((x+((buttonWidth-buttonHeight)/points)*j), buttonHeight);
    }
    addPoints(buttonHeight/5, buttonHeight);

    addPoints(-buttonHeight/10, buttonHeight/2);
    addPoints(buttonHeight/5, 0);
    // addPoints(x, 0);
    // addPoints(0, buttonHeight/2);

    // addPoints(0, buttonHeight/2);
    // addPoints(buttonHeight/4, 0);

    // Start render
    renderCanvas();
  }

  /**
   * Add points
   */
  function addPoints(x, y) {
    pointsA.push(new Point(x, y, 1));
    pointsB.push(new Point(x, y, 2));
  }

  /**
   * Point
   */
  function Point(x, y, level) {
    this.x = this.ix = 50+x;
    this.y = this.iy = 50+y;
    this.vx = 0;
    this.vy = 0;
    this.cx1 = 0;
    this.cy1 = 0;
    this.cx2 = 0;
    this.cy2 = 0;
    this.level = level;
  }

  Point.prototype.move = function() {
    this.vx += (this.ix - this.x) / (viscosity*this.level);
    this.vy += (this.iy - this.y) / (viscosity*this.level);

    var dx = this.ix - relMouseX,
      dy = this.iy - relMouseY;
    var relDist = (1-Math.sqrt((dx * dx) + (dy * dy))/mouseDist);

    // Move x
    if ((mouseDirectionX > 0 && relMouseX > this.x) || (mouseDirectionX < 0 && relMouseX < this.x)) {
      if (relDist > 0 && relDist < 1) {
        this.vx = (mouseSpeedX / 4) * relDist;
      }
    }
    this.vx *= (1 - damping);
    this.x += this.vx;

    // Move y
    if ((mouseDirectionY > 0 && relMouseY > this.y) || (mouseDirectionY < 0 && relMouseY < this.y)) {
      if (relDist > 0 && relDist < 1) {
        this.vy = (mouseSpeedY / 4) * relDist;
      }
    }
    this.vy *= (1 - damping);
    this.y += this.vy;
  };


  /**
   * Render canvas
   */
  function renderCanvas() {
    // rAF
    rafID = requestAnimationFrame(renderCanvas);

    // Clear scene
    context.clearRect(0, 0, $canvas.width(), $canvas.height());
    context.fillStyle = 'transparent';
    context.fillRect(0, 0, $canvas.width(), $canvas.height());

    // Move points
    for (var i = 0; i <= pointsA.length - 1; i++) {
      pointsA[i].move();
      pointsB[i].move();
    }

    // Create dynamic gradient
    var gradientX = Math.min(Math.max(mouseX - $canvas.offset().left, 0), $canvas.width());
    var gradientY = Math.min(Math.max(mouseY - $canvas.offset().top, 0), $canvas.height());
    var distance = Math.sqrt(Math.pow(gradientX - $canvas.width()/2, 2) + Math.pow(gradientY - $canvas.height()/2, 2)) / Math.sqrt(Math.pow($canvas.width()/2, 2) + Math.pow($canvas.height()/2, 2));

    var gradient = context.createRadialGradient(gradientX, gradientY, 300+(300*distance), gradientX, gradientY, 0);
    
    // Set dynamic gradient color stops here
    gradient.addColorStop(0, '#b30be8');
    gradient.addColorStop(1, '#2f0be8');

    // Draw shapes
    var groups = [pointsA, pointsB]

    for (var j = 0; j <= 1; j++) {
      var points = groups[j];

      if (j == 0) {
        // Background style - set button background color here
        context.fillStyle = '#c30c5a';
      } else {
        // Foreground style
        context.fillStyle = gradient;
      }

      context.beginPath();
      context.moveTo(points[0].x, points[0].y);

      for (var i = 0; i < points.length; i++) {
        var p = points[i];
        var nextP = points[i + 1];
        var val = 30*0.552284749831;

        if (nextP != undefined) {
          // if (nextP.ix > p.ix && nextP.iy < p.iy) {
          // 	p.cx1 = p.x;
          // 	p.cy1 = p.y-val;
          // 	p.cx2 = nextP.x-val;
          // 	p.cy2 = nextP.y;
          // } else if (nextP.ix > p.ix && nextP.iy > p.iy) {
          // 	p.cx1 = p.x+val;
          // 	p.cy1 = p.y;
          // 	p.cx2 = nextP.x;
          // 	p.cy2 = nextP.y-val;
          // }  else if (nextP.ix < p.ix && nextP.iy > p.iy) {
          // 	p.cx1 = p.x;
          // 	p.cy1 = p.y+val;
          // 	p.cx2 = nextP.x+val;
          // 	p.cy2 = nextP.y;
          // } else if (nextP.ix < p.ix && nextP.iy < p.iy) {
          // 	p.cx1 = p.x-val;
          // 	p.cy1 = p.y;
          // 	p.cx2 = nextP.x;
          // 	p.cy2 = nextP.y+val;
          // } else {

            p.cx1 = (p.x+nextP.x)/2;
            p.cy1 = (p.y+nextP.y)/2;
            p.cx2 = (p.x+nextP.x)/2;
            p.cy2 = (p.y+nextP.y)/2;

            context.bezierCurveTo(p.x, p.y, p.cx1, p.cy1, p.cx1, p.cy1);
          // 	continue;
          // }

          // context.bezierCurveTo(p.cx1, p.cy1, p.cx2, p.cy2, nextP.x, nextP.y);
        } else {
nextP = points[0];
            p.cx1 = (p.x+nextP.x)/2;
            p.cy1 = (p.y+nextP.y)/2;

            context.bezierCurveTo(p.x, p.y, p.cx1, p.cy1, p.cx1, p.cy1);
        }
      }

      // context.closePath();
      context.fill();
    }

    if (showIndicators) {
      // Draw points
      context.fillStyle = '#000';
      context.beginPath();
      for (var i = 0; i < pointsA.length; i++) {
        var p = pointsA[i];

        context.rect(p.x - 1, p.y - 1, 2, 2);
      }
      context.fill();

      // Draw controls
      context.fillStyle = '#f00';
      context.beginPath();
      for (var i = 0; i < pointsA.length; i++) {
        var p = pointsA[i];

        context.rect(p.cx1 - 1, p.cy1 - 1, 2, 2);
        context.rect(p.cx2 - 1, p.cy2 - 1, 2, 2);
      }
      context.fill();
    }
  }

  // Init
  initButton();
});


})(jQuery);

/* End Liquid Button */

</script>
Add the JS code snippet into the Divi -> Theme Options -> Integration -> Add code to the < body > field.
Step 4: We need to apply some CSS rules to button text and the canvas, for this we’ll use the following CSS snippet:
/* Liquid Button */

.btn-liquid .inner {
  display: block;
  position: relative;
  z-index: 2;
}

.btn-liquid canvas {
  position: absolute;
  top: -50px;
  right: -50px;
  bottom: -50px;
  left: -50px;
  z-index: 1;
}

/* End Liquid Button */
Copy the CSS snippet above and add it into the Divi -> Theme Options -> General -> Custom CSS field.
That’s all, enjoy the effect. Subscribe to stay updated and feel free to share your thoughts and suggestions below! ;)

Subscribe To Our Newsletter

Join our mailing list to download Divi freebies and get notified of our discounts, latest news and updates.

You have Successfully Subscribed! Please confirm your email address.

Divi MadMenu Coming Soon!

Join our mailing list to get notified when the Divi MadMenu module is released! Check out the sneak peek...

You have Successfully Subscribed! Please confirm your email address.

Get FREE Divi Layouts

Download 20+ Divi templates for FREE!

Please confirm your email address to complete your subscription. Thank you!

Black Friday Is Coming!

Subscribe to get notified when our BIGGEST SALE starts!

Please confirm your email address to complete your subscription. Thank you!

Cyber Monday Is Coming!

Subscribe to get notified when the SALE starts!

Please confirm your email address to complete your subscription. Thank you!

Black Friday Is Coming!

Subscribe to get notified when our BIGGEST SALE starts!

Please confirm your email address to complete your subscription. Thank you!