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为当前位置和原点位置所成的夹角】

那么把真实位置的像素赋值给期待位置,处理所有的点,这样就得到的水波纹效果。

代码如下:

  1 <!doctype html>
  2 <html lang="en">
  3 <head>
  4     <meta http-equiv="Pragma" content="no-cache" />
  5     <meta http-equiv="Cache-Control" content="no-cache" />
  6     <meta http-equiv="Expires" content="0" />
  7     <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  8     <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
  9
 10    <meta charset="UTF-8">
 11     <title>Document</title>
 12 </head>
 13 <body bgcolor="#000000">
 14 <canvas id="canvas"></canvas>
 15 <script type="text/javascript">
 16 (function(){
 17 var canvas = document.getElementById("canvas");
 18 canvas.style.position = "absolute" ;
 19 canvas.style.left = 0 ;
 20 canvas.style.top = 0 ;
 21 var cxt = canvas.getContext("2d");
 22 var imgData ;//原始图片信息
 23 var tempImageData ;//临时图片信息
 24 var depth = 40 ;//水深
 25 var waveLen = 30 ;//水波波长
 26 var step = 0 ;//运动步长
 27 var w , h ;//图片宽高
 28 var sin = Math.sin,
 29     cos = Math.cos,
 30     tan = Math.tan,
 31     atan = Math.atan,
 32     asin = Math.asin,
 33     sqrt = Math.sqrt,
 34     abs = Math.abs ,
 35     round = Math.round,
 36     max = Math.max,
 37     min = Math.min,
 38     PI = Math.PI;
 39
 40 var n = 1.3333 ;//n=sin(a1)/sin(a2)    a2 = asin(sin(a1)/n)
 41
 42 var p = {x:150.01,y:150.01};//水波圆心
 43 var r,slope,slopeDeg,inDeg,outDeg,shiftDeg,dir,shift,shiftX,shiftY,position,tmpDepth;
 44 var radius = 0 ;
 45 var speed = 2 ;
 46 var radiusWidth = 60 ;
 47 var timerAnimate = 0 ;
 48 var damp = 1;
 49
 50 document.onmousedown = function(e){
 51     if(timerAnimate)
 52         clearInterval(timerAnimate);
 53     p = {
 54         x:e.pageX + 0.01,
 55         y:e.pageY + 0.01
 56     };
 57     radius = 0 ;
 58     damp = 1 ;
 59     timerAnimate = setInterval(draw,40);
 60 }
 61 function draw(){
 62     var t = new Date;
 63     step += 1;
 64     radius += speed ;
 65     damp -= 0.01;
 66     for(var x = 0 ; x < w ; x ++){
 67         for(var y = 0 ; y < h ; y ++){
 68             var pxObj = getPoint(imgData,x,y);
 69             setPoint(tempImageData,x,y,pxObj);
 70         }
 71     }
 72     cxt.putImageData(tempImageData,0,0);
 73     console.log(new Date - t);
 74     if(damp < 0){
 75         if(timerAnimate)
 76             clearInterval(timerAnimate);
 77         cxt.putImageData(imgData,0,0);
 78     }
 79 }
 80 function getPoint(img,x,y){
 81     r = sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
 82     if(r < radius){
 83         position = (r + step)/waveLen*PI*2;//当前位置的代表弧度
 84         tmpDepth = sin(position)*waveLen/PI/2;//水波动引起的临时深度变化
 85         slope = cos(position);//斜率
 86         slopeDeg = atan(slope);//斜角
 87         slopeDeg = abs(slopeDeg*PI*2)%90/(PI*2);//入射角保证为0-360
 88         inDeg = slopeDeg;//入射角
 89         outDeg = asin(sin(inDeg)/n);//折射角
 90         shiftDeg = inDeg - outDeg ;//偏移角
 91         shift = (x-p.x)/abs(x-p.x)*tan(shiftDeg)*(depth+tmpDepth)*damp ;//偏移量
 92         deg = atan((y-p.y)/(x-p.x));
 93         shiftX = cos(deg)*shift ;//X偏移量
 94         shiftY = sin(deg)*shift ;//Y偏移量
 95         x = round(max(0,min(w-1,x+shiftX)));
 96         y = round(max(0,min(h-1,y+shiftY)));
 97     }
 98     var i = (y*w + x ) * 4 ;
 99     var pxObj = [];
100     pxObj[0] = img.data[i];
101     pxObj[1] = img.data[i+1];
102     pxObj[2] = img.data[i+2];
103     return pxObj;
104 }
105 function setPoint(img,x,y,obj){
106     var i = (y*canvas.width + x ) * 4 ;
107     img.data[i] = obj[0];
108     img.data[i+1] = obj[1];
109     img.data[i+2] = obj[2];
110     img.data[i+3] = 255;
111 }
112
113 function init(){
114     var img = new Image();
115     img.onload = function(){
116         canvas.width = this.width ;
117         canvas.height = this.height;
118         cxt.drawImage(this,0,0,this.width,this.height);
119         imgData = cxt.getImageData(0,0,this.width,this.height);
120         tempImageData = cxt.createImageData(imgData);
121          w = canvas.width ;
122         h = canvas.height ;
123         timerAnimate = setInterval(draw,30);
124    }
125    img.src = "4.jpg";
126 }
127 init();
128 })()
129
130 </script>
131 </body>
132 </html>

演示地址:http://suohb.com/work/newWater.html

更多特效,关注我的微信公众号:

时间: 2024-11-03 01:22:07

canvas水波纹效果的相关文章

如何使用 HTML5 Canvas 制作水波纹效果

原文:如何使用 HTML5 Canvas 制作水波纹效果 今天,我们继续分享 JavaScript 实现的效果例子,这篇文章会介绍使用 JavaScript 实现水波纹效果.水波效果以图片为背景,点击图片任意位置都会触发.有时候,我们使用普通的 Javascript 就可以创建一个很有趣的解决功能. 在线演示      源码下载 Step 1. HTML 和以前一样,首先是 HTML 代码: <!DOCTYPE html> <html> <head> <meta

自定义view实现水波纹效果

今天看到一篇自定view 实现水波纹效果 觉得真心不错 学习之后再次写下笔记和心得.但是感觉原作者写得有些晦涩难懂,也许是本人愚笨 所以重写此作者教程.原作者博文大家可以去看下,感觉他在自定义view方面非常厉害,本文是基于此作者原文重新改写,拥有大量像相似部分 先看下效果吧: 1. 效果1: 2. 效果2 我先们来学习效果1: 效果1实现本质:用一张波形图和一个圆形图的图片,然后圆形图在波形图上方,然后使用安卓的图片遮罩模式desIn(不懂?那么先记住有这样一个遮罩模式).(只显示上部图像和下

Android 实现RippleEffect水波纹效果

最近看到360.UC.网易新闻客户端都应用了水波纹效果,就在私下里也研究了一下,参照GIT上大神的分享,自己也跟着做了一个示例,下面先看效果: 1.RippleEffect核心实现类 package com.example.RippleEffect; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphi

Android自定义View——实现水波纹效果类似剩余流量球

最近突然手痒就想搞个贝塞尔曲线做个水波纹效果玩玩,终于功夫不负有心人最后实现了想要的效果,一起来看下吧: 效果图镇楼 一:先一步一步来分解一下实现的过程 需要绘制一个正弦曲线(sin)或者余弦曲线(cos) 通过水平平移曲线来的到像水波波动的效果 水平移动的同时还需要有水位上涨,也就是向上平移 裁剪画布为圆形,在圆形区域绘制曲线 通过上面4步就可以实现了 二:现在就来实现第一步,绘制一个sin曲线:这里画了一张图来帮助理解,在PhotoShop中我们绘制一个贝塞尔曲线可以清楚的看到它的控制点如图

自定义控件三部曲之绘图篇(六)——Path之贝赛尔曲线和手势轨迹、水波纹效果

前言:好想义无反顾地追逐梦想 相关文章:<Android自定义控件三部曲文章索引> 从这篇开始,我将延续androidGraphics系列文章把图片相关的知识给大家讲完,这一篇先稍微进阶一下,给大家把<android Graphics(二):路径及文字>略去的quadTo(二阶贝塞尔)函数,给大家补充一下. 本篇最终将以两个例子给大家演示贝塞尔曲线的强大用途: 1.手势轨迹 利用贝塞尔曲线,我们能实现平滑的手势轨迹效果 2.水波纹效果 电池充电时,有些手机会显示水波纹效果,就是这样

Android特效专辑(十)——点击水波纹效果实现,逻辑清晰实现简单

Android特效专辑(十)--点击水波纹效果实现,逻辑清晰实现简单 这次做的东西呢,和上篇有点类似,就是用比较简单的逻辑思路去实现一些比较好玩的特效,最近也是比较忙,所以博客更新的速度还得看时间去推演,但是也能保证一周三更的样子,现在也还是以小功能,或者说是一些小入门级别的博客为主,我也不算是什么很厉害的人,很多细节的支持处理的仍然还是不到位,所以也是一直在弥补,话不多说,来看看今天的效果 实现起来很简单吧,那我们就来看一下他是怎么实现的咯! OnclickRuning package com

[转]Android自定义控件系列五:自定义绚丽水波纹效果

出处:http://www.2cto.com/kf/201411/353169.html 今天我们来利用Android自定义控件实现一个比较有趣的效果:滑动水波纹.先来看看最终效果图: 图一 效果还是很炫的:饭要一口口吃,路要一步步走,这里我们将整个过程分成几步来实现 一.实现单击出现水波纹单圈效果: 图二 照例来说,还是一个自定义控件,这里我们直接让这个控件撑满整个屏幕(对自定义控件不熟悉的可以参看我之前的一篇文章:Android自定义控件系列二:自定义开关按钮(一)).观察这个效果,发现应该

Android Ripple 按钮水波纹效果(二)优化

上一篇中我们讲了自定义ripple 水波纹效果,先来回顾一下效果吧! 看了以后感觉没甚么问题,我一开始也觉得很满意了,那好,我们拿Android 5.0自带的效果来对比一下 发现了不同之处没?点击中间的时候是看不出什么区别,但是点击两边的时候,就很明显了,我们自定义的效果,波纹向两边同速度的扩散,所以就会出现,如果点击点不在中心的时候,距离短的一边波纹先到达,而距离长的一边后到达,不能同时到达边缘!而系统自带的则不存在这种情况,所以这是一个优化点;另一个优化点是:我们自定义的效果,在波纹全部覆盖

Android自定义控件-Path之贝赛尔曲线和手势轨迹、水波纹效果

从这篇开始,我将延续androidGraphics系列文章把图片相关的知识给大家讲完,这一篇先稍微进阶一下,给大家把<android Graphics(二):路径及文字>略去的quadTo(二阶贝塞尔)函数,给大家补充一下. 本篇最终将以两个例子给大家演示贝塞尔曲线的强大用途: 1.手势轨迹 利用贝塞尔曲线,我们能实现平滑的手势轨迹效果 2.水波纹效果 电池充电时,有些手机会显示水波纹效果,就是这样做出来的. 废话不多说,开整吧 一.概述 在<android Graphics(二):路径