js canvas 粒子动画 电子表
前言
从我接触canvas的第一天就觉得canvas很有趣,想搞点事情,这几天终于忍不住了,于是他来了。
先看效果
这里我做了四个大家有兴趣可以看完文章,做一个自己喜欢的动画。
思路
开始做之前,我们先分析一下这种粒子动画实现的原理,绘制的内容是由许多个带有颜色像素点构成,每个像素点在画布上都有自己的坐标。首先获取到要绘制的内容的像素点信息的数组(目标数组)例如
- [
- {x:10, y:20, color: 'rgba(255, 122, 122)'},
- {x:11, y:20, color: 'rgba(255, 122, 122)'},
- {x:12, y:20, color: 'rgba(255, 122, 122)'},
- ]
然后我们就可以让这些像素点从某些特定的位置,以某种特定的方式,移动到目标位置,动画就完成了。
实现
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个,这样我们就可以定义一个参数用来控制像素的稀释度
下面的事情就简单了,用代码实来现这一步
- /*
- * @ ImageDataFormat
- * @ param { pixels 需要格式化的ImageData对象, n 稀释度 }
- * @ return { Array }
- */
- function ImageDataFormat(pixels, n){
n = n*4- var arr = [], //目标数组
- temPixel = {}, //目标数组中存放像素信息的对象
- x = 0, //像素的x坐标
- y = 0 //像素的y坐标
- for (var i=0;i<pixels.data.length;i+=n){
- //过滤纯色背景提高性能,如背景色不可去掉可省略判断
- if(pixels.data[i] !== 0 || pixels.data[i+1] !== 0 || pixels.data[i+2] !== 0 ){
- var index = (i+1) / 4 //每四个划分为一组,重新定义索引
- if(index > timeDom.width){
- x = index % timeDom.width
- y = parseInt(index / timeDom.width)
- }else{
- x = index
- y = 0
- }
- temPixel = {
- R: pixels.data[i],
- G: pixels.data[i+1],
- B: pixels.data[i+2],
- A: pixels.data[i+3],
- I:i,
- X:x,
- Y:y
- }
- arr.push(temPixel)
- }
- }
return arr- }
2.将目标数组绘制到画布上
2.1在画布的指定位置画一个圆(一个像素点)
- /**
- * @ drawArc
- * @ param{ ctx 画布,,x x坐标,y y坐标,color 颜色}
- */
- function drawArc(ctx, x, y, color){
- x = x
- y = y
- ctx.beginPath();
- ctx.fillStyle = color
- ctx.strokeStyle = color
- ctx.arc(x,y,0.5,0,2*Math.PI);
- ctx.closePath()
- ctx.fill()
- }
2.1将点连成线,线构成面
- /**
- * 画路径
- * @param { points 格式化好的目标数组, crx 画布}
- */
- function draw_path(points, ctx){
- for(var i=0;i < points.length-1;i++){
- var color = 'rgba(' + points[i].R + ',' + points[i].G + ',' + points[i].B + ')', x, y
- drawArc(ctx,points[i].X,points[i].Y, color)
- }
- }
到此我们就画出了动画的其中一帧,下面我们就要让这一帧动起来
2.2动起来
我们的动画进行其实很简单
1.画第一帧
2.清空画布
3.画下一帧
4.在清空
....
但是想让这个动画流畅的进行起来我们还要在了解一下tween(缓动动画), window.requestAnimationFrame()
tween 我们值列举一种其他 形式感兴趣的可以自己查一下
- /*
- * @ 参数描述
- * @ t 动画执行到当前帧所经过的时间
- * @ b 起始值
- * @ c 总位移值
- * @ d 持续时间
- */
- function easeInOutExpon(t,b,c,d){
- if (t==0) return b;
- if (t==d) return b+c;
- if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
- return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
- }
window.requestAnimationFrame()
准备工作做好了可以开工了
我么只需要将前面的函数稍微改动一下他就动起来了
- function ShowTimeInit(pixels, n){
- n = 4*n
- var arr = [], temPixel = {}, x = 0, y = 0
- for (var i=0;i<pixels.data.length;i+=n){
- if(pixels.data[i] !== 0 || pixels.data[i+1] !== 0 || pixels.data[i+2] !== 0 ){
- var index = parseInt ((i+1) / 4)
- if(index > timeDom.width){
- x = index % timeDom.width
- y = parseInt(index / timeDom.width)
- }else{
- x = index
- y = 0
- }
- temPixel = {
- R: pixels.data[i],
- G: pixels.data[i+1],
- B: pixels.data[i+2],
- A: pixels.data[i+3],
- I:i,
- X:x,
- Y:y
- }
- arr.push(temPixel)
- }
- }
- var step = requestAnimationFrame(function(){draw_path(arr, ShowTime, step)})
- }
- /**
- * 画路径
- * @param path 路径
- */
- function draw_path(points, ctx, step){
- ShowTime.clearRect(0,0,ShowTimeDom.width,ShowTimeDom.height);
- var pointX, pointY, randomX, randomY
- for(var i=0;i < points.length-1;i++){
- switch (mode){
- case 'left':
- pointX = randomNum(0,0)
- pointY = randomNum(0,100)
- randomX = 0
- randomY = Math.random() + Math.random()*3000
- break;
- case 'center':
- pointX = 80
- pointY = 50
- randomX = Math.random() + Math.random()*3000
- randomY = Math.random() + Math.random()*3000
- break;
- case 'random':
- pointX = 0
- pointY = 0
- randomX = Math.random() + Math.random()*3000
- randomY = Math.random() + Math.random()*3000
- break;
- case 'flow':
- pointX = 0
- pointY = 0
- randomX = i
- randomY = i
- break;
- }
- var color = 'rgba(' + points[i].R + ',' + points[i].G + ',' + points[i].B + ')', x, y
- x = easeInOutExpon(nowDuration + randomX, pointX, points[i].X-pointX, duration)
- y = easeInOutExpon(nowDuration + randomY, pointY, points[i].Y-pointY, duration)
- drawArc(ctx,x, y, color)
- }
- nowDuration += 1000/60
- if(duration <= nowDuration){
- window.cancelAnimationFrame(step);
- }else{
- requestAnimationFrame(function(){draw_path(points, ctx, step)})
- }
- }
附上完整代码
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <style>
- </style>
- <title>电子时钟</title>
- </head>
- <body>
- <canvas id="HidenTime" width="300" height="100" style="display: none"> </canvas>
- <canvas id="ShowTime" width="300" height="100"> </canvas>
- </body>
- <script>
- function GetTime(){
- this._Hours = ''
- this._Minutes = ''
- this._Seconds = ''
- }
- GetTime.prototype = {
- constructor: GetTime,
- get Hours(){
- this._Hours = new Date().getHours()
- if(this._Hours > 9){
- return this._Hours
- }else{
- return "0" + this._Hours
- }
- },
- get Minutes(){
- this._Minutes = new Date().getMinutes()
- if(this._Minutes > 9){
- return this._Minutes
- }else{
- return "0" + this._Minutes
- }
- },
- get Seconds(){
- this._Seconds = new Date().getSeconds()
- if(this._Seconds > 9){
- return this._Seconds
- }else{
- return "0" + this._Seconds
- }
- },
- formTime:function(){
- return this.Hours + ':' + this.Minutes + ':' + this.Seconds
- }
- }
- var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
- window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
- var cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
- var duration = 3000, nowDuration = 0
- var timeDom = document.getElementById("HidenTime")
- var time = timeDom.getContext('2d')
- var ShowTimeDom = document.getElementById("ShowTime")
- var ShowTime = ShowTimeDom.getContext('2d')
- time.clearRect(0,0,timeDom.width,timeDom.height);
- var nowTime = new GetTime()
- var showTime = nowTime.formTime()
- var modes = ['left', 'random', 'center', 'flow']
- var mode = modes[0]
- time.font="50px Verdana";
- // 创建渐变
- var gradient=time.createLinearGradient(0,0,timeDom.width,0);
- gradient.addColorStop("0","magenta");
- gradient.addColorStop("0.5","blue");
- gradient.addColorStop("1.0","red");
- // 用渐变填色
- time.fillStyle=gradient;
- time.fillText(showTime,10,60);
- var pixels = time.getImageData(0,0,300,100)
- ShowTimeInit(pixels, 2)
- setInterval(function(){
- mode = modes[randomNum(0,3)]
- //mode = modes[3]
- time.clearRect(0,0,timeDom.width,timeDom.height);
- nowDuration = 0
- showTime = nowTime.formTime()
- time.fillText(showTime,10,60);
- pixels = time.getImageData(0,0,300,100)
- ShowTimeInit(pixels, 2)
- }, 5000)
- function ShowTimeInit(pixels, n){
- n = 4*n
- var arr = [], temPixel = {}, x = 0, y = 0
- for (var i=0;i<pixels.data.length;i+=n){
- if(pixels.data[i] !== 0 || pixels.data[i+1] !== 0 || pixels.data[i+2] !== 0 ){
- var index = parseInt ((i+1) / 4)
- if(index > timeDom.width){
- x = index % timeDom.width
- y = parseInt(index / timeDom.width)
- }else{
- x = index
- y = 0
- }
- temPixel = {
- R: pixels.data[i],
- G: pixels.data[i+1],
- B: pixels.data[i+2],
- A: pixels.data[i+3],
- I:i,
- X:x,
- Y:y
- }
- arr.push(temPixel)
- }
- }
- var step = requestAnimationFrame(function(){draw_path(arr, ShowTime, step)})
- }
- /**
- * 画路径
- * @param path 路径
- */
- function draw_path(points, ctx, step){
- ShowTime.clearRect(0,0,ShowTimeDom.width,ShowTimeDom.height);
- var pointX, pointY, randomX, randomY
- for(var i=0;i < points.length-1;i++){
- switch (mode){
- case 'left':
- pointX = randomNum(0,0)
- pointY = randomNum(0,100)
- randomX = 0
- randomY = Math.random() + Math.random()*3000
- break;
- case 'center':
- pointX = 80
- pointY = 50
- randomX = Math.random() + Math.random()*3000
- randomY = Math.random() + Math.random()*3000
- break;
- case 'random':
- pointX = 0
- pointY = 0
- randomX = Math.random() + Math.random()*3000
- randomY = Math.random() + Math.random()*3000
- break;
- case 'flow':
- pointX = 0
- pointY = 0
- randomX = i
- randomY = i
- break;
- }
- var color = 'rgba(' + points[i].R + ',' + points[i].G + ',' + points[i].B + ')', x, y
- x = easeInOutExpon(nowDuration + randomX, pointX, points[i].X-pointX, duration)
- y = easeInOutExpon(nowDuration + randomY, pointY, points[i].Y-pointY, duration)
- drawArc(ctx,x, y, color)
- }
- nowDuration += 1000/60
- if(duration <= nowDuration){
- window.cancelAnimationFrame(step);
- }else{
- requestAnimationFrame(function(){draw_path(points, ctx, step)})
- }
- }
- /**
- * 画圆
- */
- function drawArc(ctx, x, y, color){
- x = x
- y = y
- ctx.beginPath();
- ctx.fillStyle = color
- ctx.strokeStyle = color
- ctx.arc(x,y,0.5,0,2*Math.PI);
- ctx.closePath()
- ctx.fill()
- }
- /*
- * 参数描述
- * t 动画执行到当前帧所经过的时间
- * b 起始值
- * c 总位移值
- * d 持续时间
- */
- function easeInOutExpon(t,b,c,d){
- if (t==0) return b;
- if (t==d) return b+c;
- if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
- return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
- }
- //生成从minNum到maxNum的随机数
- function randomNum(minNum, maxNum) {
- switch (arguments.length) {
- case 1:
- return parseInt(Math.random() * minNum + 1, 10);
- break;
- case 2:
- return parseInt(Math.random() * ( maxNum - minNum + 1 ) + minNum, 10);
- break;
- default:
- return 0;
- break;
- }
- }
- </script>
- </html>
总结
当一个新想法出现时,先去github和博客上找一找,看看有没有大佬做过,大佬们的的思路是什么,有什么自己没想到的细节,感觉差不多了在动手去做。
js canvas 粒子动画 电子表的更多相关文章
- 打造高大上的Canvas粒子(一)
HTML5 Canvas <canvas>标签定义图形,比如图表和其他图像,必须用脚本(javascript)绘制图形. 举例:绘制矩形 <script> var c = do ...
- canvas学习之粒子动画
项目地址:http://pan.baidu.com/s/1ccTptc 粒子动画意思就是把一个图片粒子画,然后使用粒子作出动画效果,主要两个问题:一个图片如何粒子化,这里面我们使用canvas的get ...
- 带着canvas去流浪系列之九 粒子动画【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- 带着canvas去流浪系列之九 粒子动画
[摘要] canvas实现粒子动画 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 粒子特效 粒子特效一般指密集点阵效果,它并不是canvas独有 ...
- 【带着canvas去流浪(9)】粒子动画
目录 一. 粒子特效 二. 开发中遇到的问题 2.1 卡顿 2.2 轨迹 2.3 复位 2.4 防护层 2.5 二维向量类 三. 实现讲解 3.1 粒子类的update方法 3.2 粒子群的绘制 3. ...
- CodePen 作品秀:Canvas 粒子效果文本动画
作品名称——Shape Shifter,基于 Canvas 的粒子图形变换实验.在页面下方的输入框输入文本,上面就会进行变换出对应的粒子效果文本动画. CodePen 作品秀系列向大家展示来自 Cod ...
- css3动画和JS+DOM动画和JS+canvas动画比较
css3兼容:IE10+.FF.oprea(animation):safari.chrome(-webkit-animation) js+dom:没有兼容问题: js+canvas:IE9+:(性能最 ...
- 基于canvas与原生JS的H5动画引擎
前一段时间项目组里有一些H5动画的需求,由于没有专业的前端人员,便交由我这个做后台的研究相关的H5动画技术. 通过初步调研,H5动画的实现大概有以下几种方式: 1.基于css实现 这种方式比较简单易学 ...
- canvas粒子时钟
前面的话 本文将使用canvas实现粒子时钟效果 效果展示 点阵数字 digit.js是一个三维数组,包含的是0到9以及冒号(digit[10])的二维点阵.每个数字的点阵表示是7*10大小的二维数组 ...
随机推荐
- [Fw]中断的初始化
要使用中断肯定得初始化,这些初始化在系统启动时已经为你做好了,但是我们还是来看看怎样初始化的,这样就能更好的理解中断机制了.先看下面函数: 355 void __init init_ISA_irqs ...
- 第八组Postmortem事后分析
第八组Postmortem事后分析 一.团队成员总结的改进和教训 隆晋威:Beta阶段完善架构设计,分工更加明确,文档更丰富,交流带来开销减少.Alpha技术选型不固定,分工混乱,没有方便的测试引擎, ...
- 2019-8-31-C#-使用汇编
title author date CreateTime categories C# 使用汇编 lindexi 2019-08-31 16:55:58 +0800 2019-2-16 8:56:5 + ...
- 解决vue代理和跨域问题
一.安装vue-resource插件 安装命令:npm install vue-resource --save 安装完之后在根目录下的package.json检查一下插件的版本 在rourer-in ...
- element el-table 合计在横拉滚动条的下面,正确展示应该是滚动条在合计下面
<style lang="less"> .el-table{ overflow: auto; } .el-table .el-table__body-wrapper, ...
- 人脸识别课件需要安装的python模块
Python3.6安装face_recognition人脸识别库 https://www.jianshu.com/p/8296f2aac1aa
- \ HTML5开发项目实战:照片墙
html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <ti ...
- 杂谈、 素材资源,没有美工不会ps一样可以美观
免费素材网站 阿里巴巴矢量图,大部分图标都有颜色像素可选,格式可选3种, http://www.iconfont.cn/plus/home/index?spm=a313x.7781069.199891 ...
- docker 运行jenkins及vue项目与springboot项目(二.docker运行jenkins为自动打包运行做准备)
docker 运行jenkins及vue项目与springboot项目: 一.安装docker 二.docker运行jenkins为自动打包运行做准备 三.jenkins的使用及自动打包vue项目 四 ...
- ofbiz保存jsp页面数据
1.前台js保存 <script type="text/javascript" src="/ecloud/js/js/jquery.min.js"> ...