JS实现动态点图酷炫效果

发布于:2025-04-01 ⋅ 阅读:(17) ⋅ 点赞:(0)

实现目标

在这里插入图片描述

分析问题

整个图主要是用canvas实现,其中难点是将线的长度控制在一定范围内、并且透明度随长度变化。

前置知识

canvas绘制点、线、三角形、弧形

	// 点
 	ctx.moveTo(this.x, this.y);
    ctx.arc(this.x, this.y, this.r,
     		0, 2 * Math.PI, false);
    ctx.fillStyle = "white";
    ctx.fill();
	// 线
 	ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    // 三角形
    ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.lineTo(p3.x, p3.y);
    ctx.closePath();
    // 弧形
    ctx.beginPath();
	ctx.arc(100, 100, 50, 0, Math.PI / 2, false); // 从 0° 到 90°(顺时针)
	ctx.strokeStyle = "blue";
	ctx.lineWidth = 3;
	ctx.stroke();

代码实战

实现思路
随机生成n个点,然后随机选m个 三个随机点(属于n个点中) 组成的集合绘制三角形。

<head>
    <style>
        * {
            margin: 0 auto;
            padding: 0;
        }
        canvas {
            position: fixed;
            background: black;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <canvas></canvas>
    <script src="./index.js"></script>
</body>
// 获取 canvas节点
let cts = document.querySelector("canvas");
let ctx = cts.getContext("2d");
// 设置宽高
cts.width = window.innerWidth;
cts.height = window.innerHeight;

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.r = 1;
    }

    draw() {
        ctx.moveTo(this.x, this.y);
        ctx.arc(this.x, this.y, this.r,
            0, 2 * Math.PI, false);
        ctx.fillStyle = "white";
        ctx.fill();
    }
}

class Graph {

    constructor() {
    	// 圆点个数
        this.pointSize = 500;
        this.maxHeight = window.innerHeight;
        this.maxWidth = window.innerWidth;
        // 对角线
        this.maxLine = Math.sqrt(this.maxHeight * this.maxHeight + this.maxWidth * this.maxWidth);
        // 所有点集
        this.points = new Array(this.pointSize).fill(0)
            .map(() =>
                new Point(Math.random() * this.maxWidth, Math.random() * this.maxHeight))
    }
    
	// 绘制 p1-p2 直线
    drawLine(p1, p2) {
        let tLine =  Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));

        let at = 0.2 - tLine / 500;// this.maxLine;
        // 太长的线不绘制
        if (tLine > 100) return;
      
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.strokeStyle = `rgba(255, 255, 255, ${at})`;
    }

	// 绘制三角形
    drawRan(p1, p2, p3) {
        if (!p1 || !p2 || !p3) return;
        ctx.beginPath();
        this.drawLine(p1, p2);
        this.drawLine(p2, p3);
        ctx.stroke();
    }

    draw() {
   		// 绘制所有圆点
        for (let i = 0; i < this.pointSize; i++) {
            let p1 = this.points[i];
            p1.draw();
        }

		// 选取随机的三点绘制三角形
        for (let i = 0; i < this.pointSize * this.pointSize / 10; i++) {
            let t1 = Math.floor(Math.random() * this.pointSize);
            let t2 =  Math.floor(Math.random() * this.pointSize);
            let t3 =  Math.floor(Math.random() * this.pointSize);
            let p1 = this.points[t1];
            let p2 = this.points[t2];
            let p3 = this.points[t3];
            this.drawRan(p1, p2, p3);
        }
    }
}

let init = () => {
    let g = new Graph();
    g.draw();
}
init();

优化

点集动态移动(弧形),随机选点、弧形半径随机。


let cts = document.querySelector("canvas");
let ctx = cts.getContext("2d");

let setWH = () => {
    cts.width = window.innerWidth;
    cts.height = window.innerHeight;
}
let clearBG = () => {
    ctx.clearRect(0, 0, cts.width, cts.height);
}

class Point {
    constructor(x, y) {
        this.px = x;
        this.py = y;
        this.r = 1;
        this.radius = 500 * Math.random();
        this.angle = 0;

    }

    draw() {
        ctx.moveTo(this.x, this.y);
        ctx.arc(this.x, this.y, this.r,
            0, 2 * Math.PI, false);
        ctx.fillStyle = "white";
        ctx.fill();
    }

    move() {
        this.x = this.px + this.radius * Math.cos(this.angle);
        this.y = this.py + this.radius * Math.sin(this.angle);
        this.angle = (this.angle + 0.05) % (Math.PI * 2);
    }
}

class Graph {

    constructor() {
        this.pointSize = 500;
        this.maxHeight = window.innerHeight;
        this.maxWidth = window.innerWidth;
        this.maxLine = Math.sqrt(this.maxHeight * this.maxHeight + this.maxWidth * this.maxWidth);

        this.points = new Array(this.pointSize).fill(0)
            .map(() =>
                new Point(Math.random() * this.maxWidth, Math.random() * this.maxHeight))
    }

    drawLine(p1, p2) {
        let tLine =  Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));

        let at = 0.2 - tLine / 500;// this.maxLine;
        if (tLine > 100) return;
 
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.strokeStyle = `rgba(255, 255, 255, ${at})`;
    }

    drawRan(p1, p2, p3) {
        if (!p1 || !p2 || !p3) return;
        ctx.beginPath();
        this.drawLine(p1, p2);
        this.drawLine(p2, p3);
        ctx.stroke();
    }

    draw() {

        for (let i = 0; i < this.pointSize; i++) {
            let p1 = this.points[i];
            p1.move();
            p1.draw();
        }

        for (let i = 0; i < this.pointSize * this.pointSize / 10; i++) {
            let t1 = Math.floor(Math.random() * this.pointSize);
            let t2 =  Math.floor(Math.random() * this.pointSize);
            let t3 =  Math.floor(Math.random() * this.pointSize);
            let p1 = this.points[t1];
            let p2 = this.points[t2];
            let p3 = this.points[t3];
            this.drawRan(p1, p2, p3);
        }
    }
}

let timer = null;

let init = () => {
    setWH();

    let g = new Graph();
    timer = setInterval(() => {
        clearBG();
        g.draw();
    }, 1000)
}
init();


网站公告

今日签到

点亮在社区的每一天
去签到