<!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 );
 
} );

适配移动端的在图片上生成水波纹demo的更多相关文章

  1. 移动端H5实现图片上传

    移动端H5实现图片上传 https://segmentfault.com/a/1190000010034177

  2. php背景图片上生成二维码,二维码上带logo 代码示例 (原)

    依赖库文件 phpqrcode.php (下载地址://www.jb51.net/codes/189897.html :或者在官网下载:http://phpqrcode.sourceforge.net ...

  3. 移动端 js 实现图片上传 预览

    方法一: <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv=&q ...

  4. canvas在图片上生成文字

    newImage(text) {                 // 生成图片                 var imageBox = document.getElementById(&quo ...

  5. IOS开发-图片上传

    目前IOS端开发,图片上传到服务器分为两种,一种是直接上到服务器,一种是借助第三方储存(减少服务器压力). 一.直接上传到服务器 /** * 代码演示 */ //*******UIImagePNGRe ...

  6. 一个简单的安卓+Servlet图片上传例子

    例子比较 简单,服务端为Java Web Servlet,doPost方法中接收图片并保存,然后将保存的图片名返回给客户端,关键代码: @SuppressWarnings("deprecat ...

  7. 本地图片上传与H5适配知识

    最近用到本地图片上传作为API的参数,在网上看了许多,记录一下,以后可能用的着(仅自己记录用,看不清请绕路) function getObjectURL(file) { var url = null ...

  8. spring mvc 图片上传,图片压缩、跨域解决、 按天生成文件夹 ,删除,限制为图片代码等相关配置

    spring mvc 图片上传,跨域解决 按天生成文件夹 ,删除,限制为图片代码,等相关配置 fs.root=data/ #fs.root=/home/dev/fs/ #fs.root=D:/fs/ ...

  9. HTML5移动端图片上传模块

    上传图片的功能是极为常用的,之前做过一个移动端上传文件的功能(基于jquery的),总结梳理一下. html <div class="uploadPic clearBox"& ...

随机推荐

  1. 机器学习(Machine Learning)

    机器学习(Machine Learning)是一门专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能的学科.

  2. 用vue2.0实现点击选中active,其他选项互斥的效果

    在正常的js中.我们如果要实现点击选中active然后其他取消的效果,我们可以定义一个类,当点击的时候给给多有的dom取消active的类,给当前元素加上这个类名,说的很啰嗦,直接来看代码说话吧(表示 ...

  3. flask模块

    import flask, json #把当前的文件当成一个服务 server = flask.Flask(__name__) @server.route('/index', methods=['ge ...

  4. poj_1860 SPFA

    题目大意 有N种货币,M个兑换点,每个兑换点只能相互兑换两种货币.设兑换点A可以兑换货币C1和C2,给出rate(C1--C2)表示1单位的C1货币可以兑换出的C2货币数目,rate(C2--C1)表 ...

  5. warning: Now you can provide attr "wx:key" for a "wx:for" to improve performance.

    小程序开发过程中在写for循环的时候会出现如下报错 warning: Now you can provide attr "wx:key" for a "wx:for&qu ...

  6. ThreadLocal分析总结:

    1.ThreadLocal 是什么 它是一个数据结构,像 HashMap,可保存 "key : value" 键值对:ThreadLocal 有一个内部类ThreadLocalMa ...

  7. IOS学习笔记48--一些常见的IOS知识点+面试题

      IOS学习笔记48--一些常见的IOS知识点+面试题   1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...

  8. node.js使用require给flume提交请求

      node.js使用require给flume提交请求 - 简书 https://www.jianshu.com/p/02c20e2d011a     玄月府的小妖在debug 关注 2017.04 ...

  9. 常用移动web开发框架--转载

      阅读目录 1.1 jQuery mobile flat-ui 主题 1.2jQuery mobile Bootstrap 主题 4.1 GMU 4.2 Clouda+ 4.3 efe 5.1 Sp ...

  10. glibc-2.23_large_bin链接方式_浅析

    上面两个图应该合并为一个图,但是指针太多了,合并在一起看不清了,所以分成两张图 所有的块都通过fd和bk两个指针链接,但是为了加快查询速度,不同大小的chunk通过fd_nextsize和bk_nex ...