适配移动端的在图片上生成水波纹demo

 
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML5 canvas水波纹动画特效</title>
 
<style type="text/css">
body{
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline; /*background: transparent;*/
box-sizing: border-box;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); //为了防止ios系统出现点击闪烁现象
}
body {
overflow: hidden;
}
 
#holder{
width: 100%;
height: 6.3rem;
position: absolute;
/* cursor: pointer; */
text-align: center;
}
</style>
</head>
<body id="body">
<div id="holder"></div>
 
<script>
window.onload = ()=>{
let outer = document.getElementById('holder').getBoundingClientRect();
//获取canvas,外部容器的宽高,来进行设置canvas的宽高
console.log(outer)
function WaterRipple(element, settings) {
// 默认设置
var defaults = {
image: "",
dropRadius: 3, // 波源半径大小
 '',
height: '',
delay: 1,
attenuation: 3,
maxAmplitude: 1024,
// sourceAmplitude: 512, // 震源振幅
sourceAmplitude: 256, // 震源振幅
auto: false
};
// 合并设置
for (var item in defaults) {
if (!settings.hasOwnProperty(item)) {
settings[item] = defaults[item]
}
}
 
// 检测背景图
if (!settings.image.length) {
return false;
}
 
var width = settings.width,
height = settings.height,
dropRadius = settings.dropRadius,
delay = settings.delay * 1000,
attenuation = settings.attenuation, // 衰减级别
maxAmplitude = settings.maxAmplitude, // 最大振幅
sourceAmplitude = settings.sourceAmplitude,
half_width = width >> 1,
half_height = height >> 1,
amplitude_size = width * (height + 2) * 2,
old_index = width,
new_index = width * (height + 3),
map_index, // 振幅数组索引
texture, // 原始图像像素信息
ripple, // 参数波纹的图像像素信息
image, // Image对象
autoRepeat, // 自动产生波源的重复事件
ripple_map = [],
last_map = [];
 
var canvas = document.createElement('canvas');
canvas.style.width = outer.width+'px';
canvas.style.height = outer.height+'px';
canvas.width = width;
canvas.height = height;
element.appendChild(canvas);
 
var ctx = canvas.getContext('2d');
ctx.fillStyle = settings.bgColor;
ctx.fillRect(0, 0, width, height);
 
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
 
// 加载图片,注意图片不要跨域
function loadImage() {
image = new Image();
// image.src = settings.image;
// image.src = './wallet_top.png';
image.src = './long.jpg';
// image.src = './top@3x.png';
// image.src = './girl.png';
image.crossOrigin = 'anonymous';
image.onload = function() {
console.log(image.style)
init();
}
}
 
// 保存图像的所有像素信息
//将图片绘制在图片尺寸的宽高上
function saveImageData() {
// 在canvas中绘制图形
console.log('width',width)
ctx.drawImage(image, 0, 0,width,height);
// ctx.drawImage(image, 0, 0,375,315);
// 图像的ImageData对象
texture = ctx.getImageData(0, 0, width, height);
ripple = ctx.getImageData(0, 0, width, height);
}
 
function init() {
saveImageData();
// 波幅数组初始化为0
for (var i = 0; i < amplitude_size; i++) {
ripple_map[i] = last_map[i] = 0;
}
 
animate();
// 如果设置了自动产生波源,则随机参数波源
console.log('width',width)
if (settings.auto) {
autoRepeat = setInterval(function() {
disturb(Math.random() * width, Math.random() * height);
}, delay);
disturb(Math.random() * width, Math.random() * height);
}
 
}
 
// 动画主循环
function animate() {
requestAnimationFrame(animate);
renderRipple();
}
 
// 在指定地点产生波源
function disturb(circleX, circleY) {
// 将值向下取整
circleX <<= 0;
circleY <<= 0;
var maxDistanceX = circleX + dropRadius,
maxDistanceY = circleY + dropRadius;
for (var y = circleY - dropRadius; y < maxDistanceY; y++) {
for (var x = circleX - dropRadius; x < maxDistanceX; x++) {
ripple_map[old_index + y * width + x] += sourceAmplitude;
}
}
// console.log('ripple_map',ripple_map)
}
 
// 渲染下一帧
function renderRipple() {
var i = old_index,
deviation_x, // x水平方向偏移
deviation_y, // y竖直方向偏移
pixel_deviation, // 偏移后的ImageData对象像素索引
pixel_source; // 原始ImageData对象像素索引
 
// 交互索引 old_index, new_index
old_index = new_index;
new_index = i;
 
// 设置像素索引和振幅索引
i = 0;
map_index = old_index;
// 使用局部变量优化全局作用域查询
var _map_index = map_index,
_width = width,
_height = height,
_half_width = half_width,
_half_height = half_height,
_ripple_map = ripple_map,
_last_map = last_map,
_ripple_data = ripple.data, // 引用修改
_texture_data = texture.data, // 引用修改
_new_index = new_index,
_attenuation = attenuation,
_maxAmplitude = maxAmplitude;
 
 
// 渲染所有像素点
for (var y = 0; y < _height; y++) {
for (var x = 0; x < _width; x++) {
var x_boundary = 0, judge = _map_index % _width;
if (judge == 0) {
x_boundary = 1; // 左边边界
}else if (judge == _width - 1) {
x_boundary = 2; // 右边边界
}
var top = _ripple_map[_map_index - _width],// 上边的相邻点
bottom = _ripple_map[_map_index + _width],// 下边的相邻点
left = x_boundary != 1 ? _ripple_map[_map_index - 1] : 0,// 左边的相邻点
right = x_boundary != 2 ? _ripple_map[_map_index + 1] : 0;// 右边的相邻点
// 计算当前像素点下一时刻的振幅
var amplitude = (top + bottom + left + right) >> 1;
amplitude -= _ripple_map[_new_index + i];
amplitude -= amplitude >> _attenuation; // 计算衰减
 
// 更新振幅数组
_ripple_map[_new_index + i] = amplitude;
 
amplitude = _maxAmplitude - amplitude;
var old_amplitude = _last_map[i];
_last_map[i] = amplitude;
 
if (old_amplitude != amplitude) {
deviation_x = (((x - _half_width) * amplitude / _maxAmplitude) << 0) + _half_width;
deviation_y = (((y - _half_height) * amplitude / _maxAmplitude) << 0) + _half_height;
 
// 检查边界
if (deviation_x > _width) {
deviation_x = _width - 1;
}
if (deviation_x < 0) {
deviation_x = 0;
}
if (deviation_y > _height) {
deviation_y = _height - 1;
}
if (deviation_y < 0) {
deviation_y = 0;
}
 
pixel_source = i * 4;
// console.error(width)
pixel_deviation = (deviation_x + (deviation_y * width)) * 4;
 
// 移动像素的RGBA信息
_ripple_data[pixel_source] = _texture_data[pixel_deviation];
_ripple_data[pixel_source + 1] = _texture_data[pixel_deviation + 1];
_ripple_data[pixel_source + 2] = _texture_data[pixel_deviation + 2];
// ripple.data[pixel_source + 3] = texture.data[pixel_deviation + 3];
}
++i;
++_map_index;
}
}
 
map_index = _map_index;
ctx.putImageData(ripple, 0, 0);
}
 
function calculAmplitude(index, old_amplitude) {
var x_boundary = 0, judge = map_index % width;
if (judge == 0) {
x_boundary = 1; // 左边边界
}else if (judge == width - 1) {
x_boundary = 2; // 右边边界
}
var top = ripple_map[index - width],// 上边的相邻点
bottom = ripple_map[index + width],// 下边的相邻点
left = x_boundary != 1 ? ripple_map[index - 1] : 0,// 左边的相邻点
right = x_boundary != 2 ? ripple_map[index + 1] : 0;// 右边的相邻点
// 计算当前像素点下一时刻的振幅
var amplitude = top + bottom + left + right;
amplitude >>= 1;
amplitude -= old_amplitude;
amplitude -= amplitude >> attenuation; // 计算衰减
return amplitude;
}
this.disturb = function(a, b) {
console.log('ab',a)
console.log('ab',b)
disturb(a, b);
// disturb(573, 759);
};
loadImage();
return this;
}
 
function init() {
 
 
//Settings - params for WaterRippleEffect
var settings = {
//注意图片不要跨域
image: './girl.png',//image path
// image: './wallettop.png',//image path
dropRadius: 3,//radius of the ripple
//这里设置canvas画布的宽高,可以使用图片的宽高来设置,这样会保证图片清晰程度。
 750,//width
height: 630,//height
//  outer.width,//width
// height: outer.height,//height
delay: 1,//if auto param === true. 1 === 1 second delay for animation
auto: true,//if auto param === true, animation starts on it´s own
attenuation:5,
};
 
//init
var waterRippleEffect = new WaterRipple( document.getElementById( 'holder' ), settings );
// document.getElementById( 'holder' ).style.cursor = 'pointer';
 
//on click
document.getElementById( 'holder' ).addEventListener( 'click', function( e ) {
var mouseX = e.layerX;
var mouseY = e.layerY;
 
console.log('e',e)
let canvas_x = (mouseX/outer.width)*settings.width;
let canvas_y = (mouseY/outer.height)*settings.height;
// waterRippleEffect.disturb( mouseX, mouseY );
waterRippleEffect.disturb( canvas_x, canvas_y );
 
} );
 
 
//on mousemove
// document.getElementById( 'holder' ).addEventListener( 'mousemove', function( e ) {
// var mouseX = e.layerX;
// var mouseY = e.layerY;
// // waterRippleEffect.disturb( mouseX, mouseY );
 
// } );
 
document.onkeydown = function(e) {
var event = e || window.event || arguments.callee.caller.arguments[0];
if(event && event.keyCode==13){ // enter 键
waterRippleEffect.disturb( 200, 200);
 
}
}
}
init();
 
}
 
</script>
</body>
 
主要做了以下几个部分:
1、动态设置canvas的宽高,使其自适应移动端的尺寸
 
2、动态设置波纹出现的位置,
document.getElementById( 'holder' ).addEventListener( 'click', function( e ) {
var mouseX = e.layerX;
var mouseY = e.layerY;
 
console.log('e',e)
//鼠标或者手指在画布css尺寸上的位置来乘以canvas画布的位置来精确生成波纹。
let canvas_x = (mouseX/outer.width)*settings.width;
let canvas_y = (mouseY/outer.height)*settings.height;
// waterRippleEffect.disturb( mouseX, mouseY );
waterRippleEffect.disturb( canvas_x, canvas_y );
 
} );

原文地址:https://www.cnblogs.com/qdcnbj/p/10418665.html