欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一个有意思的Canvas绘图

程序员文章站 2022-05-28 18:49:50
...

pre

很久之前朋友给我看过一个网站的背景canvas,感觉有意思,今天正好又看到一个网站用了这个背景,所以我就直接从网页上摘下这段代码来看一下实现原理。
效果如下
一个有意思的Canvas绘图
(为了分析的方便,这里面的线条粗细以及颜色被调整过)

参考网页效果
实际网页效果

源码分析

(源码经过轻微修改,在关键步骤都做了相应的注释)

<html>
<body>
</body>
</html>
<script color="255,0,0" count="200">

    !function() {
        function A(a, b, c) {
            return a.getAttribute(b) || c
        }
        function F(a) {
            return document.getElementsByTagName(a);
        }
        function D() {
            var c = F("script"),
                a = c.length,
                b = c[a - 1];
            return {
                l: a,
                z: A(b, "zIndex", -1),
                o: A(b, "opacity", 0.5),
                c: A(b, "color", "0,0,0"),
                n: A(b, "count", 199)
            }
        }
        function E() {
            x = i.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
            B = i.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
        }
        function M() {
            J.clearRect(0, 0, x, B);
            var c = [I].concat(v);//将当前鼠标位置压入栈中
            var b, d, a, g, e, f;
            v.forEach(function(h) {
                h.x += h.xa,
                    h.y += h.ya,
                    h.xa *= h.x > x || h.x < 0 ? -1 : 1,
                    h.ya *= h.y > B || h.y < 0 ? -1 : 1,
                    J.fillRect(h.x - 0.5, h.y - 0.5, 1, 1);//以坐标点为中心,绘制一个1*1的矩形区域
                for (d = 0; d < c.length; d++) {
                    b = c[d];
                    if (h !== b && null !== b.x && null !== b.y) {
                        //计算之间的距离,不开根号,节省时间
                        g = h.x - b.x;
                        e = h.y - b.y;
                        f = g * g + e * e;
                        //纯色填充
                        f < b.max && (b === I && f >= b.max / 2 && (h.x -= 0.03 * g, h.y -= 0.03 * e), a = (b.max - f) / b.max, J.beginPath(), J.lineWidth = a *4, J.strokeStyle = "rgba(" + w.c + "," + (a + 0.2) + ")", J.moveTo(h.x, h.y), J.lineTo(b.x, b.y), J.stroke())
                        //变色填充
                        //linecolor = Math.floor(N()*255)+","+Math.floor(N()*255)+","+Math.floor(N()*255);
                        //f < b.max && (b === I && f >= b.max / 2 && (h.x -= 0.03 * g, h.y -= 0.03 * e), a = (b.max - f) / b.max, J.beginPath(), J.lineWidth = a / 2, J.strokeStyle = "rgba(" + linecolor + "," + (a + 0.2) + ")", J.moveTo(h.x, h.y), J.lineTo(b.x, b.y), J.stroke())
                    }
                }
                c.splice(c.indexOf(h), 1);
            }),
                C(M);
        }
        var i = document.createElement("canvas"),
            w = D(),//初始属性
            L = "c_n" + w.l, //id
            J = i.getContext("2d"),
            x,//可视宽度
            B,//可视高度
            C = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
                function(a) {
                    window.setTimeout(a, 1000 / 4)
                },//获取刷新函数
            N = Math.random,//随机函数
            I = {
                x: null,
                y: null,
                max: 20000
            };//记录窗口位置

        //在页面中添加canvas元素
        i.id = L;
        i.style.cssText = "position:fixed;top:0;left:0;z-index:" + w.z + ";opacity:" + w.o;
        document.body.appendChild(i);

        //捕获窗口可视区域大小
        E();
        window.onresize = E;

        //捕获鼠标位置
        window.onmousemove = function(a) {
            a = a || window.event,
                I.x = a.clientX,
                I.y = a.clientY
        };
        window.onmouseout = function() {
            I.x = null,
                I.y = null
        };

        //随机产生 w.n个初始点,并且初始化方向
        for (var v = [], z = 0; w.n > z; z++) {
            var G = N() * x,
                H = N() * B,
                y = 2 * N() - 1,
                K = 2 * N() - 1;
            v.push({
                x: G,
                y: H,
                xa: y,
                ya: K,
                max: 6000
            });
        }
        //开启定时刷新
        C(M)
    } ();
</script>

post

这样的效果实现起来没有什么困难,主要是存在几个设计上的亮点。

鼠标交互

在每一帧刷新的时候,用户的鼠标位置会被传入,然后影响整体绘图,这样的交互对于部分用户来说可能会带来惊喜感,当然不排除因为背景交互而影响内容的呈现的问题,所以在实际应用在一些网站上时,线条的移动速度和刷新率会做权衡

吸附效果

设计者在连线的时候没有直接连,他将对应的顶点拉近一个小距离,在用户的视觉上呈现的效果就是吸附了。当然会存在顶点被捕获的问题,最主要体现在鼠标顶点上,如果一个背景上的顶点位于鼠标顶点的捕获范围,同时该顶点有一个逃离鼠标捕获范围的趋势,如果顶点的逃离速度无法克服顶点吸附带来的影响,那么这个顶点就会在边界不停的抖动。当然如果两个运动方向正好想法或者有相对运动的顶点,也可以出现一个顶点被另外一个顶点拉着跑的情况。
总体来说,吸附效果增强了交互感。

层次感

如果单纯给运动中的点连线,那么这样的2D效果还不是那么OK,设计者在连线的时候参考点之间的距离为不同的练习赋予不同的线宽,为动画增加了一种层次感。利于是错觉的原理,用户会认为3D中的平面图形处于旋转状态。当然由于线宽的规则不是那么复杂,所以仔细看,还是会存在很多漏洞。

post

不得不说,前端的学问很深,难度不仅在技术上。