广告位联系
返回顶部
分享到

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

html5 来源:互联网搜集 作者:秩名 发布时间:2020-04-21 20:51:19 人浏览
摘要

svg让动画沿着不规则路径运动 查阅svg文档后发现,svg动画运动有两种实现方式,且都非常简单,但对于100%实现设计师给出的效果有很大的距离 使用offset-path偏移路径和offset-roate偏移角度让元素沿着不规则路径走 !DOCTYPE html html head titleoffset-path

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组合图形的模式,取线条和一个图形重叠部分(类似于灯罩)来实现光线效果
 

组合前
 


 

组合后
 

<!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>
 

让线条动起来

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

<!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条连接的直线
我们必须从没有具体规则的路径中抽象出一个大体的规则,不同路径规则不同
上面的例子就是将不规则路径抽象成了一个直角的规则路径
 

可优化点

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

坑点

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

到后来
 

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


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://www.jb51.net/html5/721370.html
相关文章
  • html5+实现plus.io进行拍照和图片等获取
    使用Hbuilder开发工具开发:实现可对Android机进行控制和获取资源 说明:IO模块管理本地文件系统,用于对文件系统的目录浏览、文件的读取
  • HTML标签实现带动画的抖音LOGO效果

    HTML标签实现带动画的抖音LOGO效果
    先上结果,最终实现效果如下: 还原度应该还可以吧? 抖音Logo结构 想要用CSS来画抖音的Logo,前提要先了解它的构造,一定是一些几何图形
  • video.js支持m3u8格式直播的实现教程

    video.js支持m3u8格式直播的实现教程
    为什么要使用video.js? 1. PC端浏览器并不支持video直接播放m3u8格式的视频 2. 手机端各式各样的浏览器定制的video界面风格不统一,直接写原生
  • html5调用摄像头截图功能的介绍
    关于html5调用音视频等多媒体硬件的API已经很成熟,不过一直找不到机会把这些硬件转化为实际的应用场景,不过近年来随着iot和AI的浪潮,
  • 前端监听websocket消息并实时弹出的代码实例介绍

    前端监听websocket消息并实时弹出的代码实例介绍
    本文默认您已掌握react生态开发的相关技术,并熟练应用umiJS的原则上,请继续! 项目需求: 1、服务侧推送给消息给前端,前端需要展示在
  • HTML5新增内容和API介绍

    HTML5新增内容和API介绍
    classList 属性 该属性用于在元素中添加,移除及切换 CSS 类。 classList属性返回元素的类名,作为 DOMTokenList 对象: div id=div class=test1 test2clas
  • 使用canvas仿Echarts实现金字塔图的教程

    使用canvas仿Echarts实现金字塔图的教程
    最近公司项目都偏向于数字化大屏展示????,而这次发给我的项目原型中出现了一个金字塔图?????, 好巧不巧,由于我们的图表都是使用Ech
  • 基于HTML十秒做出淘宝页面的方法

    基于HTML十秒做出淘宝页面的方法
    十秒钟做出一个网页,可能大家看完会觉得有点标题党,其实不然,把别人的抠过来还是可以的 比如我这次先把淘宝的网页扣过来为例 第一
  • HTML5+CSS+JavaScript实现捉虫小游戏的代码

    HTML5+CSS+JavaScript实现捉虫小游戏的代码
    捉虫小游戏 首页展示: 选择昆虫: 效果展示: 有密集恐惧症的别玩哟、游戏永远不会停止 一直玩 项目源码结构: 图片和js以及css等基础
  • Canvas绘制像素风图片的代码

    Canvas绘制像素风图片的代码
    童年玩红白机。尤其国内的小霸王那段时光还记得么。那个马里奥大叔还记得么。 因为特别喜欢像素风的游戏从小到大一直都是,像素风本
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计