JSFiddle - React, Tailwind, and code Playground
by Grzegorz Matyszewski
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenLite.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<canvas id='canvas'></canvas>
CSS
body { margin: 0; padding: 0; }
TypeScript
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
let image: HTMLImageElement;
let data = { y: 0 };
let over = false;
// helpers:
function mix(a, b, l) {
return a + (b - a) * l;
}
function upDown(v: number) {
return Math.sin(v) * 0.5 + 0.5;
}
// preload image:
function preload(): void {
image = new Image();
image.onload = imageLoaded;
image.src = 'https://picsum.photos/id/1014/1280/640';
//'http://tr.huncwot.net/workspace/images/panels/tr_panel-hero-1.jpg';
}
function imageLoaded(): void {
resize();
requestAnimationFrame(render);
}
function resize(): void {
canvas.width = window.innerWidth;
canvas.height = window.innerWidth;
}
// ui listeners:
canvas.onmouseenter = function() {
over = true;
requestAnimationFrame(render);
TweenMax.killTweensOf(data);
TweenMax.to(data, 1, {
y: 1,
ease: Power3.easeOut,
});
};
canvas.onmouseleave = function() {
TweenMax.killTweensOf(data);
TweenMax.to(data, 0.3, {
y: 0,
ease: Back.easeOut.config(1.2),
onComplete: (): void => {
over = false;
},
});
}
window.onresize = function() {
resize();
requestAnimationFrame(render);
};
// main:
function render(time): void {
time *= 0.001;
var t1 = time;
var t2 = time * 0.37;
for (let dstY = 0; dstY < canvas.height; ++dstY) {
var v = dstY / canvas.height;
var off1 = Math.sin((v + 0.5) * mix(3, 12, upDown(t1))) * 300;
var off2 = Math.sin((v + 0.5) * mix(3, 12, upDown(t2))) * 300;
var off = off1 + off2;
var srcY = dstY * image.height / canvas.height + off * data.y;
srcY = Math.max(0, Math.min(image.height - 1, srcY));
context.drawImage(
image,
0, srcY, image.width, 1,
0, dstY, canvas.width, 1);
}
if (!!over) {
requestAnimationFrame(render);
}
}
// run:
preload();