前言

从我接触canvas的第一天就觉得canvas很有趣,想搞点事情,这几天终于忍不住了,于是他来了。

先看效果

                     

这里我做了四个大家有兴趣可以看完文章,做一个自己喜欢的动画。

思路

开始做之前,我们先分析一下这种粒子动画实现的原理,绘制的内容是由许多个带有颜色像素点构成,每个像素点在画布上都有自己的坐标。首先获取到要绘制的内容的像素点信息的数组(目标数组)例如

  1. [
  2. {x:10, y:20, color: 'rgba(255, 122, 122)'},
  3. {x:11, y:20, color: 'rgba(255, 122, 122)'},
  4. {x:12, y:20, color: 'rgba(255, 122, 122)'},
  5. ]

然后我们就可以让这些像素点从某些特定的位置,以某种特定的方式,移动到目标位置,动画就完成了。

实现

1.获取目标数组

我们先说一下 canvas 的 getImageData() ,该方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。

对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:

  • R - 红色 (0-255)
  • G - 绿色 (0-255)
  • B - 蓝色 (0-255)
  • A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)

真实样子是这个样子的

0,1,2,3 4,5,6,7 8,9,10,11
12,13,14,15 16,17,18,19 20,21,22,23

每四个值为一组,用来表示一个像素点的信息,每一个单元格代表一个像素。

先在一个canvas中绘制想要的内容,通过getImageData()获得像素信息,我们发现ImageData 对象的信息和我们想象中的目标数组不大一样,我们要将ImageData对象处理一下,我们将其每四个划分为一组,重新定义索引,例如我们在一个12px宽的画布中,经过分析不难发现坐标与索引之间的关系,分两种情况 n<12(画布的宽度) 时坐标为((n+1)%12, n+1),n>12时坐标为((n+1)%12, parseInt((n+1)/ 12))

0(0,0) 1(0,1) .. n((n+1)%12, n+1) 11(0,11)
.. ..       ..          ..           ..            
     

n((n+1)%12, parseInt((n+1)/ 12))

 

到这里功能是实现了,但是如果操作的内容很大,像素点很多,后期操作的像素点越多性能就越差,有没有什么办法可以稀释一下这些像素呢,当然有!我们可以隔一个像素取一个像素,这样像素点瞬间就减少了一倍,同理我们隔两个隔三个隔n个,这样我们就可以定义一个参数用来控制像素的稀释度

下面的事情就简单了,用代码实来现这一步

  1. /*
  2. * @ ImageDataFormat
  3. * @ param { pixels 需要格式化的ImageData对象, n 稀释度 }
  4. * @ return { Array }
  5. */
  6.  
  7. function ImageDataFormat(pixels, n){
        n = n*4
  8. var arr = [], //目标数组
  9.     temPixel = {}, //目标数组中存放像素信息的对象
  10.     x = 0, //像素的x坐标
  11.     y = 0 //像素的y坐标
  12. for (var i=0;i<pixels.data.length;i+=n){
  13.       //过滤纯色背景提高性能,如背景色不可去掉可省略判断
  14. if(pixels.data[i] !== 0 || pixels.data[i+1] !== 0 || pixels.data[i+2] !== 0 ){
  15. var index = (i+1) / 4 //每四个划分为一组,重新定义索引
  16. if(index > timeDom.width){
  17. x = index % timeDom.width
  18. y = parseInt(index / timeDom.width)
  19. }else{
  20. x = index
  21. y = 0
  22. }
  23. temPixel = {
  24. R: pixels.data[i],
  25. G: pixels.data[i+1],
  26. B: pixels.data[i+2],
  27. A: pixels.data[i+3],
  28. I:i,
  29. X:x,
  30. Y:y
  31. }
  32.  
  33. arr.push(temPixel)
  34. }
  35.  
  36. }
        return arr
  37.  
  38. }

2.将目标数组绘制到画布上

2.1在画布的指定位置画一个圆(一个像素点)

  1. /**
  2. * @ drawArc
  3. * @ param{ ctx 画布,,x x坐标,y y坐标,color 颜色}
  4. */
  5. function drawArc(ctx, x, y, color){
  6. x = x
  7. y = y
  8. ctx.beginPath();
  9. ctx.fillStyle = color
  10. ctx.strokeStyle = color
  11. ctx.arc(x,y,0.5,0,2*Math.PI);
  12. ctx.closePath()
  13. ctx.fill()
  14. }

2.1将点连成线,线构成面

  1. /**
  2. * 画路径
  3. * @param { points 格式化好的目标数组, crx 画布}
  4. */
  5. function draw_path(points, ctx){
  6.  
  7.   for(var i=0;i < points.length-1;i++){
  8.  
  9.     var color = 'rgba(' + points[i].R + ',' + points[i].G + ',' + points[i].B + ')', x, y
  10. drawArc(ctx,points[i].X,points[i].Y, color)
  11. }
  12. }

到此我们就画出了动画的其中一帧,下面我们就要让这一帧动起来

2.2动起来

我们的动画进行其实很简单

1.画第一帧

2.清空画布

3.画下一帧

4.在清空

....

但是想让这个动画流畅的进行起来我们还要在了解一下tween(缓动动画), window.requestAnimationFrame()

tween 我们值列举一种其他 形式感兴趣的可以自己查一下

  1. /*
  2. * @ 参数描述
  3. * @ t 动画执行到当前帧所经过的时间
  4. * @ b 起始值
  5. * @ c 总位移值
  6. * @ d 持续时间
  7. */
  8. function easeInOutExpon(t,b,c,d){
  9. if (t==0) return b;
  10. if (t==d) return b+c;
  11. if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
  12. return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
  13. }

window.requestAnimationFrame()

准备工作做好了可以开工了

我么只需要将前面的函数稍微改动一下他就动起来了

  1. function ShowTimeInit(pixels, n){
  2. n = 4*n
  3. var arr = [], temPixel = {}, x = 0, y = 0
  4. for (var i=0;i<pixels.data.length;i+=n){
  5. if(pixels.data[i] !== 0 || pixels.data[i+1] !== 0 || pixels.data[i+2] !== 0 ){
  6. var index = parseInt ((i+1) / 4)
  7. if(index > timeDom.width){
  8. x = index % timeDom.width
  9. y = parseInt(index / timeDom.width)
  10. }else{
  11. x = index
  12. y = 0
  13. }
  14. temPixel = {
  15. R: pixels.data[i],
  16. G: pixels.data[i+1],
  17. B: pixels.data[i+2],
  18. A: pixels.data[i+3],
  19. I:i,
  20. X:x,
  21. Y:y
  22. }
  23.  
  24. arr.push(temPixel)
  25. }
  26.  
  27. }
  28. var step = requestAnimationFrame(function(){draw_path(arr, ShowTime, step)})
  29.  
  30. }
  31. /**
  32. * 画路径
  33. * @param path 路径
  34. */
  35. function draw_path(points, ctx, step){
  36. ShowTime.clearRect(0,0,ShowTimeDom.width,ShowTimeDom.height);
  37. var pointX, pointY, randomX, randomY
  38. for(var i=0;i < points.length-1;i++){
  39. switch (mode){
  40. case 'left':
  41. pointX = randomNum(0,0)
  42. pointY = randomNum(0,100)
  43. randomX = 0
  44. randomY = Math.random() + Math.random()*3000
  45. break;
  46. case 'center':
  47. pointX = 80
  48. pointY = 50
  49. randomX = Math.random() + Math.random()*3000
  50. randomY = Math.random() + Math.random()*3000
  51. break;
  52. case 'random':
  53. pointX = 0
  54. pointY = 0
  55. randomX = Math.random() + Math.random()*3000
  56. randomY = Math.random() + Math.random()*3000
  57. break;
  58. case 'flow':
  59. pointX = 0
  60. pointY = 0
  61. randomX = i
  62. randomY = i
  63. break;
  64. }
  65.  
  66. var color = 'rgba(' + points[i].R + ',' + points[i].G + ',' + points[i].B + ')', x, y
  67. x = easeInOutExpon(nowDuration + randomX, pointX, points[i].X-pointX, duration)
  68. y = easeInOutExpon(nowDuration + randomY, pointY, points[i].Y-pointY, duration)
  69. drawArc(ctx,x, y, color)
  70.  
  71. }
  72. nowDuration += 1000/60
  73. if(duration <= nowDuration){
  74. window.cancelAnimationFrame(step);
  75. }else{
  76. requestAnimationFrame(function(){draw_path(points, ctx, step)})
  77. }
  78.  
  79. }

附上完整代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <style>
  8.  
  9. </style>
  10. <title>电子时钟</title>
  11. </head>
  12. <body>
  13. <canvas id="HidenTime" width="300" height="100" style="display: none"> </canvas>
  14. <canvas id="ShowTime" width="300" height="100"> </canvas>
  15. </body>
  16. <script>
  17. function GetTime(){
  18. this._Hours = ''
  19. this._Minutes = ''
  20. this._Seconds = ''
  21. }
  22. GetTime.prototype = {
  23. constructor: GetTime,
  24. get Hours(){
  25. this._Hours = new Date().getHours()
  26. if(this._Hours > 9){
  27. return this._Hours
  28. }else{
  29. return "0" + this._Hours
  30. }
  31. },
  32. get Minutes(){
  33. this._Minutes = new Date().getMinutes()
  34. if(this._Minutes > 9){
  35. return this._Minutes
  36. }else{
  37. return "0" + this._Minutes
  38. }
  39. },
  40. get Seconds(){
  41. this._Seconds = new Date().getSeconds()
  42. if(this._Seconds > 9){
  43. return this._Seconds
  44. }else{
  45. return "0" + this._Seconds
  46. }
  47. },
  48. formTime:function(){
  49. return this.Hours + ':' + this.Minutes + ':' + this.Seconds
  50. }
  51. }
  52. var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
  53. window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  54. var cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
  55. var duration = 3000, nowDuration = 0
  56. var timeDom = document.getElementById("HidenTime")
  57. var time = timeDom.getContext('2d')
  58. var ShowTimeDom = document.getElementById("ShowTime")
  59. var ShowTime = ShowTimeDom.getContext('2d')
  60. time.clearRect(0,0,timeDom.width,timeDom.height);
  61. var nowTime = new GetTime()
  62. var showTime = nowTime.formTime()
  63. var modes = ['left', 'random', 'center', 'flow']
  64. var mode = modes[0]
  65. time.font="50px Verdana";
  66. // 创建渐变
  67. var gradient=time.createLinearGradient(0,0,timeDom.width,0);
  68. gradient.addColorStop("0","magenta");
  69. gradient.addColorStop("0.5","blue");
  70. gradient.addColorStop("1.0","red");
  71. // 用渐变填色
  72. time.fillStyle=gradient;
  73. time.fillText(showTime,10,60);
  74. var pixels = time.getImageData(0,0,300,100)
  75. ShowTimeInit(pixels, 2)
  76. setInterval(function(){
  77.  
  78. mode = modes[randomNum(0,3)]
  79. //mode = modes[3]
  80. time.clearRect(0,0,timeDom.width,timeDom.height);
  81. nowDuration = 0
  82. showTime = nowTime.formTime()
  83. time.fillText(showTime,10,60);
  84. pixels = time.getImageData(0,0,300,100)
  85. ShowTimeInit(pixels, 2)
  86. }, 5000)
  87. function ShowTimeInit(pixels, n){
  88. n = 4*n
  89. var arr = [], temPixel = {}, x = 0, y = 0
  90. for (var i=0;i<pixels.data.length;i+=n){
  91. if(pixels.data[i] !== 0 || pixels.data[i+1] !== 0 || pixels.data[i+2] !== 0 ){
  92. var index = parseInt ((i+1) / 4)
  93. if(index > timeDom.width){
  94. x = index % timeDom.width
  95. y = parseInt(index / timeDom.width)
  96. }else{
  97. x = index
  98. y = 0
  99. }
  100. temPixel = {
  101. R: pixels.data[i],
  102. G: pixels.data[i+1],
  103. B: pixels.data[i+2],
  104. A: pixels.data[i+3],
  105. I:i,
  106. X:x,
  107. Y:y
  108. }
  109.  
  110. arr.push(temPixel)
  111. }
  112.  
  113. }
  114. var step = requestAnimationFrame(function(){draw_path(arr, ShowTime, step)})
  115.  
  116. }
  117. /**
  118. * 画路径
  119. * @param path 路径
  120. */
  121. function draw_path(points, ctx, step){
  122. ShowTime.clearRect(0,0,ShowTimeDom.width,ShowTimeDom.height);
  123. var pointX, pointY, randomX, randomY
  124. for(var i=0;i < points.length-1;i++){
  125. switch (mode){
  126. case 'left':
  127. pointX = randomNum(0,0)
  128. pointY = randomNum(0,100)
  129. randomX = 0
  130. randomY = Math.random() + Math.random()*3000
  131. break;
  132. case 'center':
  133. pointX = 80
  134. pointY = 50
  135. randomX = Math.random() + Math.random()*3000
  136. randomY = Math.random() + Math.random()*3000
  137. break;
  138. case 'random':
  139. pointX = 0
  140. pointY = 0
  141. randomX = Math.random() + Math.random()*3000
  142. randomY = Math.random() + Math.random()*3000
  143. break;
  144. case 'flow':
  145. pointX = 0
  146. pointY = 0
  147. randomX = i
  148. randomY = i
  149. break;
  150. }
  151.  
  152. var color = 'rgba(' + points[i].R + ',' + points[i].G + ',' + points[i].B + ')', x, y
  153. x = easeInOutExpon(nowDuration + randomX, pointX, points[i].X-pointX, duration)
  154. y = easeInOutExpon(nowDuration + randomY, pointY, points[i].Y-pointY, duration)
  155. drawArc(ctx,x, y, color)
  156.  
  157. }
  158. nowDuration += 1000/60
  159. if(duration <= nowDuration){
  160. window.cancelAnimationFrame(step);
  161. }else{
  162. requestAnimationFrame(function(){draw_path(points, ctx, step)})
  163. }
  164.  
  165. }
  166. /**
  167. * 画圆
  168. */
  169. function drawArc(ctx, x, y, color){
  170. x = x
  171. y = y
  172. ctx.beginPath();
  173. ctx.fillStyle = color
  174. ctx.strokeStyle = color
  175. ctx.arc(x,y,0.5,0,2*Math.PI);
  176. ctx.closePath()
  177. ctx.fill()
  178. }
  179.  
  180. /*
  181. * 参数描述
  182. * t 动画执行到当前帧所经过的时间
  183. * b 起始值
  184. * c 总位移值
  185. * d 持续时间
  186. */
  187. function easeInOutExpon(t,b,c,d){
  188. if (t==0) return b;
  189. if (t==d) return b+c;
  190. if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
  191. return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
  192. }
  193.  
  194. //生成从minNum到maxNum的随机数
  195. function randomNum(minNum, maxNum) {
  196. switch (arguments.length) {
  197. case 1:
  198. return parseInt(Math.random() * minNum + 1, 10);
  199. break;
  200. case 2:
  201. return parseInt(Math.random() * ( maxNum - minNum + 1 ) + minNum, 10);
  202. break;
  203. default:
  204. return 0;
  205. break;
  206. }
  207. }
  208. </script>
  209. </html>

 总结

当一个新想法出现时,先去github和博客上找一找,看看有没有大佬做过,大佬们的的思路是什么,有什么自己没想到的细节,感觉差不多了在动手去做。

js canvas 粒子动画 电子表的更多相关文章

  1. 打造高大上的Canvas粒子(一)

    HTML5 Canvas <canvas>标签定义图形,比如图表和其他图像,必须用脚本(javascript)绘制图形. 举例:绘制矩形 <script> var c = do ...

  2. canvas学习之粒子动画

    项目地址:http://pan.baidu.com/s/1ccTptc 粒子动画意思就是把一个图片粒子画,然后使用粒子作出动画效果,主要两个问题:一个图片如何粒子化,这里面我们使用canvas的get ...

  3. 带着canvas去流浪系列之九 粒子动画【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  4. 带着canvas去流浪系列之九 粒子动画

    [摘要] canvas实现粒子动画 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 粒子特效 粒子特效一般指密集点阵效果,它并不是canvas独有 ...

  5. 【带着canvas去流浪(9)】粒子动画

    目录 一. 粒子特效 二. 开发中遇到的问题 2.1 卡顿 2.2 轨迹 2.3 复位 2.4 防护层 2.5 二维向量类 三. 实现讲解 3.1 粒子类的update方法 3.2 粒子群的绘制 3. ...

  6. CodePen 作品秀:Canvas 粒子效果文本动画

    作品名称——Shape Shifter,基于 Canvas 的粒子图形变换实验.在页面下方的输入框输入文本,上面就会进行变换出对应的粒子效果文本动画. CodePen 作品秀系列向大家展示来自 Cod ...

  7. css3动画和JS+DOM动画和JS+canvas动画比较

    css3兼容:IE10+.FF.oprea(animation):safari.chrome(-webkit-animation) js+dom:没有兼容问题: js+canvas:IE9+:(性能最 ...

  8. 基于canvas与原生JS的H5动画引擎

    前一段时间项目组里有一些H5动画的需求,由于没有专业的前端人员,便交由我这个做后台的研究相关的H5动画技术. 通过初步调研,H5动画的实现大概有以下几种方式: 1.基于css实现 这种方式比较简单易学 ...

  9. canvas粒子时钟

    前面的话 本文将使用canvas实现粒子时钟效果 效果展示 点阵数字 digit.js是一个三维数组,包含的是0到9以及冒号(digit[10])的二维点阵.每个数字的点阵表示是7*10大小的二维数组 ...

随机推荐

  1. [Fw]中断的初始化

    要使用中断肯定得初始化,这些初始化在系统启动时已经为你做好了,但是我们还是来看看怎样初始化的,这样就能更好的理解中断机制了.先看下面函数: 355 void __init init_ISA_irqs  ...

  2. 第八组Postmortem事后分析

    第八组Postmortem事后分析 一.团队成员总结的改进和教训 隆晋威:Beta阶段完善架构设计,分工更加明确,文档更丰富,交流带来开销减少.Alpha技术选型不固定,分工混乱,没有方便的测试引擎, ...

  3. 2019-8-31-C#-使用汇编

    title author date CreateTime categories C# 使用汇编 lindexi 2019-08-31 16:55:58 +0800 2019-2-16 8:56:5 + ...

  4. 解决vue代理和跨域问题

    一.安装vue-resource插件 安装命令:npm install vue-resource --save  安装完之后在根目录下的package.json检查一下插件的版本 在rourer-in ...

  5. element el-table 合计在横拉滚动条的下面,正确展示应该是滚动条在合计下面

    <style lang="less"> .el-table{ overflow: auto; } .el-table .el-table__body-wrapper, ...

  6. 人脸识别课件需要安装的python模块

    Python3.6安装face_recognition人脸识别库 https://www.jianshu.com/p/8296f2aac1aa

  7. \ HTML5开发项目实战:照片墙

    html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <ti ...

  8. 杂谈、 素材资源,没有美工不会ps一样可以美观

    免费素材网站 阿里巴巴矢量图,大部分图标都有颜色像素可选,格式可选3种, http://www.iconfont.cn/plus/home/index?spm=a313x.7781069.199891 ...

  9. docker 运行jenkins及vue项目与springboot项目(二.docker运行jenkins为自动打包运行做准备)

    docker 运行jenkins及vue项目与springboot项目: 一.安装docker 二.docker运行jenkins为自动打包运行做准备 三.jenkins的使用及自动打包vue项目 四 ...

  10. ofbiz保存jsp页面数据

    1.前台js保存 <script type="text/javascript" src="/ecloud/js/js/jquery.min.js"> ...