首页 技术 正文
技术 2022年11月12日
0 收藏 577 点赞 4,777 浏览 7652 个字

 

<!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, // 波源半径大小
width: '',
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画布的宽高,可以使用图片的宽高来设置,这样会保证图片清晰程度。
width: 750,//width
height: 630,//height
// width: 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 ); } );3、参考http://uusama.com/643.html

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,488
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,903
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,737
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,489
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,128
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,290