• 神奇的canvas——点与线绘制的绚丽动画效果


    代码地址如下:
    http://www.demodashi.com/demo/11636.html

    前言

    之前在某网站上看到了一个canvas绘制的动画效果,虽然组成的元素很简单,只有点和线,但是视觉效果却非常炫丽,当下就非常想自己把他实现一遍。因为工作原因这个想法搁置了一段时间,前不久忽然想起来,就抽空完成了这个demo,下面是demo的截图,想要看动态效果的小伙伴可以戳旁边的链接:canvas绘制绚丽的点线动画效果

    运行效果图

    free-dots.png

    下面就简单介绍一下完成这个demo的思路

    需要掌握的基础知识

    1. canvas绘制点与线的api
    2. 理解JavaScript中“类”

    需求分析

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

    编写代码(文章末尾有源码)

    点的实现

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

    // 声明一个Dot对象
    var Dots = function () {
        this.canvas; // canvas节点
        this.ctx; // canvas绘图上下文
        this.x; // 横向坐标
        this.y; // 纵向坐标
        this.r; // dot半径
        this.sx; // 单位时间水平移动距离
        this.sy; // 单位时间纵向移动距离
    };
    

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

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

    init()

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

    update()

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

    如何绘制一个点?

    点的本质是一个实心圆,所以绘制一个点就是绘制一个填充了颜色的圆

    this.ctx.beginPath(); // 开启绘制路径
    this.ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI); // 绘制圆 参数依次为 圆的横坐标/纵坐标/半径/绘制圆的起始位置/绘制圆的弧度大小
    this.ctx.fillStyle = "rgba(255,255,255,.8)"; // 设置填充颜色
    this.ctx.fill(); // 填充颜色
    this.ctx.closePath(); // 关闭绘制路径
    

    让点移动起来

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

    1. 当有耗时任务时,定时器任务会等待耗时任务结束,js引擎空闲时再去执行
    2. 当设定时间非常短时,可能会出现掉帧现象,产生动画不连贯的感受

    那么有什么方法可以解决这个问题呢?答案是使用全局函数requestAnimFrame()

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

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

    绘制点与点之间的连线

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

    如何绘制一条线?

    ctx.beginPath(); // 开启绘制路径
    ctx.moveTo(x, y); // 设置线的起始位置
    ctx.lineTo(dx, dy); // 设置线的结束位置
    ctx.strokeStyle = 'rgb(255,255,255)'; // 设置绘制线条的颜色
    ctx.strokeWidth = 1; // 设置绘制线条的宽度
    ctx.stroke(); // 绘制
    ctx.closePath(); // 关闭绘制路径
    

    如何实现线条的淡入淡出效果?

    设置reba颜色值可实现带透明度的颜色,透明度的计算方式为,(临界值距离 - 实际距离) / 临界值距离,这样就可以实现两点距离越远线条颜色越淡。再通过动画不停渲染,就可以造成视觉上淡入淡出的效果

    ctx.strokeStyle = 'rgba(255,255,255,'+(dotsDistance-s)/dotsDistance+')';
    

    实现鼠标交互

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

    document.addEventListener('click', createDot); // 添加点击事件
    function createDot(e) { // 点击事件方法
        var tx = e.pageX,
            ty = e.pageY; // 获取点击位置
        if ((tx > 0 && tx < width) && (ty > 0 && ty < height)) { // 判断是都在canvas内 width和height为canvas宽高
    		  for (var i = 0; i < 5; i ++) {
                var dot = new Dots();
                dotsArr.push(dot);
                dot.init(canvas, tx, ty);
            } // 循环创建5个点 并添加到数组中
        }
    };
    

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

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

    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();
        }
    
    document.addEventListener('mousemove', moveDot);
    function moveDot(e) {
        var tx = e.pageX,
            ty = e.pageY;
        if ((tx > 0 && tx < width) && (ty > 0 && ty < height)) {
            dot.mouseDot(tx, ty); // 更新跟踪点的位置 小伙伴们可以思考一下这里的dot对于的是dotsArr中的哪一个点
        }
    };
    

    性能优化

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

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

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

    至于是如何实现的,小伙伴们可以在源码中寻找答案哦!

    代码结构图

    文件结构类别如下


    神奇的canvas——点与线绘制的绚丽动画效果

    代码地址如下:
    http://www.demodashi.com/demo/11636.html

    注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    eclipse中的Invalid text string (xxx).
    在jsp文件中出现Unknown tag (c:out)
    eclipse 界面复原
    ecilpse 纠错插件
    Multiple annotations found at this line:- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    Port 8080 required by Tomcat v9.0 Server at localhost is already in use. The server may already be running in another process, or a system process may be using the port.
    调用第三方https接口
    调用第三方http接口
    创建带值枚举
    spring整合redis之Redis配置文件
  • 原文地址:https://www.cnblogs.com/demodashi/p/8509405.html
Copyright © 2020-2023  润新知