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