canvas水波纹效果
先看效果
自然界中水波纹效果十分麻烦,我这里只是根据水波纹的几个特性,在理想环境下模拟水波纹的扩散效果。
这里应用到的属性有:扩散、波动、折射。
扩散:很好理解,水波纹会从触发原点开始向周围扩散
波动:水波纹就一直波,在切面上观看,就是一个正弦函数的波形图
折射:光在不同介质中传播速度不同导致出现折射效果。
如果在平静条件下,在垂直方向上看水底事物,很正常。
在波动条件下,因为水的上下波动,导致垂直方向上看到的水底物体,因为波的角度不同,导致水下事物反射的光到人眼的时候,出现一些偏移。
找出这个偏移的算法就是这个效果的精髓所在。
看下图:
基本算法如下:
1、 根据公式,正弦函数的波形上的某个点的切线角度。
sople=cos(len)【sople为斜率,len正弦位置距离原点的位置】
2、 len要根据水波纹的波长除以2π算出一个周期下r的值
len=r*PI*2/waveLen【waveLen:水波一个周期的长度】
sople=cos(r*PI*2/waveLen)
3、 知道斜率,求出切线的倾斜角
sopleDeg=atan(sople)
sopleDeg = (sopleDeg+360)%360
4、 切线的倾斜角区间为0-360度,入射角区间0-90度
inDeg= sopleDeg%90
5、 根据公式,入射角的正弦比上折射角的正弦为折射率
sin(inDeg)/sin(outDeg)=1.3333
sin(outDeg)=sin(inDeg)/1.3333
outDeg=asin(sin(inDeg)/1.3333)
6、 根据如图,偏移角度为入射角减去折射角
shiftDeg=inDeg-outDeg
7、 知道偏移角度和水深可以计算出偏移量
shift=tan(shiftDeg)*depth
8、 将偏移量分解为X轴偏移和Y轴偏移
shiftX=cos(deg)*shift
shiftY=sin(deg)*shift【deg为当前位置和原点位置所成的夹角】
那么把真实位置的像素赋值给期待位置,处理所有的点,这样就得到的水波纹效果。
代码如下:
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" /> <meta charset="UTF-8">
<title>Document</title>
</head>
<body bgcolor="#000000">
<canvas id="canvas"></canvas>
<script type="text/javascript">
(function(){
var canvas = document.getElementById("canvas");
canvas.style.position = "absolute" ;
canvas.style.left = 0 ;
canvas.style.top = 0 ;
var cxt = canvas.getContext("2d");
var imgData ;//原始图片信息
var tempImageData ;//临时图片信息
var depth = 40 ;//水深
var waveLen = 30 ;//水波波长
var step = 0 ;//运动步长
var w , h ;//图片宽高
var sin = Math.sin,
cos = Math.cos,
tan = Math.tan,
atan = Math.atan,
asin = Math.asin,
sqrt = Math.sqrt,
abs = Math.abs ,
round = Math.round,
max = Math.max,
min = Math.min,
PI = Math.PI; var n = 1.3333 ;//n=sin(a1)/sin(a2) a2 = asin(sin(a1)/n) var p = {x:150.01,y:150.01};//水波圆心
var r,slope,slopeDeg,inDeg,outDeg,shiftDeg,dir,shift,shiftX,shiftY,position,tmpDepth;
var radius = 0 ;
var speed = 2 ;
var radiusWidth = 60 ;
var timerAnimate = 0 ;
var damp = 1; document.onmousedown = function(e){
if(timerAnimate)
clearInterval(timerAnimate);
p = {
x:e.pageX + 0.01,
y:e.pageY + 0.01
};
radius = 0 ;
damp = 1 ;
timerAnimate = setInterval(draw,40);
}
function draw(){
var t = new Date;
step += 1;
radius += speed ;
damp -= 0.01;
for(var x = 0 ; x < w ; x ++){
for(var y = 0 ; y < h ; y ++){
var pxObj = getPoint(imgData,x,y);
setPoint(tempImageData,x,y,pxObj);
}
}
cxt.putImageData(tempImageData,0,0);
console.log(new Date - t);
if(damp < 0){
if(timerAnimate)
clearInterval(timerAnimate);
cxt.putImageData(imgData,0,0);
}
}
function getPoint(img,x,y){
r = sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
if(r < radius){
position = (r + step)/waveLen*PI*2;//当前位置的代表弧度
tmpDepth = sin(position)*waveLen/PI/2;//水波动引起的临时深度变化
slope = cos(position);//斜率
slopeDeg = atan(slope);//斜角
slopeDeg = abs(slopeDeg*PI*2)%90/(PI*2);//入射角保证为0-360
inDeg = slopeDeg;//入射角
outDeg = asin(sin(inDeg)/n);//折射角
shiftDeg = inDeg - outDeg ;//偏移角
shift = (x-p.x)/abs(x-p.x)*tan(shiftDeg)*(depth+tmpDepth)*damp ;//偏移量
deg = atan((y-p.y)/(x-p.x));
shiftX = cos(deg)*shift ;//X偏移量
shiftY = sin(deg)*shift ;//Y偏移量
x = round(max(0,min(w-1,x+shiftX)));
y = round(max(0,min(h-1,y+shiftY)));
}
var i = (y*w + x ) * 4 ;
var pxObj = [];
pxObj[0] = img.data[i];
pxObj[1] = img.data[i+1];
pxObj[2] = img.data[i+2];
return pxObj;
}
function setPoint(img,x,y,obj){
var i = (y*canvas.width + x ) * 4 ;
img.data[i] = obj[0];
img.data[i+1] = obj[1];
img.data[i+2] = obj[2];
img.data[i+3] = 255;
} function init(){
var img = new Image();
img.onload = function(){
canvas.width = this.width ;
canvas.height = this.height;
cxt.drawImage(this,0,0,this.width,this.height);
imgData = cxt.getImageData(0,0,this.width,this.height);
tempImageData = cxt.createImageData(imgData);
w = canvas.width ;
h = canvas.height ;
timerAnimate = setInterval(draw,30);
}
img.src = "4.jpg";
}
init();
})() </script>
</body>
</html>
演示地址:http://suohb.com/work/newWater.html
更多特效,关注我的微信公众号:
canvas水波纹效果的更多相关文章
- 如何使用 HTML5 Canvas 制作水波纹效果
今天,我们继续分享 JavaScript 实现的效果例子,这篇文章会介绍使用 JavaScript 实现水波纹效果.水波效果以图片为背景,点击图片任意位置都会触发.有时候,我们使用普通的 Javasc ...
- canvas实现水波纹效果
本文将会从水波的基本原理开始,详细讲解在canvas中模拟水波扩散,分析并计算水波的能量分布,并通过振幅模拟水波对图像的折射效果,最后实现水波特效. 水波基本原理 首先复习一波高中物理知识. 波是指振 ...
- android自定义控件(4)-自定义水波纹效果
一.实现单击出现水波纹单圈效果: 照例来说,还是一个自定义控件,观察这个效果,发现应该需要重写onTouchEvent和onDraw方法,通过在onTouchEvent中获取触摸的坐标,然后以这个坐标 ...
- 自定义view实现水波纹效果
水波纹效果: 1.标准正余弦水波纹: 2.非标准圆形液柱水波纹: 虽说都是水波纹,但两者在实现上差异是比较大的,一个通过正余弦函数模拟水波纹效果,另外一个会运用到图像的混合模式(PorterDuffX ...
- Android 颜色渲染(七) RadialGradient 环形渲染实现水波纹效果
利用环形渲染我们可以做到什么? 其实很多都是非常常见的,比如上一篇实现的帮帮糖效果, 彩色的热气球,比如这里要讲到的水波纹效果,或者也可以理解为扩散色渲染效果 首先看一下效果图: 轻触屏幕,即可看到对 ...
- Android 自定义view实现水波纹效果
http://blog.csdn.net/tianjian4592/article/details/44222565 在实际的开发中,很多时候还会遇到相对比较复杂的需求,比如产品妹纸或UI妹纸在哪看了 ...
- Android自己定义控件系列五:自己定义绚丽水波纹效果
尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...
- 关于自定义view--实现自定义水波纹效果
开发中的东西太多,怕自己忘记了,简单记录一下. 声明:此控件借鉴了大佬的想法,在此感谢大佬提供的支持,我只是把大佬的想法拿出来而已. ok,废话到此结束,看效果: 分析一下,我们可以看到,图中有两个圆 ...
- Android特效专辑(十)——点击水波纹效果实现,逻辑清晰实现简单
Android特效专辑(十)--点击水波纹效果实现,逻辑清晰实现简单 这次做的东西呢,和上篇有点类似,就是用比较简单的逻辑思路去实现一些比较好玩的特效,最近也是比较忙,所以博客更新的速度还得看时间去推 ...
随机推荐
- JAVAEE规范基础知识
JavaEE规范基础知识 本人博客文章网址:https://www.peretang.com/basic-knowledge-of-javaee-standard/ JavaEE简介 JavaEE,J ...
- 【正常向】CODEVS上分黄金
白银上分黄金失败=.= 在之前有很认真的写了一波排序,所以排序并不是很怂,还是那个理,现阶段学习的都是比较简单的排序,都是所谓的冒泡排序啊,桶排序这类,至于插排和选择排序,再往后又是什么快拍就很尬了. ...
- [刷题]算法竞赛入门经典(第2版) 5-8/UVa230 - Borrowers
//又开学啦,不知不觉成为大二的老人了...时间过得好快啊,感觉好颓废... 题意:建立一个借书/归还系统.有借.还.把还的书插到书架上这三个指令. 代码:(Accepted, 0ms) //UVa2 ...
- Java学习笔记——设计模式之三.装饰模式
函数应该做一件事,做好这件事,只做这一件事. --Clean Code 装饰模式,上代码: 先定义零件类: package cn.no3.decorator.template; public abst ...
- flume集群日志收集
一.Flume简介 Flume是一个分布式的.高可用的海量日志收集.聚合和传输日志收集系统,支持在日志系统中定制各类数据发送方(如:Kafka,HDFS等),便于收集数据.其核心为agent,agen ...
- 读《Java并发编程的艺术》(一)
离开博客园很久了,自从找到工作,到现在基本没有再写过博客了.在大学培养起来的写博客的习惯在慢慢的消失殆尽,感觉汗颜.所以现在要开始重新培养起这个习惯,定期写博客不仅是对自己学习知识的一种沉淀,更是在督 ...
- HiveHbase集成实践
作者:Syn良子 出处:http://www.cnblogs.com/cssdongl/p/6857891.html 转载请注明出处 简单的说就是可以通过Hive SQL直接对hbase的表进行读写操 ...
- PHP 类的封装和使用
类:相似的数据和数据操作的封装 class 成员量:普通的量加上一定的修饰就变成了成员量 public,protected,private 成员方法:普通的函数,加上一定的修饰,放入到类中就变成了成 ...
- linux系统管理--进程管理
这两天一直维护公司的服务器,主要对进程管理和linux工作管理,把一些零散的知识整理一下,书归正传~ 什么进程? 以下是百度给的解释的进程,说实话,云里雾里的,其实linux进程和windows进程 ...
- 用C语言模仿Python函数
首先得说明一点,C 语言不是函数式编程语言,要想进行完全的函数式编程,还得先写个虚拟机,然后再写个解释器才行(相当于 CPython ). 下面我们提供一个例子,说明 C 语言函数可以"适度 ...