欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript實現(xiàn)文字黑洞特效的代碼詳解

 更新時間:2025年03月03日 09:32:22   作者:hy_2095  
這篇文章介紹了用 JavaScript 和 HTML5 Canvas 實現(xiàn)文字黑洞特效的項目,包括項目特征,如黑洞特效、引力效果、文字動畫和交互設(shè)計,以及技術(shù)亮點,還闡述了實現(xiàn)邏輯,涵蓋項目結(jié)構(gòu)、文字和黑洞對象的實現(xiàn)、動畫循環(huán)等,并給出了詳細的代碼示例和解釋

簡介:

在這篇教程中,我們將通過 JavaScript 和 HTML5 Canvas 實現(xiàn)一個酷炫的“文字黑洞”特效。當用戶觸摸屏幕時,黑洞會吞噬周圍的文字,并隨著手指移動;松開手指后,文字會以爆炸效果回到原位。本文將詳細介紹如何實現(xiàn)引力效果、動畫邏輯以及交互設(shè)計,帶你一步步完成這個有趣的項目。

項目特征

1. 核心功能

  • 黑洞特效:用戶觸摸屏幕時,生成一個黑洞,吸引周圍的文字。
  • 引力效果:文字對象會根據(jù)黑洞的位置和引力范圍被吸引。
  • 文字動畫:文字圍繞黑洞旋轉(zhuǎn),并在黑洞消失后以爆炸效果回到原位。
  • 交互設(shè)計:黑洞會跟隨用戶手指移動,松開手指后黑洞消失。

2. 技術(shù)亮點

  • HTML5 Canvas:用于繪制文字、黑洞和動畫效果。
  • JavaScript 物理模擬:實現(xiàn)引力、速度和位置的計算。
  • 緩動動畫:讓文字回到原位時更加自然。
  • 觸摸事件:支持移動端觸摸交互。

實現(xiàn)邏輯

1. 項目結(jié)構(gòu)

  • Canvas 初始化:設(shè)置畫布大小和樣式。
  • 文字對象:每個字符是一個獨立對象,記錄位置、速度和狀態(tài)。
  • 黑洞對象:記錄黑洞的位置、半徑和狀態(tài)。
  • 動畫循環(huán):通過 requestAnimationFrame 實現(xiàn)平滑動畫。

2. 核心邏輯

(1)文字對象的實現(xiàn)

每個文字對象(Character 類)包含以下屬性:

  • char:字符內(nèi)容。
  • origXorigY:字符的原始位置。
  • xy:字符的當前位置。
  • vxvy:字符的速度。
  • isCaptured:是否被黑洞捕獲。
  • angleangularSpeed:用于實現(xiàn)圍繞黑洞旋轉(zhuǎn)的效果。

代碼實現(xiàn):

class Character {
    constructor(char, x, y) {
        this.char = char;
        this.origX = x;
        this.origY = y;
        this.x = x;
        this.y = y;
        this.vx = 0;
        this.vy = 0;
        this.isCaptured = false;
        this.angle = Math.random() * Math.PI * 2;
        this.angularSpeed = (Math.random() - 0.5) * 0.1;
    }

    draw() {
        ctx.fillStyle = '#000';
        ctx.fillText(this.char, this.x, this.y);
    }

    update(blackHole) {
        if (blackHole && blackHole.active) {
            // 計算黑洞對字符的引力
            const dx = blackHole.x - this.x;
            const dy = blackHole.y - this.y;
            const dist = Math.sqrt(dx * dx + dy * dy);

            if (dist < blackHole.radius * 2) {
                const force = (blackHole.radius * 2 - dist) / (blackHole.radius * 2);
                const angle = Math.atan2(dy, dx);

                if (dist < blackHole.radius) {
                    this.isCaptured = true;
                    // 圍繞黑洞旋轉(zhuǎn)
                    this.angle += this.angularSpeed;
                    this.x = blackHole.x + Math.cos(this.angle) * blackHole.radius * 0.8;
                    this.y = blackHole.y + Math.sin(this.angle) * blackHole.radius * 0.8;
                } else {
                    this.vx += Math.cos(angle) * force * 1.5;
                    this.vy += Math.sin(angle) * force * 1.5;
                }
            } else {
                this.isCaptured = false;
            }
        } else {
            // 爆炸效果:從當前位置回到原位
            if (this.isCaptured) {
                this.isCaptured = false;
                this.vx = (Math.random() - 0.5) * 10;
                this.vy = (Math.random() - 0.5) * 10;
            }

            const dx = this.origX - this.x;
            const dy = this.origY - this.y;
            const dist = Math.sqrt(dx * dx + dy * dy);

            if (dist > 1) {
                // 緩動回到原位
                const easing = 0.1;
                this.vx += dx * easing;
                this.vy += dy * easing;
            } else {
                this.x = this.origX;
                this.y = this.origY;
                this.vx = 0;
                this.vy = 0;
            }
        }

        // 更新位置
        this.x += this.vx;
        this.y += this.vy;
        this.vx *= 0.95;
        this.vy *= 0.95;
    }
}

(2)黑洞對象的實現(xiàn)

黑洞對象(BlackHole 類)包含以下屬性:

  • xy:黑洞的位置。
  • radius:黑洞的半徑。
  • targetRadius:黑洞的目標半徑(用于動態(tài)調(diào)整大?。?/li>
  • active:黑洞是否處于活動狀態(tài)。
  • capturedCount:被吞噬的文字數(shù)量。

代碼實現(xiàn):

class BlackHole {
    constructor(x, y, radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.targetRadius = radius;
        this.active = false;
        this.capturedCount = 0;
    }

    draw() {
        if (this.active) {
            // 根據(jù)吞噬數(shù)量調(diào)整半徑
            this.targetRadius = Math.min(100, 60 + this.capturedCount * 2);
            this.radius += (this.targetRadius - this.radius) * 0.1;

            // 繪制光暈效果
            const gradient1 = ctx.createRadialGradient(
                this.x, this.y, 0,
                this.x, this.y, this.radius * 1.5
            );
            gradient1.addColorStop(0, 'hsla(45, 100%, 70%, 0.8)');
            gradient1.addColorStop(1, 'hsla(45, 100%, 50%, 0)');
            ctx.fillStyle = gradient1;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius * 1.5, 0, Math.PI * 2);
            ctx.fill();

            // 繪制黑洞核心
            const gradient2 = ctx.createRadialGradient(
                this.x, this.y, 0,
                this.x, this.y, this.radius
            );
            gradient2.addColorStop(0, 'hsl(45, 100%, 50%)');
            gradient2.addColorStop(1, 'hsla(45, 100%, 50%, 0)');
            ctx.fillStyle = gradient2;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
            ctx.fill();
        }
    }
}

(3)動畫循環(huán)

通過 requestAnimationFrame 實現(xiàn)動畫循環(huán),每一幀更新文字和黑洞的狀態(tài)并重新繪制。

代碼實現(xiàn):

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

    // 更新和繪制字符
    let capturedCount = 0;
    characters.forEach(char => {
        char.update(blackHole);
        char.draw();
        if (char.isCaptured) capturedCount++;
    });

    // 更新黑洞吞噬數(shù)量
    if (blackHole) blackHole.capturedCount = capturedCount;

    // 繪制黑洞
    if (blackHole) blackHole.draw();

    requestAnimationFrame(animate);
}
animate();

html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style>
        * { margin: 0; padding: 0; }
        canvas { touch-action: none; display: block; }
    </style>
</head>
<body>
<script src="index.js"></script>
</body>
</html>

index.js:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

// 固定 Canvas 尺寸
canvas.width = 320;
canvas.height = 720;

// 文本內(nèi)容
const textContent = `dealing with fonts with multiple weights and styles is easier to assign a different font family to each of them and just use different font families, in the example's code we do show how to load different weights with the same family name, but be aware that one font file has one family, one weight and one style. Loading the font Lato doesn't grant you access to all variants of the fonts, but just one. there is one file per variant.`;

// 字符類
class Character {
    constructor(char, x, y) {
        this.char = char; // 字符內(nèi)容
        this.origX = x; // 原始位置
        this.origY = y;
        this.x = x;
        this.y = y;
        this.vx = 0; // 速度
        this.vy = 0;
        this.isCaptured = false; // 是否被黑洞捕獲
        this.angle = Math.random() * Math.PI * 2; // 初始旋轉(zhuǎn)角度
        this.angularSpeed = (Math.random() - 0.5) * 0.1; // 旋轉(zhuǎn)速度
    }

    draw() {
        ctx.fillStyle = '#000';
        ctx.fillText(this.char, this.x, this.y);
    }

    update(blackHole) {
        if (blackHole && blackHole.active) {
            // 計算黑洞對字符的引力
            const dx = blackHole.x - this.x;
            const dy = blackHole.y - this.y;
            const dist = Math.sqrt(dx * dx + dy * dy);

            if (dist < blackHole.radius * 2) { // 引力范圍稍大一些
                const force = (blackHole.radius * 2 - dist) / (blackHole.radius * 2); // 引力強度
                const angle = Math.atan2(dy, dx);

                if (dist < blackHole.radius) {
                    this.isCaptured = true;
                    // 圍繞黑洞旋轉(zhuǎn)
                    this.angle += this.angularSpeed;
                    this.x = blackHole.x + Math.cos(this.angle) * blackHole.radius * 0.8;
                    this.y = blackHole.y + Math.sin(this.angle) * blackHole.radius * 0.8;
                } else {
                    this.vx += Math.cos(angle) * force * 1.5; // 增加引力強度
                    this.vy += Math.sin(angle) * force * 1.5;
                }
            } else {
                this.isCaptured = false;
            }
        } else {
            // 爆炸效果:從當前位置回到原位
            if (this.isCaptured) {
                this.isCaptured = false;
                // 賦予隨機速度模擬爆炸
                this.vx = (Math.random() - 0.5) * 10;
                this.vy = (Math.random() - 0.5) * 10;
            }

            const dx = this.origX - this.x;
            const dy = this.origY - this.y;
            const dist = Math.sqrt(dx * dx + dy * dy);

            if (dist > 1) {
                // 緩動回到原位,減少彈跳力
                const easing = 0.1; // 緩動系數(shù),控制返回速度
                this.vx += dx * easing;
                this.vy += dy * easing;
            } else {
                this.x = this.origX;
                this.y = this.origY;
                this.vx = 0;
                this.vy = 0;
            }
        }

        // 更新位置
        this.x += this.vx;
        this.y += this.vy;
        this.vx *= 0.95; // 速度衰減
        this.vy *= 0.95;
    }
}

// 黑洞類
class BlackHole {
    constructor(x, y, radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.targetRadius = radius; // 目標半徑
        this.active = false;
        this.capturedCount = 0; // 吞噬的文字對象數(shù)量
    }

    draw() {
        if (this.active) {
            // 根據(jù)吞噬數(shù)量調(diào)整半徑
            this.targetRadius = Math.min(100, 60 + this.capturedCount * 2); // 最大半徑為 100
            this.radius += (this.targetRadius - this.radius) * 0.1; // 緩動調(diào)整半徑

            // 繪制光暈效果
            const gradient1 = ctx.createRadialGradient(
                this.x, this.y, 0,
                this.x, this.y, this.radius * 1.5
            );
            gradient1.addColorStop(0, 'hsla(45, 100%, 70%, 0.8)'); // 光暈內(nèi)圈
            gradient1.addColorStop(1, 'hsla(45, 100%, 50%, 0)'); // 光暈外圈
            ctx.fillStyle = gradient1;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius * 1.5, 0, Math.PI * 2);
            ctx.fill();

            // 繪制黑洞核心
            const gradient2 = ctx.createRadialGradient(
                this.x, this.y, 0,
                this.x, this.y, this.radius
            );
            gradient2.addColorStop(0, 'hsl(45, 100%, 50%)'); // 核心顏色
            gradient2.addColorStop(1, 'hsla(45, 100%, 50%, 0)'); // 漸變透明
            ctx.fillStyle = gradient2;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
            ctx.fill();
        }
    }
}

// 生成字符對象
const characters = [];
const fontSize = 14;
const lineHeight = 20;
const maxWidth = canvas.width - 20; // 留出邊距
ctx.font = `${fontSize}px Arial`;

let x = 10, y = 30; // 初始位置
for (const char of textContent) {
    if (x + ctx.measureText(char).width > maxWidth || char === '\n') {
        x = 10;
        y += lineHeight;
    }
    if (char !== '\n') {
        characters.push(new Character(char, x, y));
        x += ctx.measureText(char).width;
    }
}

let blackHole = null;

// 觸摸事件處理
canvas.addEventListener('touchstart', (e) => {
    const { clientX, clientY } = e.touches[0];
    const rect = canvas.getBoundingClientRect();
    const x = clientX - rect.left;
    const y = clientY - rect.top;
    blackHole = new BlackHole(x, y, 60); // 黑洞初始半徑 60
    blackHole.active = true;
});

canvas.addEventListener('touchmove', (e) => {
    if (blackHole) {
        const { clientX, clientY } = e.touches[0];
        const rect = canvas.getBoundingClientRect();
        blackHole.x = clientX - rect.left;
        blackHole.y = clientY - rect.top;
    }
});

canvas.addEventListener('touchend', () => {
    if (blackHole) blackHole.active = false;
});

// 動畫循環(huán)
function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 更新和繪制字符
    let capturedCount = 0;
    characters.forEach(char => {
        char.update(blackHole);
        char.draw();
        if (char.isCaptured) capturedCount++;
    });

    // 更新黑洞吞噬數(shù)量
    if (blackHole) blackHole.capturedCount = capturedCount;

    // 繪制黑洞
    if (blackHole) blackHole.draw();

    requestAnimationFrame(animate);
}
animate();

總結(jié)

通過這篇教程,我們實現(xiàn)了一個基于 JavaScript 和 Canvas 的文字黑洞特效。核心邏輯包括:

  • 引力效果:通過計算字符與黑洞的距離和角度,模擬引力作用。
  • 動畫邏輯:使用緩動函數(shù)和隨機速度實現(xiàn)文字回到原位的爆炸效果。
  • 交互設(shè)計:通過觸摸事件實現(xiàn)黑洞的生成和移動。

以上就是使用JavaScript實現(xiàn)文字黑洞特效的代碼詳解的詳細內(nèi)容,更多關(guān)于JavaScript文字黑洞特效的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論