不知不觉又好久没更过博客了,老调新弹一下,之前做的一个小效果,觉得蛮有意思的,也有朋友问是怎么做的,就分享一下,写个博文吧。

  先上demo吧:http://whxaxes.github.io/canvas-test/src//Funny-demo/netparticle/net_1.html

  上面这个demo是最早写的,后来做了点小修改后就用到了自己的网站上当个banner,有兴趣的也可以看看效果:http://wanghx.cn/    ,至少同事说还是挺酷炫的。这种效果其实很早之前就有了的,我也是在一个网站上看到类似的效果,发现这个创意不错,而且难度很小,就花了一个午休的时间写了一下。

  接下来就分析一下怎么实现把。

  这种效果一眼看过去,就知道其实就是一堆粒子在进行无序的运动。然后当粒子与粒子之间的距离小于一定值后,就进行连线,并且根据距离的大小来对线条的粗细进行一些更改,就可以做出这种有点像蛛网的感觉了。原理很简单,直接上代码:

  1. var dots = [];
  2. for (var i = 0; i < 200; i++) {
  3. var x = Math.random() * (canvas.width + 2*extendDis) - extendDis;
  4. var y = Math.random() * (canvas.height + 2*extendDis) - extendDis;
  5. var xa = (Math.random() * 2 - 1)/1.5;
  6. var ya = (Math.random() * 2 - 1)/1.5;
  7.  
  8. dots.push({x, y, xa, ya})
  9. }

  首先,用一个数组,装载两百个分散在canvas各处的粒子对象,并且给每个对象一个随机的运动趋势。也就是xa和ya,用于表示垂直和水平的运动趋势。其实就是一个用于每次循环的时候进行叠加的值。

  实例化好两百个粒子对象后。就可以让他们开始运动:

  1. dot.x += dot.xa;
  2. dot.y += dot.ya;
  3.  
  4. // 遇到边界将速度反向
  5. dot.xa *= (dot.x > (canvas.width + extendDis) || dot.x < -extendDis) ? -1 : 1;
  6. dot.ya *= (dot.y > (canvas.height + extendDis) || dot.y < -extendDis) ? -1 : 1;
  7.  
  8. // 绘制点
  9. ctx.fillStyle = `rgba(${rgb},${rgb},${rgb},1`;
  10. ctx.fillRect(dot.x - 0.5, dot.y - 0.5, 1, 1);

  运动的逻辑也很简单,每次给粒子更新新的状态,其实就是根据此前初始化粒子的时候给予的xa和ya,进行一个累加,就可以形成运动的效果了。

  当然,粒子不能往一个方向无限的运动下去,所以我们还需要判断粒子是否运动到边界了,如果运动到了边界,就把运动趋势进行反转。也就做出了一种粒子反弹的效果。上面的extendDis其实是我为了让粒子反弹点在canvas外而定义的一个变量,用于控制粒子跑到离开canvas多远后才进行反弹。

  当然,每次运动完都对粒子进行一个绘制。这一段代码会放到一个叫move的function里。

  

  就上面的一些代码,就完成了粒子的初始化,以及运动了。接下来就是画线了。逻辑也很简单,就是遍历,逐个粒子计算距离,当两个比较的粒子之间的距离小于某个值,就进行画线。代码如下:

  1. /**
  2. * 逐个对比连线
  3. * @param ndots
  4. */
  5. function bubDrawLine(ndots){
  6. var ndot;
  7.  
  8. dots.forEach(function (dot) {
  9.  
  10. move(dot);
  11.  
  12. // 循环比对粒子间的距离
  13. for (var i = 0; i < ndots.length; i++) {
  14. ndot = ndots[i];
  15.  
  16. if (dot === ndot || ndot.x === null || ndot.y === null) continue;
  17.  
  18. var xc = dot.x - ndot.x;
  19. var yc = dot.y - ndot.y;
  20.  
  21. // 如果x轴距离或y轴距离大于max,则不计算粒子距离
  22. if(xc > ndot.max || yc > lineDis) continue;
  23.  
  24. // 两个粒子之间的距离
  25. var dis = xc * xc + yc * yc;
  26.  
  27. // 如果粒子距离超过max,则不做处理
  28. if( dis > lineDis ) continue;
  29.  
  30. // 距离比
  31. var ratio;
  32.  
  33. // 如果是鼠标,则让粒子向鼠标的位置移动
  34. if (ndot === warea && dis < 20000) {
  35. dot.x -= xc * 0.01;
  36. dot.y -= yc * 0.01;
  37. }
  38.  
  39. // 计算距离比
  40. ratio = (lineDis - dis) / lineDis;
  41.  
  42. // 粒子间连线
  43. ctx.beginPath();
  44. ctx.lineWidth = ratio / 2;
  45. ctx.strokeStyle = `rgba(${rgb},${rgb},${rgb},${ratio + 0.2}`;
  46. ctx.moveTo(dot.x, dot.y);
  47. ctx.lineTo(ndot.x, ndot.y);
  48. ctx.stroke();
  49. }
  50.  
  51. // 将已经计算过的粒子从数组中删除
  52. ndots.splice(ndots.indexOf(dot), 1);
  53. });
  54. }

  逻辑也比较简单,就是遍历数组,把遍历到的粒子跟其他粒子进行逐个比对。当距离小于上面的lineDis的时候,就进行连线。为了减少计算量,每次计算过的粒子将会从用于计算的ndots数组中删除,避免重复计算。同时如果两个粒子的垂直距离和水平距离大于lineDis,那也就没必要再算两个粒子的距离了,直接不做处理,从而减少计算量。

  其实这个计算用的还是所谓的笨方法,我此前有在想有什么更好的计算方法能更好的优化计算效率呢。然后想了一个方法并且进行了一个测试,就是先对粒子根据x轴进行快速排序,然后按顺序进行比较,当比较到的粒子的水平距离大于lineDis的时候,就不用再比下去了。因为后面的都肯定会比当前粒子要更远,想着就按照这样会减少计算量应该会提升效率。但是我对两个不同的计算方法都进行了耗时比较,结果还是原来的笨方法的性能更优。因为这个新方法每次都要重新排序,这个计算量也是蛮大的。然后就暂时没想到其他了,如果读者有更好的idea不妨分享一下。

  

  同事有问我那个鼠标划过,粒子会聚起来的效果很神奇,怎么做的,其实这个效果比想象中简单很多,而且在上面的代码里我也给出来了。再给出一段保存鼠标位置的代码,很简单,就是鼠标移动的时候保存鼠标位置。

  1. // 鼠标活动时,获取鼠标坐标
  2. var warea = {x: null, y: null};
  3. var animateHeader = document.getElementById("animateHeader");
  4.  
  5. animateHeader.onmousemove = function (e) {
  6. e = e || window.event;
  7.  
  8. warea.x = e.clientX + 10;
  9. warea.y = e.clientY;
  10. };

  保存了鼠标位置后,在每次动画循环的时候,把鼠标位置也当成一个粒子对象塞进数组进行比较:

  1. // 每一帧循环的逻辑
  2. function animate() {
  3. ctx.clearRect(0, 0, canvas.width, canvas.height);
  4.  
  5. bubDrawLine([warea].concat(dots));
  6.  
  7. RAF(animate);
  8. }

  而粒子往鼠标方向运动的代码,其实就这么一小截:

  1. // 如果是鼠标,则让粒子向鼠标的位置移动
  2. if (ndot === warea && dis < 20000) {
  3. dot.x -= xc * 0.01;
  4. dot.y -= yc * 0.01;
  5. }

  计算鼠标与粒子的距离,当鼠标与粒子之间的距离小于一定的时候,把粒子的位置更新为 “当前位置 - 鼠标粒子距离 * 0.01”即可。然后就会形成粒子往鼠标位置移动的效果了。

  整个效果就这样完成了,很简单,也很有意思,有兴趣的可以去研究一下发掘一些更好玩的效果。

  贴上这个demo的github地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src//Funny-demo/netparticle

  这个demo是很早之前写的,跟上面贴出来的代码会有点出入,但是原理是一样的。懂了原理,就可以自己去实现一个了。

  

  如果觉得demo不错,就在github给个star呗,当然也欢迎fork

用 canvas 做个好玩的网站背景的更多相关文章

  1. 如何把canvas元素作为网站背景总结详解

    如何把canvas元素作为网站背景总结详解 一.总结 一句话总结:最简单的做法是绝对定位并且z-index属性设置为负数. 1.如何把canvas元素作为网站背景的两种方法? a.设置层级(本例代码就 ...

  2. Node JS爬虫: 阮老师网站背景图

    小白,轻喷,本文教你三分钟入门爬虫 前言 有一天发现,阮一峰老师的博客图片很好看,想收藏一下,因为几百张,一张张下载太慢,朋友推荐了个谷歌插件(Fatkun)确实挺好用的,这之后就对爬虫有了兴趣,这个 ...

  3. 我们能用canvas做什么?

    什么是Canvas? Canvas元素是HTML5的一部分,允许脚本语言动态渲染位图像.Canvas由一个可绘制地区HTML代码中的属性定义决定高度和宽度.JavaScript代码可以访问该地区,通过 ...

  4. 用Canvas做动画

    之前看过不少HTML5动画的书,讲解的是如何去做,对于其中的数学原理讲解的不详细,常有困惑.最近看的<HTML5+JavaScript 动画基础>这个是译本,Keith Peters曾写过 ...

  5. html5 canvas做的图表插件

    用highchart的时候发现它是用svg来画图的,那么用canvas来做怎么样的. 以前做AS图表插件的时候,绘制图画主要用容器的Graphics对象来绘制,而canvas的context和Grap ...

  6. canvas做动画

    一.绘制图片 ①加载图片 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  7. 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎

    Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...

  8. 关于iphone 6 ios8网站背景图片错乱的问题解决办法

    最近公司有个客户的网站用手机safari打开出现背景图片错乱,本来应该显示A图片的却显示B图片,网速越慢的情况下越容易出现这种问题,悲催的是这种情况只在iPhone 6上出现,并且不是一直这样,多刷新 ...

  9. 一起用HTML5 canvas做一个简单又骚气的粒子引擎

    前言 好吧,说是"粒子引擎"还是大言不惭而标题党了,离真正的粒子引擎还有点远.废话少说,先看demo 本文将教会你做一个简单的canvas粒子制造器(下称引擎). 世界观 这个简单 ...

随机推荐

  1. MongoDB Sharding、库、collection设计学习汇总

    sharding设计须考虑的几个因素 Sharding Key的选择           在片键的选择上,最好是能够在字段中选择混合型的片键,大范围的递增健.和随机分布的健组合,如按月份递增.按用户名 ...

  2. Java api 入门教程 之 JAVA的String 类

    1.String对象的初始化 由于String对象特别常用,所以在对String对象进行初始化时,Java提供了一种简化的特殊语法,格式如下: String s = “abc”; s = “Java语 ...

  3. 如何配置使用 Log4j

    最近在用Java 写一个FTP上传下载文件工具,正好需要产生操作日志. 利用Log4j.jar可以轻松解决.下面介绍一下Log4j的使用,具体概念到官网查阅: -------------------- ...

  4. iNeedle系统使用注意事项

    1.不能监控https协议的报文 目前只能监控http协议的网站,对于https协议(加密)的网站暂时不支持监控,甚至连流量都没有作统计,在测试iNeedle的时候也要多加注意.

  5. poj 2104 K-th Number(可持久线段树)

    K-th Number 持久化:http://www.cnblogs.com/tedzhao/archive/2008/11/12/1332112.html 结构:http://www.docin.c ...

  6. 关于TCP连接建立与终止那点事

    0. 前言 最近在处理公司遗留项目的时候发现自己对TCP协议一点都不懂,所以补了点关于TCP连接的建立和终止的内容,这里简单写下自己了解的部分,省略了报文序号确认序号这些无关的字段,主要讨论TCP状态 ...

  7. 【2016-10-13】【坚持学习】【Day4】【模板方法模式】

    今天学习模板方法模式 例子代码; 模板抽象类,定义了一套流程,有一些方法已经实现,有一些抽象方法需要继承它的子类实现 abstract class DataViewer { //抽象方法:获取数据 p ...

  8. C++11 之 scoped enum

      C++11 枚举类型是“域化的” (scoped enum),相比 C++98 枚举类型的“非域化” (unscoped enum),具有如下优点: 1  命名空间污染  一般来说,声明在花括号内 ...

  9. ATM模拟程序

    一个很简单的ATM模拟程序 #include <stdio.h> void chaxun(int a3){ int b; b=a3; printf("您的余额为:%d\n&quo ...

  10. AC日记——计算2的N次方 openjudge 1.6 12

    12:计算2的N次方 总时间限制:  1000ms 内存限制:  65536kB 描述 任意给定一个正整数N(N<=100),计算2的n次方的值. 输入 输入一个正整数N. 输出 输出2的N次方 ...