效果图

需求分析

  • 点随机产生并向随机方向以随机的速度匀速移动
  • 未点击时,点的总数保持不变;点击时在点击的位置产生数个新的点
  • 点与点之间在一定距离内有细线连接
  • 鼠标在画面中移动时,能够与其他点产生互动

1.点的实现

由于在整个demo中需要使用到大量的点,所以使用一个Dot类来负责产生点的实例以及这个点的所有行为

var Dots = function (speed, alpha) {// 画布相关this.canvas; // canvas节点this.ctx; //canvas绘图上下文// 绘制点相关this.x; // 横向坐标this.y; // 纵向坐标this.r; // dot半径this.a = alpha && alpha > 0 && alpha <= 1 ? alpha : .8;// 移动相关this.speed = speed && speed > 0 ? speed : 2;this.sx; // 单位时间水平移动距离this.sy; // 单位时间纵向移动距离this.isMouseDot = 0;
};

Dot的原型链中需要有一下两个方法:init() 与 update()

Dots.prototype = {init: function(){...},update: function(){...}
}

init()

Dots实例的初始化方法,在canvas中绘制一个点,并确定这个Dots实例移动的方向与速度(由sx与sy决定,即确定sx与sy的值)

// 初始化点的方法 x/y为可选参数 为生成点的位置 不传则随机生成init: function (canvas, x, y, isMouseDot) {this.canvas = canvas;this.ctx = this.canvas.getContext('2d');this.x = x * 2 || Math.random() * this.canvas.width;this.y = y * 2 || Math.random() * this.canvas.height;this.r = Math.random() * 6; // 随机生成 <6 的半径值if (isMouseDot) this.isMouseDot = 1;// 随机确定点的移动速度与方向 速度值在 [-this.speed, this.speed) 之间 提高数值可加快速度this.sx = isMouseDot ? 0 : Math.random() * this.speed * 2 - this.speed;this.sy = isMouseDot ? 0 : Math.random() * this.speed * 2 - this.speed;// 绘制点this.ctx.beginPath(); // 开启绘制路劲this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); // 绘制圆 参数依次为 圆的横坐标/纵坐标/半径/绘制圆的起始位置/绘制圆的弧度大小this.ctx.fillStyle = 'rgba(255,255,255,' + this.a + ')'; // 设置填充颜色this.ctx.fill(); // 填充颜色this.ctx.closePath(); // 关闭绘制路径}

update()

更新dot的位置,通过不断调用其的update方法,使其产生运动的效果,并且判断dot所处的位置是否已经超出canvas的边界,若超出则调用其init()方法,使其重生在canvas内

// 更新点位置update: function () {if (this.isMouseDot) return;this.x = this.x + this.sx;this.y = this.y + this.sy;// 点超出canvas范围时另其"重生"if (this.x < 0 || this.x > this.canvas.width) {this.init(this.canvas);}if (this.y < 0 || this.y > this.canvas.height) {this.init(this.canvas);}this.ctx.beginPath();this.ctx.arc(this.x, this.y, this.r + 0.5, 0, 2 * Math.PI);this.ctx.fillStyle = 'rgba(255,255,255,' + this.a + ')';this.ctx.fill();this.ctx.closePath();}

2.让点移动起来

如果让你实现一个动画,你可能会想到通过定时器setTimeout或者setInterval的方式来实现,时间设定越短动画也就越流畅,但是使用定时器会有这么几个问题出现:

当有耗时任务时,定时器任务会等待耗时任务结束,js引擎空闲时再去执行
当设定时间非常短时,可能会出现掉帧现象,产生动画不连贯的感受
那么有什么方法可以解决这个问题呢?答案是使用全局函数requestAnimFrame()

requestAnimFrame的字面意思是“请求动画帧”,作用是根据GPU的渲染频率来执行方法内的js代码,这样就不会出现上面使用定时器而导致的两个可能的问题

// dot移动效果
function animateUpdate() {dot.update(); // 更新dot的当前位置requestAnimFrame(animateUpdate);
}
requestAnimFrame(animateUpdate);

3.绘制点与点之间的连线

将产生的点存放在一个数组中,就得到了一个当前所有点实例的集合,通过for循环的嵌套,将数组中的每个点进行两两比较,当点与点之间的距离达到预先设置的临界值时,即可绘制连线

// 绘制连线
for (var i = overNum; i < dotsNum; i++) {for (var j = i + 1; j < dotsNum; j++) {var tx = dotsArr[i].x - dotsArr[j].x,ty = dotsArr[i].y - dotsArr[j].y,s = Math.sqrt(Math.pow(tx, 2) + Math.pow(ty, 2));if (s < dotsDistance) {//当点与点之间的距离达到预先设置的临界值时,即可绘制连线ctx.beginPath();//开启绘制路径ctx.moveTo(dotsArr[i].x, dotsArr[i].y);//设置线的起始位置ctx.lineTo(dotsArr[j].x, dotsArr[j].y);//设置线的结束位置//透明度的计算方式为,(临界值距离 - 实际距离) / 临界值距离ctx.strokeStyle = 'rgba(255,255,255,' + (dotsDistance - s) / dotsDistance + ')';//设置线条的绘制颜色ctx.strokeWidth = 1;//设置绘制线条的宽度ctx.stroke();//绘制ctx.closePath();//关闭绘制路径}}
}

4.实现鼠标交互

添加点击事件 click 事件监听器,当点击时实例化多个Dots对象,并将其添加到上文保存点的数组中,这样既可将新产生的点与原有的点产生联系。需要注意的是,产生点的位置应该为点击的位置,由于demo中的canvas是全屏显示的,所以只需要获取鼠标点的pageX / pageY,如果canvas并非全屏,则需要获取到的点产生的位置应该是相对于canvas的位置,而不能直接使用pageX / pageY, 并且需要判断是否在canvas内,如果不在则不产生新点

//鼠标事件
var clickWithNew = opts.clickWithDotsNumber || 5;
document.addEventListener('click', createDot);
function createDot(e) {var tx = e.pageX,ty = e.pageY;// 判断是不是在canvas内 width和height为canvas宽高if ((tx > 0 && tx < width) && (ty > 0 && ty < height)) {for (var i = 0; i < clickWithNew; i++) {var dot = new Dots(opts.speed, opts.dotsAlpha);dotsArr.push(dot);dotsNum += 1;dot.init(canvas, tx, ty); } // 循环创建5个点 并添加到数组中}
};
canvas.addEventListener('mouseenter', mouseDotEnter);
canvas.addEventListener('mouseleave', mouseDotLeave);
function mouseDotEnter(e) {var tx = e.pageX,ty = e.pageY;dot.init(canvas, tx, ty, 1);
};
function mouseDotLeave(e) {dot.init(canvas);
}

5.如何实现鼠标在canvas中移动的交互效果?

首先添加 mousemove 的事件监听器,其他步骤与上面点击的代码相同,唯一不同的是,确定点击在canvas内后不能创建新的点。现在为Dots对象添加一个新的原型链方法mouseDot()用于更新需要跟踪鼠标移动的点的位置

// 跟踪鼠标的点的位置更新 x/y为鼠标位置
mouseDot: function (x, y) {this.x = x * 2;this.y = y * 2; // 这里的2是屏幕的devicePixelRatio 是一个全局熟悉 在retain屏幕下 devicePixelRatio=2 标识浏览器会用两个像素点去绘制原先的一个像素 这样会导致绘图不清晰this.ctx.beginPath();this.ctx.arc(this.x, this.y, this.r + 0.5, 0, 2*Math.PI);this.ctx.fillStyle = "rgba(255,0,0,.8)";this.ctx.fill();this.ctx.closePath();}
canvas.addEventListener('mousemove', mouseDotMove);
function mouseDotMove(e) {var tx = e.pageX,ty = e.pageY;if ((tx > 0 && tx < width) && (ty > 0 && ty < height)) {dot.mouseDot(tx, ty); // 更新跟踪点的位置}
};

性能优化

以上就是canvas实现绚丽点线效果的基本思路啦!但是还有一个问题需要优化,刚开始不断点击的时候会不断产生点,但是当点的数量到达一定程度的时候就会发现:不管怎么点击,画面中的点的数量基本保持不变

其实在之前看到的网站上,点的数量是可以无上限增加的。但是点的数量不断增加会严重消耗性能,导致动画效果卡顿严重,无法直视,同时点太多也十分的不美观,于是demo就对这一情况做了优化:

当点的数量增加到预设的最大值时,每新增一个点,就会舍弃掉点数组中最先添加进去的点

var dotsNum = opts.dotsNumber || parseInt(area / 5000),maxDotsNum = dotsNum * 2,overNum = 0, // 超出最大数量的点的数量// 更新点的位置 数量超出最大值时舍弃旧的点
if (dotsNum > maxDotsNum) {overNum = dotsNum - maxDotsNum;
}
for (var i = overNum; i < dotsNum; i++) {if (dotsArr[i]) dotsArr[i].update();
}

最后的代码

html

<body><style>* {margin: 0;padding: 0;box-sizing: border-box;}#wonder {position: fixed;width: 100%;height: 100%;left: 0;top: 0;z-index: -1;background: url(darksky.jpg) no-repeat;background-size: cover;background-position: center center;}</style><div id="wonder"></div><script src="Wonder.js"></script><script>/*** el {String} 元素id或class* dotsNumber {Int} 初始化时页面上点的数量,如不传将根据绘制面积控制点的数量* lineMaxLength {Int} 两点之间最大的连接线长度,默认:250* dotsAlpha {Float} 点的透明度,取值范围 (0,1],默认:0.8* speed {Float} 点的移动速度,取值范围:大于0,默认:2* clickWithDotsNumber {Int} 每次点击产生的点的数量,默认:5*/new Wonder({el: '#wonder',dotsNumber: 100,lineMaxLength: 300,dotsAlpha: .5,speed: 1.5,clickWithDotsNumber: 5})</script><body>

js(部分省略)

var Dots = function (speed, alpha) {...
};
Dots.prototype = {init:function(){...};update:function(){...};mouseDot:function(){...];
};
function Wonder(opts) {var part = document.querySelector(opts.el),canvas = document.createElement('canvas'),ctx = canvas.getContext('2d'),partStyle = window.getComputedStyle(part, null),width = parseInt(partStyle.width),height = parseInt(partStyle.height),area = width * height, // canvas区域面积cssText = 'width: ' + width + 'px; height: ' + height + 'px;';canvas.setAttribute('style', cssText);canvas.width = (width * 2).toString();canvas.height = (height * 2).toString();part.appendChild(canvas);var dotsArr = [],dotsNum = opts.dotsNumber || parseInt(area / 5000),maxDotsNum = dotsNum * 2,overNum = 0, // 超出最大数量的点的数量dotsDistance = opts.lineMaxLength || 250; // 点之间产生连线的最大距离//生成点for (var i = 0; i < dotsNum; i++) {var dot = new Dots(opts.speed, opts.dotsAlpha);if (i < dotsNum - 1) {dot.init(canvas);} else {dot.init(canvas, 0, 0, 1);}dotsArr.push(dot);}//..鼠标事件//动画与连线var requestAnimFrame = requestAnimationFrame || webkitRequestAnimationFrame || oRequestAnimationFrame || msRequestAnimationFrame;requestAnimFrame(animateUpdate); // 兼容不同浏览器的requestAnimationFrame//dot移动效果function animateUpdate() {ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空canvas中原有的内容// 更新点的位置 数量超出最大值时舍弃旧的点if (dotsNum > maxDotsNum) {overNum = dotsNum - maxDotsNum;}for (var i = overNum; i < dotsNum; i++) {// if (dotsArr[i].isMouseDot) console.log('aaa')if (dotsArr[i]) dotsArr[i].update();}// ...绘制连线requestAnimFrame(animateUpdate);}
}

转自:https://zhuanlan.zhihu.com/p/24771668

canvas实现点线动画效果相关推荐

  1. 基于Canvas的画线动画效果

    想着用js实现一个画线动画,借助Canvas实现了.动画效果: 手机端访问 https://sagitarioo.github.io/Personal/htmlCode/linePaint/lineS ...

  2. Canvas科幻网状波浪动画效果

    下载地址Canvas科幻网状波浪动画效果是一款正弦波弧度波浪动画,像素块波浪特效. dd:

  3. CSS:文字下波浪线动画效果

    之前有至少5个人在评论中询问我文章中链接hover时候波浪下划线动画是怎么实现的,类似下图gif示意: 这里就介绍下是如何实现的. 有两种实现方法,各有优劣. 一.使用径向渐变纯CSS实现 就是使用径 ...

  4. HTML怎么在字体下方加波浪线,CSS实现文字下面波浪线动画效果

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8607 本文可全文转载,个人网站无需授权,但需要保留原作者.出处以及文中链接,任 ...

  5. 利用canvas绘制的拓扑动画效果

    展示效果网址 reloadimage(callback){let imgArrs = [];this.imgArray.forEach((img,index,arrs)=>{imgArrs[in ...

  6. html怎么在字体中加波浪线,强大的CSS:文字下波浪线动画效果

    之前有至少5个人在评论中询问我文章中链接hover时候波浪下划线动画是怎么实现的,类似下图gif示意: 这里就介绍下是如何实现的. 有两种实现方法,各有优劣. 一.使用径向渐变纯CSS实现 就是使用径 ...

  7. web前端入门到实战:CSS文字下波浪线动画效果

    之前有至少5个人在评论中询问我文章中链接hover时候波浪下划线动画是怎么实现的,类似下图gif示意: 这里就介绍下是如何实现的. 有两种实现方法,各有优劣. 一.使用径向渐变纯CSS实现 就是使用径 ...

  8. html5动态连线,canvas简单连线动画的实现代码

    前言:canvas动画入门系列之简单连线动画.虽然简单,但连线动画应用场景还挺多,因此做了个小demo,一通百通. step1:绘制点 首先创建个标签 设置几个点的坐标 const points = ...

  9. html制作的动态粒子图像,一种基于AnimateCC+Canvas的随机粒子动画效果设计

    潘博 摘要:粒子动画是一种应用广泛的动画效果,也有不同的制作方法.Animate CC是Flash的延续和发展,在动画制作方面功能强大,而Canvas是HTML5在网页中绘制图形.制作动画的核心元素. ...

最新文章

  1. [DM] 都是套路: 从上帝视角看透时间序列和数据挖掘
  2. HDU 4708 Rotation Lock Puzzle(模拟)
  3. 05传智_jbpm与OA项目_部门模块中增加部门的jsp页面增加一个在线编辑器功能
  4. 【转】自旋锁及其衍生锁
  5. matlab中quat2angle,matlab 学习记录
  6. mfc定义了变量仍提示未定义标识符_JavaScript-变量
  7. Zookeeper3.6.1常用的Shell命令
  8. cannot be cast to org.springframework.web.method.HandlerMethod 统一异常处理发生异常。
  9. c# 访问hbase_大数据技术之C#通过Thrift连接查询HBase主要方法总结
  10. java 表格树_00030-layui+java 树形表格treeTable
  11. BootStrap的介绍与案例使用
  12. python爬虫运行一遍后再运行不成功_python爬虫 - pyspdier run时运行到on_start就停止 但是单步调试可继续...
  13. Excel图表如何更改坐标轴最大值
  14. Lumerical官方案例、FDTD时域有限差分法仿真学习(一)——闪耀光栅(Blazed grating)
  15. 简约黑白双色求职简历PPT模板
  16. Android 应用FPS测试方法介绍
  17. 弗雷格的伟大历史贡献:量词的引入
  18. 怎么禁用计算机usb驱动,电脑设置禁用U盘的四大方法|怎么让电脑无法使用U盘
  19. apache mediawiki 安装_mediawiki安装使用
  20. *TEST 2 for NOIP 妈的智障

热门文章

  1. 视频教程-沐风老师3DMAX室内建模挤出法详解-3Dmax
  2. Android UI简单美化
  3. 21届秋招ATL宁德新能源一面面经[数据分析工程师]
  4. Openstack的安装部署教程
  5. criterial查询
  6. 计算机平均数函数试题,计算机期末Excel表格题及二级考试常用函数
  7. win7 安装openssh_Windows安装OpenSSH服务
  8. ApacheCN 计算机视觉译文集 20210218 更新
  9. macOS在IDEA上集成PLANTUML Graphviz绘制UML图并导出SVG
  10. Latex参考文献问题---参考文献条数不显示