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! ;)










Thanks for this! Just curious – is there a way to do this with a border radius of 0?
Hi , this is an awesome effect. Can it only be used on on button ? I got it working but when I add the class to other buttons the effect only works on the last button the class was added to .
It was great but I am getting a new error so had to remove it, the error is:
(index):2131 Uncaught TypeError: Failed to execute ‘createRadialGradient’ on ‘CanvasRenderingContext2D’: The provided double value is non-finite.
at renderCanvas ((index):2131)
renderCanvas @ (index):2131
Not sure why…
Hmm… interesting. Need to test it more, thanks for letting me know.
This is so freaking awesome! Thanks for sharing!
You’re welcome, Dan ;)
Awesome, Is it possible to add this to a button on the slider?
Haven’t tried it but it should work, you only need to adjust the CSS selectors to target the slider button for
canvasinsertion and maybe apply some CSS rules to the button.Pretty cool. It would be exponentially cooler with sound. :)
Yeah, splash sounds would be awesome :)