JavaScript implements particle text effects in HTML

This is a very practical little function, which is used to display particle text effects in HTML. What is the specific effect? ​​Not much to say, look at the picture: it will spread in full screen at the beginning, and then
special effects display
gather into preset text. Mouse movement and click on the text will generate corresponding diffusion effects, and automatically restore the text display. The size, number, moving speed of particles, text size, color, etc. can be customized.

The JS code is as follows:

/**
 * 粒子文字特效
 * 范例:
 * <canvas id="ChangeText">文字</canvas>
 * <script src="JavaScript/ParticleText.js"></script>
 * 默认页面居中显示
 * @author Rob Sivan
 * @date 2022/9/20
 */
const COLOR = "#39BC54"; // 设定粒子特效颜色
let MESSAGE = document.getElementById("ChangeText").textContent; // 根据标签的ID获取待处理的文字内容

let FONT_SIZE = (window.innerWidth * 0.08); // 字体大小
let AMOUNT = 6000; // 设定粒子数量
let SIZE = 2; // 粒子大小
let INITIAL_DISPLACEMENT = 500; // 最初位移量
const INITIAL_VELOCITY = 7.5; // 最初速度
const VELOCITY_RETENTION = 0.95; // 速度保持
let SETTLE_SPEED = 1; // 稳定速度
const FLEE_SPEED = 2; // 逃逸速度
const FLEE_DISTANCE = 50; // 逃逸距离
let FLEE = true; // 逃逸模式
let SCATTER_VELOCITY = 3; // 散射速度
const SCATTER = true; // 散射模式

// 若处于移动设备展示
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
    
    
    // Mobile
    MESSAGE = document.getElementById("ChangeText").textContent; // 通过标签ID获取文本内容

    FONT_SIZE = 50;  // 字体大小减小
    AMOUNT = 300; // 粒子数量减少
    SIZE = 2;
    INITIAL_DISPLACEMENT = 100; // 最初位移量减少
    SETTLE_SPEED = 1; // 最初速度减少
    FLEE = false; // 关闭逃逸模式
    SCATTER_VELOCITY = 2; // 散射速度
}

const canvas = document.getElementById("ChangeText");
const ctx = canvas.getContext("2d"); // 创建画布

let POINTS = [];
const MOUSE = {
    
    
    x: 0,
    y: 0
};

function Point(x, y, r, g, b, a) {
    
    
    const angle = Math.random() * 6.28;
    this.x = canvas.width / 2 - x + (Math.random() - 0.5) * INITIAL_DISPLACEMENT;
    this.y = canvas.height / 2 - y + (Math.random() - 0.5) * INITIAL_DISPLACEMENT;
    this.velx = INITIAL_VELOCITY * Math.cos(angle);
    this.vely = INITIAL_VELOCITY * Math.sin(angle);
    this.target_x = canvas.width / 2 - x;
    this.target_y = canvas.height / 2 - y;
    this.r = r;
    this.g = g;
    this.b = b;
    this.a = a;

    this.getX = function () {
    
    
        return this.x;
    }

    this.getY = function () {
    
    
        return this.y;
    }
    this.fleeFrom = function () {
    
    
        this.velx -= ((MOUSE.x - this.x) * FLEE_SPEED / 10);
        this.vely -= ((MOUSE.y - this.y) * FLEE_SPEED / 10);
    }

    this.settleTo = function () {
    
    
        this.velx += ((this.target_x - this.x) * SETTLE_SPEED / 100);
        this.vely += ((this.target_y - this.y) * SETTLE_SPEED / 100);
        this.velx -= this.velx * (1 - VELOCITY_RETENTION);
        this.vely -= this.vely * (1 - VELOCITY_RETENTION);
    }

    this.scatter = function () {
    
    
        const unit = this.unitVecToMouse();
        const vel = SCATTER_VELOCITY * 10 * (0.5 + Math.random() / 2);
        this.velx = -unit.x * vel;
        this.vely = -unit.y * vel;
    }

    this.move = function () {
    
    
        if (this.distanceToMouse() <= FLEE_DISTANCE) {
    
    
            this.fleeFrom();
        } else {
    
    
            this.settleTo();
        }

        if (this.x + this.velx < 0 || this.x + this.velx >= canvas.width) {
    
    
            this.velx *= -1;
        }
        if (this.y + this.vely < 0 || this.y + this.vely >= canvas.height) {
    
    
            this.vely *= -1;
        }

        this.x += this.velx;
        this.y += this.vely;
    }
    this.distanceToMouse = function () {
    
    
        return this.distanceTo(MOUSE.x, MOUSE.y);
    }

    this.distanceTo = function (x, y) {
    
    
        return Math.sqrt((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y));
    }
    this.unitVecToMouse = function () {
    
    
        return this.unitVecTo(MOUSE.x, MOUSE.y);
    }

    this.unitVecTo = function (x, y) {
    
    
        const dx = x - this.x;
        const dy = y - this.y;
        return {
    
    
            x: dx / Math.sqrt(dx * dx + dy * dy),
            y: dy / Math.sqrt(dx * dx + dy * dy)
        };
    }
}

window.addEventListener("resize", function () {
    
    
    resizeCanvas()
    adjustText()
});

if (FLEE) {
    
    
    window.addEventListener("mousemove", function (event) {
    
    
        MOUSE.x = event.clientX;
        MOUSE.y = event.clientY;
    });
}

if (SCATTER) {
    
    
    window.addEventListener("click", function (event) {
    
    
        MOUSE.x = event.clientX;
        MOUSE.y = event.clientY;
        for (let i = 0; i < POINTS.length; i++) {
    
    
            POINTS[i].scatter();
        }
    });
}

function resizeCanvas() {
    
    
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
}

function adjustText() {
    
    
    ctx.fillStyle = COLOR;
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.font = FONT_SIZE + "px Arial";
    ctx.fillText(MESSAGE, canvas.width / 2, canvas.height / 2);
    const textWidth = ctx.measureText(MESSAGE).width;
    if (textWidth === 0) {
    
    
        return;
    }
    const minX = canvas.width / 2 - textWidth / 2;
    const minY = canvas.height / 2 - FONT_SIZE / 2;
    const data = ctx.getImageData(minX, minY, textWidth, FONT_SIZE).data;
    let isBlank = true;
    for (let i = 0; i < data.length; i++) {
    
    
        if (data[i] !== 0) {
    
    
            isBlank = false;
            break;
        }
    }

    if (!isBlank) {
    
    
        let count = 0;
        let curr = 0;
        let num = 0;
        let x = 0;
        let y = 0;
        const w = Math.floor(textWidth);
        POINTS = [];
        while (count < AMOUNT) {
    
    
            while (curr === 0) {
    
    
                num = Math.floor(Math.random() * data.length);
                curr = data[num];
            }
            num = Math.floor(num / 4);
            x = w / 2 - num % w;
            y = FONT_SIZE / 2 - Math.floor(num / w);
            POINTS.push(new Point(x, y, data[num * 4], data[num * 4 + 1], data[num * 4 + 2], data[num * 4 + 3]));
            curr = 0;
            count++;
        }
    }
}

function init() {
    
    
    resizeCanvas()
    adjustText()
    window.requestAnimationFrame(animate);
}

function animate() {
    
    
    update();
    draw();
}

function update() {
    
    
    let point;
    for (let i = 0; i < POINTS.length; i++) {
    
    
        point = POINTS[i];
        point.move();
    }
}

function draw() {
    
    
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    let point;
    for (let i = 0; i < POINTS.length; i++) {
    
    
        point = POINTS[i];
        ctx.fillStyle = "rgba(" + point.r + "," + point.g + "," + point.b + "," + point.a + ")";
        ctx.beginPath();
        ctx.arc(point.getX(), point.getY(), SIZE, 0, 2 * Math.PI);
        ctx.fill();
    }

    window.requestAnimationFrame(animate);
}

init();

For the specific usage method, you can call it externally:
Example of external call
here you need to use the canvas tag and define its ID. Of course, the external call is to make the theme of the page more concise, and if you want to save trouble, you can also directly pull it under it?
Example of internal calls
The specific sample code is:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试页面</title>
</head>
<body>
<canvas id="ChangeText">测试文本</canvas>

<script type="text/javascript">
    const COLOR = "#39BC54"; // 设定粒子特效颜色
    let MESSAGE = document.getElementById("ChangeText").textContent; // 根据标签的ID获取待处理的文字内容

    let FONT_SIZE = (window.innerWidth * 0.08); // 字体大小
    let AMOUNT = 6000; // 设定粒子数量
    let SIZE = 2; // 粒子大小
    let INITIAL_DISPLACEMENT = 500; // 最初位移量
    const INITIAL_VELOCITY = 7.5; // 最初速度
    const VELOCITY_RETENTION = 0.95; // 速度保持
    let SETTLE_SPEED = 1; // 稳定速度
    const FLEE_SPEED = 2; // 逃逸速度
    const FLEE_DISTANCE = 50; // 逃逸距离
    let FLEE = true; // 逃逸模式
    let SCATTER_VELOCITY = 3; // 散射速度
    const SCATTER = true; // 散射模式

    // 若处于移动设备展示
    if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
      
      
        // Mobile
        MESSAGE = document.getElementById("ChangeText").textContent; // 通过标签ID获取文本内容

        FONT_SIZE = 50;  // 字体大小减小
        AMOUNT = 300; // 粒子数量减少
        SIZE = 2;
        INITIAL_DISPLACEMENT = 100; // 最初位移量减少
        SETTLE_SPEED = 1; // 最初速度减少
        FLEE = false; // 关闭逃逸模式
        SCATTER_VELOCITY = 2; // 散射速度
    }

    const canvas = document.getElementById("ChangeText");
    const ctx = canvas.getContext("2d"); // 创建画布

    let POINTS = [];
    const MOUSE = {
      
      
        x: 0,
        y: 0
    };

    function Point(x, y, r, g, b, a) {
      
      
        const angle = Math.random() * 6.28;
        this.x = canvas.width / 2 - x + (Math.random() - 0.5) * INITIAL_DISPLACEMENT;
        this.y = canvas.height / 2 - y + (Math.random() - 0.5) * INITIAL_DISPLACEMENT;
        this.velx = INITIAL_VELOCITY * Math.cos(angle);
        this.vely = INITIAL_VELOCITY * Math.sin(angle);
        this.target_x = canvas.width / 2 - x;
        this.target_y = canvas.height / 2 - y;
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;

        this.getX = function () {
      
      
            return this.x;
        }

        this.getY = function () {
      
      
            return this.y;
        }
        this.fleeFrom = function () {
      
      
            this.velx -= ((MOUSE.x - this.x) * FLEE_SPEED / 10);
            this.vely -= ((MOUSE.y - this.y) * FLEE_SPEED / 10);
        }

        this.settleTo = function () {
      
      
            this.velx += ((this.target_x - this.x) * SETTLE_SPEED / 100);
            this.vely += ((this.target_y - this.y) * SETTLE_SPEED / 100);
            this.velx -= this.velx * (1 - VELOCITY_RETENTION);
            this.vely -= this.vely * (1 - VELOCITY_RETENTION);
        }

        this.scatter = function () {
      
      
            const unit = this.unitVecToMouse();
            const vel = SCATTER_VELOCITY * 10 * (0.5 + Math.random() / 2);
            this.velx = -unit.x * vel;
            this.vely = -unit.y * vel;
        }

        this.move = function () {
      
      
            if (this.distanceToMouse() <= FLEE_DISTANCE) {
      
      
                this.fleeFrom();
            } else {
      
      
                this.settleTo();
            }

            if (this.x + this.velx < 0 || this.x + this.velx >= canvas.width) {
      
      
                this.velx *= -1;
            }
            if (this.y + this.vely < 0 || this.y + this.vely >= canvas.height) {
      
      
                this.vely *= -1;
            }

            this.x += this.velx;
            this.y += this.vely;
        }
        this.distanceToMouse = function () {
      
      
            return this.distanceTo(MOUSE.x, MOUSE.y);
        }

        this.distanceTo = function (x, y) {
      
      
            return Math.sqrt((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y));
        }
        this.unitVecToMouse = function () {
      
      
            return this.unitVecTo(MOUSE.x, MOUSE.y);
        }

        this.unitVecTo = function (x, y) {
      
      
            const dx = x - this.x;
            const dy = y - this.y;
            return {
      
      
                x: dx / Math.sqrt(dx * dx + dy * dy),
                y: dy / Math.sqrt(dx * dx + dy * dy)
            };
        }
    }

    window.addEventListener("resize", function () {
      
      
        resizeCanvas()
        adjustText()
    });

    if (FLEE) {
      
      
        window.addEventListener("mousemove", function (event) {
      
      
            MOUSE.x = event.clientX;
            MOUSE.y = event.clientY;
        });
    }

    if (SCATTER) {
      
      
        window.addEventListener("click", function (event) {
      
      
            MOUSE.x = event.clientX;
            MOUSE.y = event.clientY;
            for (let i = 0; i < POINTS.length; i++) {
      
      
                POINTS[i].scatter();
            }
        });
    }

    function resizeCanvas() {
      
      
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }

    function adjustText() {
      
      
        ctx.fillStyle = COLOR;
        ctx.textBaseline = "middle";
        ctx.textAlign = "center";
        ctx.font = FONT_SIZE + "px Arial";
        ctx.fillText(MESSAGE, canvas.width / 2, canvas.height / 2);
        const textWidth = ctx.measureText(MESSAGE).width;
        if (textWidth === 0) {
      
      
            return;
        }
        const minX = canvas.width / 2 - textWidth / 2;
        const minY = canvas.height / 2 - FONT_SIZE / 2;
        const data = ctx.getImageData(minX, minY, textWidth, FONT_SIZE).data;
        let isBlank = true;
        for (let i = 0; i < data.length; i++) {
      
      
            if (data[i] !== 0) {
      
      
                isBlank = false;
                break;
            }
        }

        if (!isBlank) {
      
      
            let count = 0;
            let curr = 0;
            let num = 0;
            let x = 0;
            let y = 0;
            const w = Math.floor(textWidth);
            POINTS = [];
            while (count < AMOUNT) {
      
      
                while (curr === 0) {
      
      
                    num = Math.floor(Math.random() * data.length);
                    curr = data[num];
                }
                num = Math.floor(num / 4);
                x = w / 2 - num % w;
                y = FONT_SIZE / 2 - Math.floor(num / w);
                POINTS.push(new Point(x, y, data[num * 4], data[num * 4 + 1], data[num * 4 + 2], data[num * 4 + 3]));
                curr = 0;
                count++;
            }
        }
    }

    function init() {
      
      
        resizeCanvas()
        adjustText()
        window.requestAnimationFrame(animate);
    }

    function animate() {
      
      
        update();
        draw();
    }

    function update() {
      
      
        let point;
        for (let i = 0; i < POINTS.length; i++) {
      
      
            point = POINTS[i];
            point.move();
        }
    }

    function draw() {
      
      
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        let point;
        for (let i = 0; i < POINTS.length; i++) {
      
      
            point = POINTS[i];
            ctx.fillStyle = "rgba(" + point.r + "," + point.g + "," + point.b + "," + point.a + ")";
            ctx.beginPath();
            ctx.arc(point.getX(), point.getY(), SIZE, 0, 2 * Math.PI);
            ctx.fill();
        }

        window.requestAnimationFrame(animate);
    }

    init();
</script>
</body>
</html>

emmmmm... It's really a lot, and I only put a text here, and then add other page elements, which is not conducive to later maintenance. Of course, it's up to you.

Okay, everyone, use your imagination to debug, little Afan is smooth~
insert image description here

Guess you like

Origin blog.csdn.net/m0_46700215/article/details/126963561