html5 canvas 实现光线沿不规则路径运动


Posted in HTML / CSS onApril 20, 2020

svg让动画沿着不规则路径运动

查阅svg文档后发现,svg动画运动有两种实现方式,且都非常简单,但对于100%实现设计师给出的效果有很大的距离

使用offset-path偏移路径和offset-roate偏移角度让元素沿着不规则路径走

<!DOCTYPE html>
	<html>
	<head>
		<title>offset-path/offset-roate</title>
	</head>
	<style type="text/css">
		* {
			padding: 0;
			margin: 0;
			box-sizing: border-box;
		}
		body {
			background: #000;
		}
		.line {
			width: 80px;
			height: 3px;
	        position: absolute;
	        background: red;
			offset-path: path("M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500");
	        animation: move 10s linear infinite;
		}
		@keyframes move {
			100% {
				offset-distance: 2000px;
			}
		}
		.line1 {
			position: absolute;
			left: 100px;
			width: 20px;
			height: 20px;
			border-radius: 50%;
			background: red;
			offset-path: path("M0,0a72.5,72.5 0 1,0 145,0a72.5,72.5 0 1,0 -145,0");
	   		offset-rotate: 0deg;
	   		animation: load 1.8s cubic-bezier(0.86, 0, 0.07, 1) infinite;
	   		animation-delay: 0.147s;
	    	animation-fill-mode: forwards;
		}
		@keyframes load {
			from {
		        offset-distance: 0;
		    }
		    to {
		        offset-distance: 100%;
		    }
		}
	</style>
	<body>
		<h2>路径偏移</h2>
		<div class="line"></div>
		<svg width="100%" height="600px" version="1.0" id="svg1">
			<path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF"></path>
		</svg>
		<h2>角度偏移</h2>
		<div class="line1">
		</div>
	</body>
	</html>

此种方式的限制是滚动元素无法随路径进行没有规律的变化

使用stroke-dasharray和stroke-dashoffset让路径边线动起来

stroke-dasharray:设置shap和text 边框虚线的实线长度与实线之间的间隔(虚线长度)
stroke-dashoffser:设置边框线条相对于默认位置的偏移(正值:向左,负值:向右)

<!DOCTYPE html>
	<html>
	<head>
		<title>stroke-dasharray/stroke-dashoffser</title>
	</head>
	<style type="text/css">
		* {
			padding: 0;
			margin: 0;
			box-sizing: border-box;
		}
		body {
			background: #000;
			color: #fff;
		}
		.move {
			animation: moving 5s infinite;
		}
		@keyframes moving {
			0% {
				stroke-dashoffset: 80px;
			}
			100% {
				stroke-dashoffset: -1600px;
			}
		}
	</style>
	<body>
	<h2>设置stroke-dasharray</h2>
	<b>storke-dasharray设置为80 ,此时实线和实线间隔一样</b>
	<svg width="100%" height="600px" version="1.0" id="svg1">
		<path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF" stroke-dasharray="80"></path>
	</svg>
	<b>storke-dasharray设置为80 320,此时实线和是实线间隔的1/4</b>
	<svg width="100%" height="600px" version="1.0" id="svg1">
		<path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF" stroke-dasharray="80 320"></path>
	</svg>
	<h2>设置stroke-dashoffset让边线相对于初始位置发生偏移</h2>
	<svg width="100%" height="600px" version="1.0" id="svg1">
		<path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF" stroke-dasharray="80 320" stroke-dashoffset="40"></path>
	</svg>
	<h2>通过设置stroke-dasharray 和 stroke-dashoffset让边线动起来</h2>
	<svg width="100%" height="600px" version="1.0" id="svg1">
		<path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="#FFF"></path>
		<path d="M10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500" fill="#tranparent" stroke="red" stroke-dasharray="80 1600" stroke-dashoffset="0" class="move"></path>
	</svg>
	</body>
	</html>

此种方式通过边框偏移的效果可以设置跟随路径的滚线条,但是无法设置线条的光线效果,即实线的阴影和实线的渐变效果(渐变区域需随着偏移路径的变化而变化)

canvas实现线条延不规则路径运动

线条实现

对于不规则路径,如果直接用画线条的方式实现光线,需要计算每一个开始点和结束点的位置,中间还可能存在转折点,计算起来非常麻烦,不可取
故这边采取canvas组合图形的模式,取线条和一个图形重叠部分(类似于灯罩)来实现光线效果
 

组合前
 

html5 canvas 实现光线沿不规则路径运动
 

组合后
 

html5 canvas 实现光线沿不规则路径运动

<!DOCTYPE html>
	<html>
	<head>
		<title>canvas实现不规则路径光效</title>
	</head>
	<style type="text/css">
		body {
			background: #000;
		}
		#wrap {
			position: absolute;
			width: 1200px;
			height: 600px
		}
	</style>
	<body>
		<div id="wrap">
			<canvas id="canvas" width="1200" height="600"></canvas>
		</div>
	</body>
	<script type="text/javascript">
		var path = 'M 10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500';
		var list = path.match(/([A-Z]([^A-Z]){1,})/g).map(item => {
			return {
				x: item.split(' ')[1],
				y: item.split(' ')[2],
				action: item.split(' ')[0],
			}
		});//获取每个点位置
		var canvas = document.getElementById('canvas');
		var ctx = canvas.getContext('2d');
		ctx.strokeStyle = 'rgba(255,255,255,1)';
		function drawPath() {
			ctx.lineWidth = 3;
			ctx.beginPath();
			list.forEach(item => {
				if(item.action == 'M') ctx.moveTo(item.x, item.y);
				if(item.action == 'L') ctx.lineTo(item.x, item.y);
			});
			ctx.stroke();
		}
		drawPath();

		function drawLine() {
			//设置图形组合方式 默认source-over
			ctx.globalCompositeOperation = "destination-in";
			ctx.lineWidth = 60;
			ctx.beginPath();
			ctx.moveTo(40, 80);
			ctx.lineTo(200, 80);
			ctx.stroke();
		}
		drawLine();

	</script>
	</html>

让线条动起来

当我们实现好线条剩下就需要让线条动起来,由于线条是通过灯罩的方式来实现的,让线条运动只需要让灯罩动起来就好
 

html5 canvas 实现光线沿不规则路径运动

<!DOCTYPE html>
	<html>
	<head>
		<title>canvas实现不规则路径光效</title>
	</head>
	<style type="text/css">
		body {
			background: #000;
		}
		#wrap {
			position: absolute;
			width: 1200px;
			height: 600px
		}
	</style>
	<body>
		<div id="wrap">
			<canvas id="canvas" width="1200" height="600"></canvas>
		</div>
	</body>
	<script type="text/javascript">
		var path = 'M 10 80 L 77.5 60 L 145 80 L 280 100 L 500 80 L 600 120 L 800 80 L 950 120 L 950 200 L 930 250 L 950 300 L 950 500';
		var list = path.match(/([A-Z]([^A-Z]){1,})/g).map(item => {
			return {
				x: item.split(' ')[1],
				y: item.split(' ')[2],
				action: item.split(' ')[0],
			}
		});//获取每个点位置
		var step = 3;
		var x1, x2, y1, y2;//确定路径中最大最小点
		var timer;
		var canvas = document.getElementById('canvas');
		var ctx = canvas.getContext('2d');
		ctx.strokeStyle = 'rgba(255,255,255,1)';
		ctx.shadowColor = 'rgba(255,255,255,1)';
		ctx.lineCap = 'round';
		ctx.shadowBlur = 3;
		list.forEach(item => {
			x1 = !x1 || Number(item.x) < x1 ? Number(item.x) : x1;
			y1 = !y1 || Number(item.y) < y1 ? Number(item.y) : y1;
			x2 = !x2 || Number(item.x) > x2 ? Number(item.x) : x2;
			y2 = !y2 || Number(item.y) > y2 ? Number(item.y) : y2;
		});
		function drawPath() {
			ctx.lineWidth = 3;
			ctx.beginPath();
			list.forEach(item => {
				if(item.action == 'M') ctx.moveTo(item.x, item.y);
				if(item.action == 'L') ctx.lineTo(item.x, item.y);
			});
			//添加光效渐变
			var grd = ctx.createLinearGradient(arrLine[arrLine.length - 1].x, arrLine[arrLine.length - 1].y, arrLine[0].x, arrLine[0].y);
			grd.addColorStop(0, 'rgba(255, 255, 255, 0)');   //定义渐变线起点颜色
	        grd.addColorStop(1, 'rgba(255, 255, 255, 1)');  //定义渐变线结束点的颜色
	        ctx.strokeStyle = grd;
			ctx.stroke();
		}
		//设计合适的初始线条状态
		var arrLine = Array(10).fill(0).map((item, inx) => {
			return {
				x: x1 - 20 * inx,
				y: y1 + 30,
			}
		});
		//随时间变化图形路径
		function getArrLine() {
			var isEnd
			arrLine = arrLine.map(item => {
				var x = item.x;
				var y = item.y;
				if(x < x2 - 30) {
					x = x + step > x2 -30 ? x2 - 30 : x + step;
				} else if(x == x2 -30 && y < y2) {
					y = y + step > y2 ? y2 : y + step;
				} else {
					isEnd = true;
				}
				return {
					x,
					y
				}
			});
			isEnd && timer && cancelAnimationFrame(timer);
		}
		//绘制图形
		function drawLine() {
			//设置图形组合方式 默认source-over
			ctx.globalCompositeOperation = "destination-in";
			ctx.lineWidth = 70;
			ctx.beginPath();
			arrLine.forEach((item, inx) => {
				if(inx == 0) {
					ctx.moveTo(item.x, item.y);
				} else {
					ctx.lineTo(item.x, item.y);
				}
			})
			ctx.stroke();
		}

		function start() {
			ctx.clearRect(0, 0, 1200, 600);
			ctx.globalCompositeOperation = 'source-over';
			drawPath();
			drawLine();
			getArrLine();
			timer = requestAnimationFrame(start);
		}

		timer = requestAnimationFrame(start);

	</script>
	</html>

这种实现方式也有一定的条件限制,那就是路径可大体抽象成为一个有一定规律的图型或者线条,比如上面demo中路径可抽象成为一个矩形的两边,或者是2条连接的直线
我们必须从没有具体规则的路径中抽象出一个大体的规则,不同路径规则不同
上面的例子就是将不规则路径抽象成了一个直角的规则路径
 

html5 canvas 实现光线沿不规则路径运动

可优化点

这边找到了2个可优化的点
1.时间方向上: 为了让动画消耗较小,代码中的定时器已经用的是requestAnimationFrame, 但是由于光线的特殊性(自带模糊效果),为了性能更加,尝试了2次requestAnimationFrame调用一次绘图的方式,效果较前者未有明显区别
2.绘图方向上: 从上图可发现,灯罩每次只圈出路径的一部分,故绘图中不需要每次都绘制全部路径,只需要找出灯罩前后的路径点,将这一段路径绘制出来就好

坑点

在完成这个动动画效果之后遇到一个至今原因不明的bug,随着屏幕放置时间的变长,动画越来越慢,打开任务管理器,未见内存泄漏或者cpu使用率过高。打开performance,发现页面调帧严重,屏幕帧数越来越低,单个Frame CPU time越来越长,范围来看,script和render和paint耗时未发生线性变化,只有system时间越来越来长,越来越长,期望能被大佬告知原因
一开始
 

html5 canvas 实现光线沿不规则路径运动

到后来
 

html5 canvas 实现光线沿不规则路径运动

解决的办法较为...,光线每循环一个周期,我销毁了之前的canvas并新建了canvas,上层规避了system time不知道为什么越来越长的问题
chrome版本:80.0.3987.163(正式版本) (64 位)

到此这篇关于html5 canvas 实现光线沿不规则路径运动的文章就介绍到这了,更多相关canvas 光线不规则运动内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章,希望大家以后多多支持三水点靠木!

HTML / CSS 相关文章推荐
一款纯css3实现的颜色渐变按钮的代码教程
Nov 12 HTML / CSS
CSS3 mask 遮罩的具体使用方法
Nov 03 HTML / CSS
css3新单位vw、vh的使用教程
Mar 23 HTML / CSS
Html5中的桌面通知Notification的实现
Sep 25 HTML / CSS
html5指南-6.如何创建离线web应用程序实现离线访问
Jan 07 HTML / CSS
HTML5 离线应用之打造零请求、无流量网站的解决方法
Apr 25 HTML / CSS
html5如何在Canvas中实现自定义路径动画示例
Sep 18 HTML / CSS
canvas拼图功能实现代码示例
Nov 21 HTML / CSS
html5 拖拽及用 js 实现拖拽功能的示例代码
Oct 23 HTML / CSS
CSS3通过var()和calc()函数实现动画特效
Mar 30 HTML / CSS
使用CSS设置滚动条样式
Jan 18 HTML / CSS
CSS元素定位之通过元素的标签或者元素的id、class属性定位详解
Sep 23 HTML / CSS
基于HTML5+tracking.js实现刷脸支付功能
Apr 16 #HTML / CSS
HTML中meta标签及Keywords
Apr 15 #HTML / CSS
详解移动端h5页面根据屏幕适配的四种方案
Apr 15 #HTML / CSS
html5移动端自适应布局的实现
Apr 15 #HTML / CSS
HTML里显示pdf、word、xls、ppt的方法示例
Apr 14 #HTML / CSS
HTML5 直播疯狂点赞动画实现代码 附源码
Apr 14 #HTML / CSS
HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码
Apr 10 #HTML / CSS
You might like
PHP 5.0 Pear安装方法
2006/12/06 PHP
PHP has encountered a Stack overflow问题解决方法
2014/11/03 PHP
php数组转成json格式的方法
2015/03/09 PHP
使用URL传输SESSION信息
2015/07/14 PHP
PHP实现的函数重载功能示例
2018/08/03 PHP
thinkPHP+mysql+ajax实现的仿百度一下即时搜索效果详解
2019/07/15 PHP
基于Laravel-admin 后台的自定义页面用法详解
2019/09/30 PHP
Ext JS Grid在IE6 下宽度的问题解决方法
2009/02/15 Javascript
Ext 表单布局实例代码
2009/04/30 Javascript
12种JavaScript常用的MVC框架比较分析
2015/11/16 Javascript
js中window.open的参数及注意注意事项
2016/07/06 Javascript
浅谈javascript中的Function和Arguments
2016/08/30 Javascript
jQuery is not defined 错误原因与解决方法小结
2017/03/19 Javascript
Angular.js自动化测试之protractor详解
2017/07/07 Javascript
JS仿QQ好友列表展开、收缩功能(第二篇)
2017/07/07 Javascript
使用JS实现导航切换时高亮显示的示例讲解
2018/08/22 Javascript
Vue 中如何正确引入第三方模块的方法步骤
2019/05/05 Javascript
JS猜数字游戏实例讲解
2020/06/30 Javascript
python链接Oracle数据库的方法
2015/06/28 Python
学习python中matplotlib绘图设置坐标轴刻度、文本
2018/02/07 Python
人脸识别经典算法一 特征脸方法(Eigenface)
2018/03/13 Python
python3使用flask编写注册post接口的方法
2018/12/28 Python
解决.ui文件生成的.py文件运行不出现界面的方法
2019/06/19 Python
python列表推导式操作解析
2019/11/26 Python
使用jupyter Nodebook查看函数或方法的参数以及使用情况
2020/04/14 Python
纯DOM+CSS3实现简单的小风车动画
2016/09/27 HTML / CSS
美国马匹用品和马钉购物网站:State Line Tack
2018/08/05 全球购物
小学生国旗下演讲稿
2014/04/25 职场文书
销售行政专员岗位职责
2014/06/10 职场文书
2014年中秋节活动总结
2014/08/29 职场文书
师德自我剖析材料范文
2014/10/06 职场文书
个人年度总结报告
2015/03/09 职场文书
2016年“5.12”国际护士节活动总结
2016/04/06 职场文书
Vue.js中v-for指令的用法介绍
2022/03/13 Vue.js
教你在 Java 中实现 Dijkstra 最短路算法的方法
2022/04/08 Java/Android
Win11怎么添加用户?Win11添加用户账户的方法
2022/07/15 数码科技