class WaterRippleEffect {
constructor(canvasId, imagePath, width = 1080, height = 720, rippleRadius = 3, updateInterval = 10) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.halfWidth = width >> 1;
this.halfHeight = height >> 1;
this.rippleMapSize = width * (height + 2) * 2;
this.updateInterval = updateInterval;
this.newIndex = width * (height + 3);
this.rippleRadius = rippleRadius;
this.imagePath = imagePath;
this.canvas.width = this.width;
this.canvas.height = this.height;
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = this.imagePath;
this.ctx.drawImage(image, 0, 0, this.width, this.height);
this.texture = this.ctx.getImageData(0, 0, this.width, this.height);
this.rippleData = this.ctx.getImageData(0, 0, this.width, this.height);
for (let i = 0; i < this.rippleMapSize; i++) {
this.rippleMap[i] = this.lastMap[i] = 0;
setInterval(() => this.updateFrame(), this.updateInterval);
setInterval(() => this.createRandomRipple(), 700);
this.disturb(Math.random() * this.width, Math.random() * this.height);
for (let j = y - this.rippleRadius; j < y + this.rippleRadius; j++) {
for (let k = x - this.rippleRadius; k < x + this.rippleRadius; k++) {
this.rippleMap[this.oldIndex + (j * this.width) + k] += 512;
let data, currentPixel, newPixel, oldData;
[this.oldIndex, this.newIndex] = [this.newIndex, this.oldIndex];
let rippleMapIndex = this.oldIndex;
const width = this.width, height = this.height;
const rippleMap = this.rippleMap, lastMap = this.lastMap;
const rippleData = this.rippleData.data, textureData = this.texture.data;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
rippleMap[rippleMapIndex - width] +
rippleMap[rippleMapIndex + width] +
rippleMap[rippleMapIndex - 1] +
rippleMap[rippleMapIndex + 1]
data -= rippleMap[this.newIndex + rippleMapIndex - this.oldIndex];
rippleMap[this.newIndex + rippleMapIndex - this.oldIndex] = data;
oldData = lastMap[rippleMapIndex - this.oldIndex];
lastMap[rippleMapIndex - this.oldIndex] = data;
const offsetX = (((x - this.halfWidth) * data / 1024) | 0) + this.halfWidth;
const offsetY = (((y - this.halfHeight) * data / 1024) | 0) + this.halfHeight;
const boundX = Math.max(0, Math.min(offsetX, width - 1));
const boundY = Math.max(0, Math.min(offsetY, height - 1));
newPixel = (boundX + (boundY * width)) * 4;
currentPixel = (rippleMapIndex - this.oldIndex) * 4;
rippleData[currentPixel] = textureData[newPixel];
rippleData[currentPixel + 1] = textureData[newPixel + 1];
rippleData[currentPixel + 2] = textureData[newPixel + 2];
this.ctx.putImageData(this.rippleData, 0, 0);
const canvas = document.getElementById('c');
canvas.onmousemove = (event) => {
rippleEffect.disturb(event.offsetX || event.layerX, event.offsetY || event.layerY);
canvas.ontouchstart = (event) => {
const touch = event.touches[0];
rippleEffect.disturb(touch.clientX - canvas.offsetLeft, touch.clientY - canvas.offsetTop);
const rippleEffect = new WaterRippleEffect('c', 'bladen.png');
document.getElementById('c').onmousemove = (event) => {
rippleEffect.disturb(event.offsetX || event.layerX, event.offsetY || event.layerY);