编织璀璨星空图,html5时钟教程

用 Canvas 编织光彩夺目星空图

2016/05/14 · CSS · Canvas, 画布

初稿出处: Cyandev   

先来探视最后的法力:

图片 1

GitHub项目: CyandevToys / ParticleWeb
是还是不是还蛮酷的呢?本文大家就来一点一点解析怎么贯彻它!


原著出处: WAxes   

用HTML5塑造数字石英钟的教程,html5机械钟教程

那篇小说首要介绍了用HTML5创设数字石英钟的学科,首要行使HTML5中的Canvas API,须求的爱人能够参谋下

图片 2

便是那个数字石英钟,此时认为那么些创新意识不错,可是也没去折腾。直到今日同事又在网络来看那几个案例,他认为相当的帅炫,就跑过来问作者,这一个是怎么贯彻的,然后作者大概想了须臾间贯彻方式后也来了点兴趣,就花了一点光阴模仿做出来了三个。分裂的是,岑安用的是div来做的。而自身正是用canvas来贯彻的。用canvas来做品质方面会更加好,因为就唯有操控每一种点的移位,用js调节dom的style属性跟用js调节canvas绘图相比较性能方面鲜明是全部欠缺的。

  先上个自己做的DEMO吧,然后再简述一下做那些的办法:   看DEMO请戳小编 。

  做那个思路超粗略,就是通过字符串保存各类数字的地点: 
复制代码

XML/HTML Code复制内容到剪贴板

  1. var numData = [   
  2.             "1111/1001/1001/1001/1001/1001/1111", //0   
  3.             "0001/0001/0001/0001/0001/0001/0001", //1   
  4.             "1111/0001/0001/1111/1000/1000/1111", //2   
  5.             "1111/0001/0001/1111/0001/0001/1111", //3   
  6.             "1010/1010/1010/1111/0010/0010/0010", //4   
  7.             "1111/1000/1000/1111/0001/0001/1111", //5   
  8.             "1111/1000/1000/1111/1001/1001/1111", //6   
  9.             "1111/0001/0001/0001/0001/0001/0001", //7   
  10.             "1111/1001/1001/1111/1001/1001/1111", //8   
  11.             "1111/1001/1001/1111/0001/0001/1111", //9   
  12.             "0000/0000/0010/0000/0010/0000/0000", //:   
  13.         ]  

  0代表没像素,1意味有像素,/是为着更加雅观些,正是分行嘛,简单谈起来:举个例子0正是:

  

XML/HTML Code复制内容到剪贴板

  1.         1  1  1  1   
  2.   
  3.   1  0  0  1   
  4.   
  5.   1  0  0  1   
  6.   
  7.   1  0  0  1   
  8.   
  9.   1  0  0  1   
  10.   
  11.   1  0  0  1   
  12.   
  13.   1  1  1  1     

如此那般就很明白了啊。从0到9还会有一个:号都用字符串表示好。

  然后就写个粒子对象,也正是像素点:

XML/HTML Code复制内容到剪贴板

  1. var P_radius = 8,Gravity = 9.8;   
  2.         var Particle = function(){   
  3.             this.x = 0;   
  4.             this.y = 0;   
  5.             this.vx = 0;   
  6.             this.vy = 0;   
  7.             this.color = "";   
  8.             this.visible = false;   
  9.             this.drop = false;   
  10.         }   
  11.         Particle.prototype = {   
  12.             constructors:Particle,   
  13.             paint:function(){        //绘制自个儿   
  14.                 ctx.fillStyle = this.color;   
  15.                 ctx.beginPath();   
  16.                 ctx.arc(this.x,this.y,P_radius,0,2*Math.PI);   
  17.                 ctx.fill();   
  18.             },   
  19.             reset:function(x,y,color){        //重置   
  20.                 this.x = x;   
  21.                 this.y = y;   
  22.                 this.vx = 0;   
  23.                 this.vy = 0;   
  24.                 this.color = color;   
  25.                 this.visible = true;   
  26.                 this.drop = false;   
  27.             },   
  28.             isDrop:function(){        //落下   
  29.                 this.drop = true;   
  30.                 var vx = Math.random()*20 15   
  31.                 this.vx = Math.random()>=0.5?-vx : vx;   
  32.             },   
  33.             update:function(time){        //每一帧的动作   
  34.                 if(this.drop){   
  35.                     this.x  = this.vx*time;   
  36.                     this.y  = this.vy*time;   
  37.   
  38.                     var vy = this.vy Gravity*time;   
  39.   
  40.                     if(this.y>=canvas.height-P_radius){   
  41.                         this.y = canvas.height-P_radius   
  42.                         vy = -vy*0.7;   
  43.                     }   
  44.   
  45.                     this.vy = vy;   
  46.   
  47.                     if(this.x<-P_radius||this.x>canvas.width P_radius||this.y<-P_radius||this.y>canvas.height P_radius){   
  48.                         this.visible = false;   
  49.                     }   
  50.                 }   
  51.             }   
  52.         }     
  53.   

粒子对象的性质比较轻松,就地方,速度,以至是不是可视化。方法的话,paint是绘制方法,reset是重新恢复设置(因为粒子要循环利用的,升高品质),isDrop是粒子落下方法,update正是每后生可畏帧更新粒子的动作,update中当粒子运动过量canvas的绘图区域时,就把它的可视化置为false,在粒子容器中保存起来等待下三遍调用。

  写好粒子对象后,就要怀恋如何让粒子依照岗位画上去,同有的时候候当粒子无需用时能够让她做自由落体的卡通了。

  先画背景(也正是那还未有像素的白点):

XML/HTML Code复制内容到剪贴板

  1. function drawBg(){   
  2.             var tx = (canvas.width-((P_radius*2 X_J)*4*8 7*xjg))/2;   
  3.             for(var i=0;i<8;i ){   
  4.                 var ty = (canvas.height-((P_radius yjg)*6))/2;   
  5.                 for(var j=0;j<numData[0].length;j ){   
  6.                     var tt = numData[0].charAt(j);   
  7.                     if(tt==="/"){   
  8.                         ty =yjg;   
  9.                     }else {   
  10.                         var x = tx j%5*(P_radius*2 X_J),   
  11.                             y = ty;   
  12.                         bgctx.fillStyle = "#FFF";   
  13.                         bgctx.beginPath();   
  14.                         bgctx.arc(x,y,P_radius,0,2*Math.PI);   
  15.                         bgctx.fill();   
  16.                     }   
  17.                 }   
  18.                 tx =xjg 4*(P_radius*2 X_J);   
  19.             }   
  20.         }   

  先把背景画到二个离屏canvas中缓存起来,接下去每生龙活虎帧重画的时候就不要求逻辑总括了,直接把非常离屏canvas画上去就行了。下面的逻辑应该轻松了解,就是经过多个巡回,循环8个数字,然后再对各种数字一个点一个点展开绘图,当碰到“/”时,就认证要换行了,把绘制的ty加个换行间距,再把tx重新载入参数,再开展绘图。就像此,点就能够都画出来了。效果图如下:
图片 3

背景画好了,就从头依据每黄金时代秒的岁月,画数字像素吧。方法重假若其后生可畏:

XML/HTML Code复制内容到剪贴板

  1. function setTime(time){   
  2.             var h = time.getHours() "",   
  3.                 m = time.getMinutes() "",   
  4.                 s = time.getSeconds() "";   
  5.             hh = h.length===1?"0" h:h;   
  6.             mm = m.length===1?"0" m:m;   
  7.             ss = s.length===1?"0" s:s;   
  8.   
  9.             var nowdate = h ":" m ":" s;   
  10.             var tx = (canvas.width-((P_radius*2 X_J)*4*8 7*xjg))/2,color = "";   
  11.             for(var i=0;i<nowdate.length;i ){   
  12.                 var n = nowdate.charAt(i)===":"?10:parseInt(nowdate.charAt(i)),   
  13.                     text = numData[n];   
  14.   
  15.                 var ty = (canvas.height-((P_radius yjg)*6))/2;   
  16.   
  17.                 switch(i){   
  18.                     case 0:color = "#4DCB74";break;   
  19.                     case 2:color = "#4062E0";break;   
  20.                     case 3:color = "#D65050";break;   
  21.                     case 5:color = "#4062E0";break;   
  22.                     case 6:color = "#797C17";break;   
  23.                 }   
  24.   
  25.                 for(var j=0;j<text.length;j ){   
  26.                     var tt = text.charAt(j);   
  27.                     if(tt==="/"){   
  28.                         ty =yjg;   
  29.                     }else{   
  30.                         var x = tx j%5*(P_radius*2 X_J),   
  31.                             y = ty,   
  32.                             pp = null,   
  33.                             usefullp = null;   
  34.                         particles.forEach(function(p){   
  35.                             if(p.visible&p.x===x&p.y===y){   
  36.                                 ppp = p;   
  37.                             }else if(!p.visible&usefullp===null){   
  38.                                 usefullp = p;   
  39.                             }   
  40.                         });   
  41.                         if(pp!==null&tt==="0"){   
  42.                             pp.isDrop();   
  43.                         }else if(pp===null&tt==="1"){   
  44.                             usefullp.reset(x , y , color);   
  45.                         }   
  46.                     }   
  47.                 }   
  48.                 tx =xjg 4*(P_radius*2 X_J);   
  49.             }   
  50.         }  

  原理也简单,也是跟上边画背景大概,遍历全部一些,然后依照当下日子的数字转变到的字符串来判别,当前点是或不是合宜有像素,若是有像素就再判定当前以此点是还是不是早就有粒子对象在了,就算已经有粒子对象在了,就径直跳出不管理,若无粒子对象在,就再粒子容器中找三个不曾被选用的粒子reset到那几个职责。还会有生机勃勃种情状,正是现阶段那个点是不应有有像素的,可是却有粒子,那就得到这么些粒子,让那么些粒子举行自由落体。

  时间设置也写好了,就能够写舞台更新的代码了:

XML/HTML Code复制内容到剪贴板

  1. var timeCount_0 = 0,timeCount_1 = 0,particles = [];   
  2.         function initAnimate(){   
  3.             for(var i=0;i<200;i ){   
  4.                 var p = new Particle();   
  5.                 particles.push(p);   
  6.             }   
  7.   
  8.             timeCount_0 = new Date();   
  9.             timeCount_1 = new Date();   
  10.             drawBg();   
  11.             setTime(timeCount_0)   
  12.             animate();   
  13.         }   
  14.   
  15.         function animate(){   
  16.             ctx.clearRect(0,0,canvas.width,canvas.height);   
  17.             ctx.drawImage(bgcanvas,0,0);   
  18.   
  19.             var timeCount_2 = new Date();   
  20.   
  21.             if(timeCount_1-timeCount_0>=1000){   
  22.                 setTime(timeCount_1);   
  23.                 timeCount_0 = timeCount_1;   
  24.             }   
  25.   
  26.             particles.forEach(function(p){   
  27.                 if(p.visible){   
  28.                     p.update((timeCount_2-timeCount_1)/70);   
  29.                     p.paint();   
  30.                 }   
  31.             });   
  32.   
  33.             timeCount_1 = timeCount_2;   
  34.   
  35.             RAF(animate)   
  36.         }  

  在initAnimate进行动画开头化,伊始化也正是先实例化五百个粒子对象放置粒子容器中保存起来,再改正时间戳,缓存背景,设置当前光阴,然后调用animate动画循环主体从前动画。

  animate中的逻辑也非常轻巧了,获取时间戳,假设三个时间戳之间的岁月差大于或等于1秒,就进展setTime。而再下边包车型大巴便是对粒子容器里的具有可视化的粒子实行遍历循环重绘了。
接下来就搞好啦:
图片 4

个效率依然有为数不菲能够优化的地点的,因为机械钟和分钟都以动的相当少的,所以能够把那七个缓存起来,当未有动作的时候就径直将缓存数据画上去就行了,这样就能够削减舞台每少年老成帧的绘图API调用量,明显是能增高品质的。但是以后毕竟粒子非常少,两八百个粒子对象也就足足了,假若不去做优化,动画也还是能够很通畅的运维的。所以楼主就偷个小懒啦。

  源码地址:

那篇小说首要介绍了用HTML5创设数字机械钟的科目,主要行使HTML5中的Canvas API,必要的意中人能够参谋下...

分析

第生机勃勃大家看看那些职能具体有那七个要点。首先,这么炫人眼目的效果与利益自然是要用到 Canvas 了,每个星星能够看作为贰个粒子,由此,整个功用其实正是粒子系统了。别的,大家得以窥见每一种粒子之间是互相连接的,只可是离的近的粒子之间的连线相当的粗且折射率十分的低,而离的远的则相反。

明日想弄二个网页,把本身学HTML5进度中做的片段DEMO放上去做集结,可是,尽管就单单做个网页把装有DEMO一个三个排列又感觉太无耻了。就想,既然学了canvas,那就来折腾下浏览器,做个细微开场动画吧。

开始 Coding

开场动画的效率,想了一会,决定用粒子,因为认为粒子相比有趣。还记得以前小编写的第生龙活虎篇手艺博文,便是讲文字图片粒子化的:文字图片粒子化 , 那个时候就只有做的是直线运动,顺便加了好几3D成效。运动公式很简短。所以就想以此开场动画就做的更充沛一些吧。

HTML 部分

那部分自家就轻便放了三个 `` 标签,设置样式使其填写全屏。

<canvas height="620" width="1360" id="canvas" style="position: absolute; height: 100%;"/>

1
<canvas height="620" width="1360" id="canvas" style="position: absolute; height: 100%;"/>

下一场为了让抱有因素没有间距和内补,小编还加了一条全局样式:

* { margin: 0; padding: 0; }

1
2
3
4
    * {
      margin: 0;
      padding: 0;
    }

先上DEMO:

JavaScript 部分

下边大家来写宗旨的代码。首先大家要猎取非常 canvas 并获得绘制上下文:

var canvasEl = document.getElementById('canvas'); var ctx = canvasEl.getContext('2d'); var mousePos = [0, 0];

1
2
3
var canvasEl = document.getElementById('canvas');
var ctx = canvasEl.getContext('2d');
var mousePos = [0, 0];

随着大家证明七个变量,分别用于存款和储蓄“星星”和边:

var nodes = []; var edges = [];

1
2
var nodes = [];
var edges = [];

下一步,我们做些策动干活,正是让画布在窗口大小发生变化时再也绘制,并且调动本身分辨率:

window.onresize = function () { canvasEl.width = document.body.clientWidth; canvasEl.height = canvasEl.clientHeight; if (nodes.length == 0) { constructNodes(); } render(); }; window.onresize(); // trigger the event manually.

1
2
3
4
5
6
7
8
9
10
11
12
window.onresize = function () {
    canvasEl.width = document.body.clientWidth;
    canvasEl.height = canvasEl.clientHeight;
 
    if (nodes.length == 0) {
      constructNodes();
    }
 
    render();
};
 
window.onresize(); // trigger the event manually.

大家在率先次修改尺寸后营造了具有节点,这里就要用到下三个函数(constructNodes)了

其豆蔻梢头函数中大家随意创造多少个点,大家用字典对象的点子存储这几个点的相继音讯:

function constructNodes() { for (var i = 0; i < 100; i ) { var node = { drivenByMouse: i == 0, x: Math.random() * canvasEl.width, y: Math.random() * canvasEl.height, vx: Math.random() * 1 - 0.5, vy: Math.random() * 1 - 0.5, radius: Math.random() > 0.9 ? 3 Math.random() * 3 : 1 Math.random() * 3 }; nodes.push(node); } nodes.forEach(function (e) { nodes.forEach(function (e2) { if (e == e2) { return; } var edge = { from: e, to: e2 } addEdge(edge); }); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function constructNodes() {
    for (var i = 0; i < 100; i ) {
      var node = {
        drivenByMouse: i == 0,
        x: Math.random() * canvasEl.width,
        y: Math.random() * canvasEl.height,
        vx: Math.random() * 1 - 0.5,
        vy: Math.random() * 1 - 0.5,
        radius: Math.random() > 0.9 ? 3 Math.random() * 3 : 1 Math.random() * 3
      };
 
      nodes.push(node);
    }
 
    nodes.forEach(function (e) {
      nodes.forEach(function (e2) {
        if (e == e2) {
          return;
        }
 
        var edge = {
          from: e,
          to: e2
        }
 
        addEdge(edge);
      });
    });
  }

为了促成前面一个更绚烂的机能,小编给第三个点加了一个 drivenByMouse 属性,那几个点的职位不会被粒子系统管理,也不会绘制出来,不过它会与别的点连线,那样就贯彻了鼠标跟随的成效了。

此地稍微解释一下 radius 属性的取值,笔者期望让绝超越二分一点都以小半径的,而极个其余点半径比非常的大,所以小编这里用了少数小 tricky,正是用概率调节点的半径取值,不断调节这些可能率阈值就会取得期望的半径随机遍及。

点都营造完结了,将要创设点与点之间的连线了,大家用到再度遍历,把三个点捆绑成生机勃勃组,放到 edges 数组中。注意这里自个儿用了其余一个函数来成功那件事,而并未有间接用 edges.push() ,为什么?

如果大家早前线总指挥部是了 A、B两点,也正是外围循环是A,内侧循环是B,那么在下叁次巡回中,外侧为B,内侧为A,是或不是也会成立一条边呢?而事实上,那七个边除了方向不平等以外是一心相近的,这一丝一毫未有要求而且占用财富。因而大家在 addEdge 函数中进行贰个剖断:

function addEdge(edge) { var ignore = false; edges.forEach(function (e) { if (e.from == edge.from & e.to == edge.to) { ignore = true; } if (e.to == edge.from & e.from == edge.to) { ignore = true; } }); if (!ignore) { edges.push(edge); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function addEdge(edge) {
    var ignore = false;
 
    edges.forEach(function (e) {
      if (e.from == edge.from & e.to == edge.to) {
        ignore = true;
      }
 
      if (e.to == edge.from & e.from == edge.to) {
        ignore = true;
      }
    });
 
    if (!ignore) {
      edges.push(edge);
    }
  }

于今,大家的希图干活就得了了,下边我们要让点动起来:

function step() { nodes.forEach(function (e) { if (e.drivenByMouse) { return; } e.x = e.vx; e.y = e.vy; function clamp(min, max, value) { if (value > max) { return max; } else if (value < min) { return min; } else { return value; } } if (e.x <= 0 || e.x >= canvasEl.width) { e.vx *= -1; e.x = clamp(0, canvasEl.width, e.x) } if (e.y <= 0 || e.y >= canvasEl.height) { e.vy *= -1; e.y = clamp(0, canvasEl.height, e.y) } }); adjustNodeDrivenByMouse(); render(); window.requestAnimationFrame(step); } function adjustNodeDrivenByMouse() { nodes[0].x = (mousePos[0] - nodes[0].x) / easingFactor; nodes[0].y = (mousePos[1] - nodes[0].y) / easingFactor; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function step() {
    nodes.forEach(function (e) {
      if (e.drivenByMouse) {
        return;
      }
 
      e.x = e.vx;
      e.y = e.vy;
 
      function clamp(min, max, value) {
        if (value > max) {
          return max;
        } else if (value < min) {
          return min;
        } else {
          return value;
        }
      }
 
      if (e.x <= 0 || e.x >= canvasEl.width) {
        e.vx *= -1;
        e.x = clamp(0, canvasEl.width, e.x)
      }
 
      if (e.y <= 0 || e.y >= canvasEl.height) {
        e.vy *= -1;
        e.y = clamp(0, canvasEl.height, e.y)
      }
    });
 
    adjustNodeDrivenByMouse();
    render();
    window.requestAnimationFrame(step);
  }
 
  function adjustNodeDrivenByMouse() {
    nodes[0].x = (mousePos[0] - nodes[0].x) / easingFactor;
    nodes[0].y = (mousePos[1] - nodes[0].y) / easingFactor;
  }

来看那样一大段代码不要惊悸,其实做的工作相当的轻松。这是粒子系统的中坚,正是遍历粒子,况兼更新其情景。更新的公式正是

v = v a s = s v

1
2
v = v a
s = s v

a是加快度,v是速度,s是位移。由于大家那边不关乎加速度,所以就不写了。然后大家须求作二个边缘的碰撞检查评定,否则大家的“星星”都无拘无缚地一丢丢飞~走~了~。边缘碰撞后的管理方式便是让速度矢量反转,那样粒子就能够“掉头”回来。

还记得大家须要做的鼠标跟随吗?也在这里处理,大家让第一个点之处一点一点运动到鼠标的地点,下面这些公式很有意思,能够轻便达成缓动:

x = x (t - x) / factor

1
x = x (t - x) / factor

内部 factor 是缓动机原因子,t 是终极地方,x 是现阶段地点。至于那几个公式的分解还也可以有个互相大神 Bret Victor在她的演讲中涉嫌过,摄像做的老大好,有条(ti)件(zi)我们自然要拜谒: Bret Victor – Stop Drawing Dead Fish

好了,回到主旨。我们在上头的函数中拍卖完了风流浪漫帧中的数据,大家要让整个粒子系统一而再地启动起来就必要三个timer了,可是那些不提倡咱们使用 setInterval,而是尽可能选择 requestAnimationFrame,它能担保你的帧率锁定在

剩余的正是绘制啦:

function render() { ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, canvasEl.width, canvasEl.height); edges.forEach(function (e) { var l = lengthOfEdge(e); var threshold = canvasEl.width / 8; if (l > threshold) { return; } ctx.strokeStyle = edgeColor; ctx.lineWidth = (1.0

  • l / threshold) * 2.5; ctx.globalAlpha = 1.0 - l / threshold; ctx.beginPath(); ctx.moveTo(e.from.x, e.from.y); ctx.lineTo(e.to.x, e.to.y); ctx.stroke(); }); ctx.globalAlpha = 1.0; nodes.forEach(function (e) { if (e.drivenByMouse) { return; } ctx.fillStyle = nodeColor; ctx.beginPath(); ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI); ctx.fill(); }); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function render() {
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);
 
    edges.forEach(function (e) {
      var l = lengthOfEdge(e);
      var threshold = canvasEl.width / 8;
 
      if (l > threshold) {
        return;
      }
 
      ctx.strokeStyle = edgeColor;
      ctx.lineWidth = (1.0 - l / threshold) * 2.5;
      ctx.globalAlpha = 1.0 - l / threshold;
      ctx.beginPath();
      ctx.moveTo(e.from.x, e.from.y);
      ctx.lineTo(e.to.x, e.to.y);
      ctx.stroke();
    });
    ctx.globalAlpha = 1.0;
 
    nodes.forEach(function (e) {
      if (e.drivenByMouse) {
        return;
      }
 
      ctx.fillStyle = nodeColor;
      ctx.beginPath();
      ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI);
      ctx.fill();
    });
  }

常规的 Canvas 绘图操作,注意 beginPath 必定要调用,不然你的线就全体穿在联合了… 必要验证的是,在绘制边的时候,我们先要总计两点离开,然后依照四个阈值来决断是不是要绘制那条边,这样大家才具兑现间隔远的点时期连线不可以预知的效用。

到此地,我们的不论什么事职能就做到了。若是不清楚我们也得以去本身文章初步放的 repo 离去看完整的源码。Have fun!!

2 赞 4 收藏 评论

图片 5

作用是或不是比直线的移动愈来愈饱满呢?何况也实在非常的粗略,别忘了那篇博文的难点,小小滴公式,大大滴乐趣。要做出这样的职能,用的就可是是大家初级中学。。可能高中时候的大要知识,加速移动,减速运动的公式啦。所以实乃小小滴公式。楼主很赏识折腾一些光彩夺目的东西,纵然恐怕常常干活上用不上,可是,那野趣确实很让人着迷啊。况且,做下这个也得以拉长一下编制程序的思维本领哈。

废话十分的少说,步入正题啦。就总结的解释一下原理吧~~~

粒子运动的着力代码就像此一点:

JavaScript

update:function(time){ this.x = this.vx*time; this.y = this.vy*time; if(!this.globleDown&&this.y>0){ var yc = this.toy - this.y; var xc = this.tox - this.x; this.jl = Math.sqrt(xc*xc yc*yc); var za = 20; var ax = za*(xc/this.jl), ay = za*(yc/this.jl), vx = (this.vx ax*time)*0.97, vy = (this.vy ay*time)*0.97; this.vx = vx; this.vy = vy; }else { var gravity = 9.8; var vy = this.vy gravity*time; if(this.y>canvas.height){ vy = -vy*0.7; } this.vy = vy; } },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
update:function(time){
            this.x = this.vx*time;
            this.y = this.vy*time;
 
            if(!this.globleDown&&this.y>0){
                var yc = this.toy - this.y;
                var xc = this.tox - this.x;
 
                this.jl = Math.sqrt(xc*xc yc*yc);
 
                var za = 20;
 
                var ax = za*(xc/this.jl),
                    ay = za*(yc/this.jl),
                    vx = (this.vx ax*time)*0.97,
                    vy = (this.vy ay*time)*0.97;
 
                this.vx = vx;
                this.vy = vy;
 
            }else {
                var gravity = 9.8;
                var vy = this.vy gravity*time;
 
                if(this.y>canvas.height){
                    vy = -vy*0.7;
                }
 
                this.vy = vy;
            }
        },

粒子总共有三种情形,风流浪漫种是自由落体,意气风发种就是境遇吸力。自由落体就掩盖了。说吸力以前先贴出粒子的质量:

var Dot = function(x,y,vx,vy,tox,toy,color){ this.x=x; this.y=y; this.vx=vx; this.vy=vy; this.nextox = tox; this.nextoy = toy; this.color = color; this.visible = true; this.globleDown = false; this.setEnd(tox , toy); } setEnd:function(tox , toy){     this.tox = tox;     this.toy = toy;     var yc = this.toy - this.y;     var xc = this.tox - this.x; },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var Dot = function(x,y,vx,vy,tox,toy,color){
        this.x=x;
        this.y=y;
        this.vx=vx;
        this.vy=vy;
        this.nextox = tox;
        this.nextoy = toy;
        this.color = color;
        this.visible = true;
        this.globleDown = false;
        this.setEnd(tox , toy);
    }
 
setEnd:function(tox , toy){
    this.tox = tox;
    this.toy = toy;
    var yc = this.toy - this.y;
    var xc = this.tox - this.x;
},

x,y便是粒子的岗位,vx是粒子水平速度,vy是粒子的垂直速度,nexttox之类知否道都不在乎,只是不常保留变量的。tox,和toy正是粒子的指标地地点。

首先,先予以全部粒子一个目标地,这些目标地下边再会说。也正是要粒子达到的地点,然后再定义多个变量za作为加快度,具体数值的话,就协和多测量检验下就能有大约参数的了,笔者设成20,认为就大多了。za是粒子和目标地之间连线的加快度,所以,我们因而粒子的职责和指标地的职分,通过轻便的三角形函数,就可以把粒子的等级次序加快度和垂直加速度求出来了,就这段

var ax = za*(xc/this.jl), ay = za*(yc/this.jl),

1
2
var ax = za*(xc/this.jl),
  ay = za*(yc/this.jl),

有了层次加快度和垂直加快度后,接下去就更简短了,直接计算水平速度和垂直速度的增量,进而改换程度速度和垂直速度的值

vx = (this.vx ax*time)*0.97, vy = (this.vy ay*time)*0.97;

1
2
vx = (this.vx ax*time)*0.97,
vy = (this.vy ay*time)*0.97;

所以要乘于0.97是为着模仿能量消耗,粒子才会放缓。time是每生机勃勃帧的时日差

测算出速度后就改过粒子地点就行了。

this.x = this.vx*time; this.y = this.vy*time;

1
2
this.x = this.vx*time;
this.y = this.vy*time;

因为粒子在宇宙航行进程中,与指标地之间的连线方向是不停矫正的,所以每风流倜傥帧都要再一次总结粒子的程度增加速度度和垂直加速度。

一抬手一动脚规律正是这么,是还是不是相当粗略吗。

活动规律讲完了,再扯一下方面十分动画的求实实现吗:动画初叶化,在一个离屏canvas上把想要的字恐怕图片画出来,然后再经过getImageData那么些办法获得离屏canvas的像素。然后用贰个生生不息,把离屏canvas中有绘制的区域寻觅来,因为imageData里的data值正是一个rgba数组,所以大家看清最终贰个的值也正是光滑度大于128便是有绘制过的区域。然后拿走该区域的xy值,为了防止粒子对象过多导致页面卡顿,所以大家就限定一下粒子的多少,取像素的时候x值和y值每一次依次增加2,从而减少粒子数量。

this.osCanvas = document.createElement("canvas"); var osCtx = this.osCanvas.getContext("2d"); this.osCanvas.width = 1000; this.osCanvas.height = 150; osCtx.text阿里gn = "center"; osCtx.textBaseline = "middle"; osCtx.font="70px 微软雅黑,金鼎文 bold"; osCtx.fillStyle = "#1D181F" osCtx.fillText("WelCome" , this.osCanvas.width/2 , this.osCanvas.height/2-40); osCtx.fillText("To wAxes' HOME" , this.osCanvas.width/2 , this.osCanvas.height/2 40); var bigImageData = osCtx.getImageData(0,0,this.osCanvas.width,this.osCanvas.height); dots = []; for(var x=0;x<bigImageData.width;x =2){ for(var y=0;y<bigImageData.height;y =2){ var i = (y*bigImageData.width x)*4; if(bigImageData.data[i 3]>128){ var dot = new Dot( Math.random()>0.5?Math.random()*20 10:Math.random()*20 canvas.width-40, -Math.random()*canvas.height*2, 0, 0, x (canvas.width/2-this.osCanvas.width/2), y (canvas.height/2-this.osCanvas.height/2), "rgba(" bigImageData.data[i] "," bigImageData.data[i 1] "," bigImageData.data[i 2] ",1)" ); dot.setEnd(canvas.width/2,canvas.height/2) dots.push(dot); } } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
this.osCanvas = document.createElement("canvas");
        var osCtx = this.osCanvas.getContext("2d");
 
        this.osCanvas.width = 1000;
        this.osCanvas.height = 150;
 
        osCtx.textAlign = "center";
        osCtx.textBaseline = "middle";
        osCtx.font="70px 微软雅黑,黑体 bold";
        osCtx.fillStyle = "#1D181F"
        osCtx.fillText("WelCome" , this.osCanvas.width/2 , this.osCanvas.height/2-40);
        osCtx.fillText("To wAxes' HOME" , this.osCanvas.width/2 , this.osCanvas.height/2 40);
        var bigImageData = osCtx.getImageData(0,0,this.osCanvas.width,this.osCanvas.height);
 
        dots = [];
 
        for(var x=0;x<bigImageData.width;x =2){
            for(var y=0;y<bigImageData.height;y =2){
                var i = (y*bigImageData.width x)*4;
                if(bigImageData.data[i 3]>128){
                    var dot = new Dot(
                        Math.random()>0.5?Math.random()*20 10:Math.random()*20 canvas.width-40,
                        -Math.random()*canvas.height*2,
                        0,
                        0,
                        x (canvas.width/2-this.osCanvas.width/2),
                        y (canvas.height/2-this.osCanvas.height/2),
                        "rgba(" bigImageData.data[i] "," bigImageData.data[i 1] "," bigImageData.data[i 2] ",1)"
                    );
                    dot.setEnd(canvas.width/2,canvas.height/2)
                    dots.push(dot);
                }
            }
        }

经过巡回获取到粒子之处xy值后,把地方赋给粒子,成为粒子的目标地。然后动画伊始,就足以做出文字图片粒子化的职能了。

下边贴出动画达成的js代码。假若对任何代码也风乐趣的,能够平素看调节台哈,没压缩的。

var part_1 = (function(w){ var dots = [],DOT_SIZE = 2,cube=null; var Dot = function(x,y,vx,vy,tox,toy,color){ this.x=x; this.y=y; this.vx=vx; this.vy=vy; this.nextox = tox; this.nextoy = toy; this.color = color; this.visible = true; this.globleDown = false; this.setEnd(tox , toy); } Dot.prototype = { paint:function(){ ctx.fillStyle=this.color; ctx.fillRect(this.x-DOT_SIZE/2 , this.y-DOT_SIZE/2 , DOT_SIZE , DOT_SIZE); }, setEnd:function(tox , toy){ this.tox = tox; this.toy = toy; var yc = this.toy - this.y; var xc = this.tox - this.x; // this.initjl = Math.sqrt(xc*xc yc*yc); }, update:function(time){ this.x = this.vx*time; this.y = this.vy*time; if(!this.globleDown&&this.y>0){ var yc = this.toy - this.y; var xc = this.tox - this.x; this.jl = Math.sqrt(xc*xc yc*yc); var za = 20; var ax = za*(xc/this.jl), ay = za*(yc/this.jl), vx = (this.vx ax*time)*0.97, vy = (this.vy ay*time)*0.97; this.vx = vx; this.vy = vy; // if(Math.abs(this.vx)<1&&Math.abs(this.vy)<1){ // this.y = this.toy // this.x = this.tox // } }else { var gravity = 9.8; var vy = this.vy gravity*time; if(this.y>canvas.height){ vy = -vy*0.7; } this.vy = vy; } }, loop:function(time){ this.update(time); this.paint(); } } var animate = function(){ this.state = "before" } var ap = animate.prototype; ap.init = function(){ this.osCanvas = document.createElement("canvas"); var osCtx = this.osCanvas.getContext("2d"); this.osCanvas.width = 1000; this.osCanvas.height = 150; osCtx.textAlign = "center"; osCtx.textBaseline = "middle"; osCtx.font="70px 微软雅黑,隶书 bold"; osCtx.fillStyle = "#1D181F" osCtx.fillText("WelCome" , this.osCanvas.width/2 , this.osCanvas.height/2-40); osCtx.fillText("To wAxes' HOME" , this.osCanvas.width/2 , this.osCanvas.height/2 40); var bigImageData = osCtx.getImageData(0,0,this.osCanvas.width,this.osCanvas.height); dots = []; for(var x=0;x<bigImageData.width;x =2){ for(var y=0;y<bigImageData.height;y =2){ var i = (y*bigImageData.width x)*4; if(bigImageData.data[i 3]>128){ var dot = new Dot( Math.random()>0.5?Math.random()*20 10:Math.random()*20 canvas.width-40, -Math.random()*canvas.height*2, 0, 0, x (canvas.width/2-this.osCanvas.width/2), y (canvas.height/2-this.osCanvas.height/2), "rgba(" bigImageData.data[i] "," bigImageData.data[i 1] "," bigImageData.data[i 2] ",1)" ); dot.setEnd(canvas.width/2,canvas.height/2) dots.push(dot); } } } console.log(dots.length) } ap.changeState = function(){ var osCtx = this.osCanvas.getContext("2d"); osCtx.clearRect(0,0,this.osCanvas.width,this.osCanvas.height); this.osCanvas.width = 460; this.osCanvas.height = 100; osCtx.fillStyle="#5C5656" osCtx.fillRect(20,20,60,60) drawLogo(this.osCanvas , osCtx); var bigImageData = osCtx.getImageData(0,0,this.osCanvas.width,this.osCanvas.height); var index=0; dots.sort(function(a , b){ return Math.random()-Math.random(); }) for(var x=0;x<bigImageData.width;x =2){ for(var y=0;y<bigImageData.height;y =2){ var i = (y*bigImageData.width x)*4; if(bigImageData.data[i 3]>128){ var d = dots[index]; if(d){ d.setEnd(x (canvas.width/2-300) , y 50) d.color = "rgba(" bigImageData.data[i] "," bigImageData.data[i 1] "," bigImageData.data[i 2] ",1)"; index } } } } setTimeout(function(){ var endindex = index; for(var i=0;i<dots.length-endindex;i ){ if(dots[index]){ var d = dots[index]; d.globleDown = true; d.vx = Math.random()*100-50; } index ; } } , 2000) } function endState(){ canvas.width = 600; canvas.height = 100; canvas.style.display="block"; canvas.style.top = "50px"; canvas.style.left = (window.innerWidth-canvas.width)/2 "px"; cube = new Cube(50); cube._initVector(50,50); } function draw图标(canvas , ctx){ ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.font="65px 微软雅黑,楷体 bold" ctx.fillStyle="#E06D2F" ctx.fillText("DEMO" , 300 , canvas.height/2) ctx.font="40px 微软雅黑,燕书 bold" ctx.fillStyle="#405159" ctx.fillText("吖猩的" , 160 , canvas.height/2) ctx.fillText("小窝" , 420 , canvas.height/2) } var num = 0; ap.update = function(time){ time = time/100; if(this.state==="first"||this.state==="before"){ var completeNum = 0; dots.forEach(function(dot){ if(dot.visible) dot.loop(time); if(dot.jl<5){ completeNum } }); if(completeNum>=5*dots.length/6){ if(this.state==="before"){ this.state = "first"; dots.forEach(function(dot){ dot.setEnd(dot.nextox , dot.nextoy); }); }else { this.state = "second"; this.changeState(); } } }else if(this.state==="second"){ var completeNum = 0, allnum = 0; dots.forEach(function(dot){ if(dot.visible) dot.loop(time); if(dot.globleDown){ allnum ; if(Math.abs(dot.y-canvas.height)<2){ completeNum } } }); if(completeNum===allnum&&allnum!==0){ this.state = "third"; part_2.animate(); endState(); } }else if(this.state==="third"){ cube.update(); drawLogo(canvas , ctx); } } return new animate(); })(window)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
var part_1 = (function(w){
    var dots = [],DOT_SIZE = 2,cube=null;
 
    var Dot = function(x,y,vx,vy,tox,toy,color){
        this.x=x;
        this.y=y;
        this.vx=vx;
        this.vy=vy;
        this.nextox = tox;
        this.nextoy = toy;
        this.color = color;
        this.visible = true;
        this.globleDown = false;
        this.setEnd(tox , toy);
    }
 
    Dot.prototype = {
        paint:function(){
            ctx.fillStyle=this.color;
            ctx.fillRect(this.x-DOT_SIZE/2 , this.y-DOT_SIZE/2 , DOT_SIZE , DOT_SIZE);
        },
 
        setEnd:function(tox , toy){
            this.tox = tox;
            this.toy = toy;
            var yc = this.toy - this.y;
            var xc = this.tox - this.x;
            // this.initjl = Math.sqrt(xc*xc yc*yc);
        },
 
        update:function(time){
            this.x = this.vx*time;
            this.y = this.vy*time;
 
            if(!this.globleDown&&this.y>0){
                var yc = this.toy - this.y;
                var xc = this.tox - this.x;
 
                this.jl = Math.sqrt(xc*xc yc*yc);
 
                var za = 20;
 
                var ax = za*(xc/this.jl),
                    ay = za*(yc/this.jl),
                    vx = (this.vx ax*time)*0.97,
                    vy = (this.vy ay*time)*0.97;
 
                this.vx = vx;
                this.vy = vy;
 
                // if(Math.abs(this.vx)<1&&Math.abs(this.vy)<1){
                //     this.y = this.toy
                //     this.x = this.tox
                // }
            }else {
                var gravity = 9.8;
                var vy = this.vy gravity*time;
 
                if(this.y>canvas.height){
                    vy = -vy*0.7;
                }
 
                this.vy = vy;
            }
        },
 
        loop:function(time){
            this.update(time);
            this.paint();
        }
    }
 
    var animate = function(){
        this.state = "before"
    }
 
    var ap = animate.prototype;
 
    ap.init = function(){
        this.osCanvas = document.createElement("canvas");
        var osCtx = this.osCanvas.getContext("2d");
 
        this.osCanvas.width = 1000;
        this.osCanvas.height = 150;
 
        osCtx.textAlign = "center";
        osCtx.textBaseline = "middle";
        osCtx.font="70px 微软雅黑,黑体 bold";
        osCtx.fillStyle = "#1D181F"
        osCtx.fillText("WelCome" , this.osCanvas.width/2 , this.osCanvas.height/2-40);
        osCtx.fillText("To wAxes' HOME" , this.osCanvas.width/2 , this.osCanvas.height/2 40);
        var bigImageData = osCtx.getImageData(0,0,this.osCanvas.width,this.osCanvas.height);
 
        dots = [];
 
        for(var x=0;x<bigImageData.width;x =2){
            for(var y=0;y<bigImageData.height;y =2){
                var i = (y*bigImageData.width x)*4;
                if(bigImageData.data[i 3]>128){
                    var dot = new Dot(
                        Math.random()>0.5?Math.random()*20 10:Math.random()*20 canvas.width-40,
                        -Math.random()*canvas.height*2,
                        0,
                        0,
                        x (canvas.width/2-this.osCanvas.width/2),
                        y (canvas.height/2-this.osCanvas.height/2),
                        "rgba(" bigImageData.data[i] "," bigImageData.data[i 1] "," bigImageData.data[i 2] ",1)"
                    );
                    dot.setEnd(canvas.width/2,canvas.height/2)
                    dots.push(dot);
                }
            }
        }
        console.log(dots.length)
    }
 
    ap.changeState = function(){
        var osCtx = this.osCanvas.getContext("2d");
        osCtx.clearRect(0,0,this.osCanvas.width,this.osCanvas.height);
        this.osCanvas.width = 460;
        this.osCanvas.height = 100;
 
        osCtx.fillStyle="#5C5656"
        osCtx.fillRect(20,20,60,60)
 
        drawLogo(this.osCanvas , osCtx);
 
        var bigImageData = osCtx.getImageData(0,0,this.osCanvas.width,this.osCanvas.height);
 
        var index=0;
        dots.sort(function(a , b){
            return Math.random()-Math.random();
        })
        for(var x=0;x<bigImageData.width;x =2){
            for(var y=0;y<bigImageData.height;y =2){
                var i = (y*bigImageData.width x)*4;
                if(bigImageData.data[i 3]>128){
                        var d = dots[index];
                        if(d){
                            d.setEnd(x (canvas.width/2-300) , y 50)
                            d.color = "rgba(" bigImageData.data[i] "," bigImageData.data[i 1] "," bigImageData.data[i 2] ",1)";
                            index
                        }
                }
            }
        }
 
        setTimeout(function(){
            var endindex = index;
            for(var i=0;i<dots.length-endindex;i ){
                if(dots[index]){
                    var d = dots[index];
 
                    d.globleDown = true;
                    d.vx = Math.random()*100-50;
                }
                index ;
            }
        } , 2000)
    }
 
    function endState(){
        canvas.width = 600;
        canvas.height = 100;
        canvas.style.display="block";
        canvas.style.top = "50px";
        canvas.style.left = (window.innerWidth-canvas.width)/2 "px";
        cube = new Cube(50);
        cube._initVector(50,50);
    }
 
    function drawLogo(canvas , ctx){
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.font="65px 微软雅黑,黑体 bold"
        ctx.fillStyle="#E06D2F"
        ctx.fillText("DEMO" , 300 , canvas.height/2)
 
        ctx.font="40px 微软雅黑,黑体 bold"
        ctx.fillStyle="#405159"
        ctx.fillText("吖猩的" , 160 , canvas.height/2)
        ctx.fillText("小窝" , 420 , canvas.height/2)
    }
 
    var num = 0;
    ap.update = function(time){
        time = time/100;
        if(this.state==="first"||this.state==="before"){
            var completeNum = 0;
            dots.forEach(function(dot){
                if(dot.visible) dot.loop(time);
                if(dot.jl<5){
                    completeNum
                }
            });
            if(completeNum>=5*dots.length/6){
 
                if(this.state==="before"){
                    this.state = "first";
                    dots.forEach(function(dot){
                        dot.setEnd(dot.nextox , dot.nextoy);
                    });
                }else {
                    this.state = "second";
                    this.changeState();
                }
            }
        }else if(this.state==="second"){
            var completeNum = 0,
                allnum = 0;
            dots.forEach(function(dot){
                if(dot.visible) dot.loop(time);
                if(dot.globleDown){
                    allnum ;
                    if(Math.abs(dot.y-canvas.height)<2){
                        completeNum
                    }
                }
            });
 
            if(completeNum===allnum&&allnum!==0){
                this.state = "third";
                part_2.animate();
                endState();
            }
        }else if(this.state==="third"){
            cube.update();
            drawLogo(canvas , ctx);
        }
    }
 
    return new animate();
})(window)

赞 收藏 1 评论

本文由星彩网app下载发布于前端技术,转载请注明出处:编织璀璨星空图,html5时钟教程

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。