<div id="container">
<canvas id="sand" width="700" height="400"></canvas>
<canvas id="text" width="700" height="400"></canvas>
</div>
body {
background: #222;
margin: 0;
}
#container {
position: relative;
}
canvas {
display: block;
margin: auto;
}
#sand {
left: 0;
position: absolute;
top: 0;
right: 0;
z-index: 1;
}
// inspired by https://jsfiddle.net/sdfx/0sk1svu9/
function init() {
// configure text for canvas
var headlineText = 'How far can you walk in 6.5 minutes?';
textCtx.fillStyle = '#eee';
var options = {
font: 'bold 55px sans-serif',
lineHeight: 1,
paddingX: 80,
paddingY: 80,
sizeToFill: true,
renderHDPI: false,
}
// add text to canvas
CanvasTextWrapper(textCanvas, headlineText, options);
// show fps stats
if (typeof Stats === 'function') {
document.body.appendChild((stats = new Stats()).domElement);
}
// generate sand particles
generateSandEffect();
}
function generateSandEffect() {
var imageData = textCtx.getImageData(0, 0, a, s),
imageDataArray = imageData.data,
pixelArray = [];
// push fully visible pixels to pixelArray using alpha values
for(n = 3; n < imageDataArray.length; n+=4) {
if (imageDataArray[n] !== 0) {
pixelArray.push(imageDataArray[n]);
}
}
pixelCount = pixelArray.length;
var t = new Float32Array(5 * pixelCount),
i = 0,
m = 0,
g = 0;
for (; s > g; g++) {
for (var v = 0; a > v; v++) {
if (imageDataArray[4 * m + 3] > 0) {
t[i++] = v + (a - u) / 2;
t[i++] = g + (s - c) / 2;
t[i++] = 0, t[i++] = 0;
t[i++] = imageDataArray[4 * m + 3];
}
m++;
}
}
var mouse = {
x: 0,
y: 0
},
xPos = 0,
yPos = 0,
xDistance = 0,
yDistance = 0;
sandCanvas.onmousemove = function (e) {
xPos = e.clientX - sandCanvas.offsetLeft;
yPos = e.clientY - sandCanvas.offsetTop + window.pageYOffset;
xDistance += xPos - mouse.x;
yDistance += yPos - mouse.y;
mouse.x = xPos;
mouse.y = yPos;
requestTick();
};
sandCanvas.ontouchmove = function (e) {
e.preventDefault();
sandCanvas.onmousemove(e.touches[0]);
};
sandCtx.globalCompositeOperation = "source-over";
function requestTick() {
if(!ticking) {
requestAnimationFrame(step);
ticking = true;
}
}
function step() {
ticking = false;
var r, i, o, l;
sandCtx.clearRect(0, 0, a, s);
var imageData = sandCtx.createImageData(a, s),
c = 0,
d = 0,
h = t.length;
for (; h > d; d += 5) {
r = t[d];
i = t[d + 1];
if (!(0 > r || r > a || 0 > i || i > s)) {
o = t[d + 2];
l = t[d + 3];
var m = f2(r, i, xPos, yPos);
if (f > m) {
o += xDistance * (f - m) * Math.random() * 0.03;
l += yDistance * (f - m) * Math.random() * 0.03;
}
r += o;
i += l;
if (!(0 > r || r > a || 0 > i || i > s)) {
c = 4 * ((Math.floor(i) - 1) * a + Math.floor(r));
imageData.data[c] = p;
imageData.data[c + 1] = p;
imageData.data[c + 2] = p;
imageData.data[c + 3] = t[d + 4];
o *= 0.5;
l *= 0.5;
t[d] = r;
t[d + 1] = i;
t[d + 2] = o;
t[d + 3] = l;
}
}
}
sandCtx.putImageData(imageData, 0, 0);
xDistance *= 0.5;
yDistance *= 0.5;
}
step();
}
function f2(e, t, n, r) {
var i = 0,
o = 0;
i = n - e;
i *= i;
o = r - t;
o *= o;
return Math.sqrt(i + o);
}
window.Float32Array = window.Float32Array || Array;
var sandCanvas = document.getElementById("sand"),
textCanvas = document.getElementById("text"),
sandCtx = sandCanvas.getContext("2d"),
textCtx = textCanvas.getContext("2d"),
a = sandCanvas.clientWidth,
s = sandCanvas.clientHeight,
l = sandCanvas.parentNode.clientWidth,
u = a, // source image data width
c = s, // source image data height
pixelCount = 0, // number of visible pixels
f = 15,
p = 255,
ticking = false;
sandCanvas.width = a;
sandCanvas.height = s;
textCanvas.width = a;
textCanvas.height = s;
init();
function monitor() {
stats.begin();
stats.end();
requestAnimationFrame(monitor);
}
requestAnimationFrame(monitor);
This Pen doesn't use any external CSS resources.