ホームページ >ウェブフロントエンド >jsチュートリアル >フロントエンド リフレッシュ プロジェクト - 電子スパイダー

フロントエンド リフレッシュ プロジェクト - 電子スパイダー

DDD
DDDオリジナル
2024-09-19 03:20:02513ブラウズ

序文: JavaScript を学習すると、JavaScript を使用して興味深い効果を実現できるようになります。この記事では、JavaScript を使用して電子スパイダーを Web ページに実装する方法について説明します。

ウェブ スパイダーの書き方を学び始める前に、この電子スパイダーがどのようなものかを見てみましょう。

Frontend Refresh Project - An Electronic Spider

マウスで動くことがわかりますが、この効果はどのようにして実現するのでしょうか?説明を始めましょう。

HTMLコード

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dreaming</title>
    <!-- External JavaScript files -->
    <script src=".test.js"></script>
    <style>
        /* Remove default padding and margins from body */
        body {
            margin: 0px;
            padding: 0px;
            position: fixed;
            /* Set the background color of webpage to black */
            background: rgb(0, 0, 0);
        }
    </style>
</head>

<body>
    <!-- Create a canvas for drawing -->
    <canvas id="canvas"></canvas>
</body>

</html>

ご覧のとおり、HTML コードは非常にシンプルなので、作業を始めてみましょう!

JavaScript コードを書き始める前に、計画を作成してください。

全体的なプロセス

  • ページが読み込まれると、キャンバス要素と描画コンテキストが初期化されます。

  • 触手オブジェクトを定義します。各触手は複数のセグメントで構成されています。

  • マウスの移動イベントをリッスンし、マウスの位置をリアルタイムで更新します。

  • 触手はアニメーション ループを通じて描画され、マウスの位置に応じて動的に変化し、滑らかなアニメーション効果を生み出します。

一般的なプロセスは上記の手順ですが、このコードを書き終えるまでは上記のプロセスを理解できないかもしれませんが、とにかく問題ないので、Web スパイダーの作成を始めましょう:

序文: コードのロジックをよりよく理解できるように、各コードにコメントを追加しました。コメントを参考にして少しずつコードを理解していただければ幸いです:

JavaScript コード

// Define requestAnimFrame function
window.requestAnimFrame = function () {
    // Check if the browser supports requestAnimFrame function
    return (
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        // If all these options are unavailable, use setTimeout to call the callback function
        function (callback) {
            window.setTimeout(callback)
        }
    )
}

// Initialization function to get canvas element and return related information
function init(elemid) {
    // Get canvas element
    let canvas = document.getElementById(elemid)
    // Get 2d drawing context, note that 'd' is lowercase
    c = canvas.getContext('2d')
    // Set canvas width to window inner width and height to window inner height
    w = (canvas.width = window.innerWidth)
    h = (canvas.height = window.innerHeight)
    // Set fill style to semi-transparent black
    c.fillStyle = "rgba(30,30,30,1)"
    // Fill the entire canvas with the fill style
    c.fillRect(0, 0, w, h)
    // Return drawing context and canvas element
    return { c: c, canvas: canvas }
}

// Execute function when page is fully loaded
window.onload = function () {
    // Get drawing context and canvas element
    let c = init("canvas").c,
        canvas = init("canvas").canvas,
        // Set canvas width to window inner width and height to window inner height
        w = (canvas.width = window.innerWidth),
        h = (canvas.height = window.innerHeight),
        // Initialize mouse object
        mouse = { x: false, y: false },
        last_mouse = {}

    // Function to calculate distance between two points
    function dist(p1x, p1y, p2x, p2y) {
        return Math.sqrt(Math.pow(p2x - p1x, 2) + Math.pow(p2y - p1y, 2))
    }

    // Define segment class
    class segment {
        // Constructor to initialize segment object
        constructor(parent, l, a, first) {
            // If it's the first tentacle segment, position is the tentacle top position
            // Otherwise, position is the nextPos coordinates of the previous segment object
            this.first = first
            if (first) {
                this.pos = {
                    x: parent.x,
                    y: parent.y,
                }
            } else {
                this.pos = {
                    x: parent.nextPos.x,
                    y: parent.nextPos.y,
                }
            }
            // Set segment length and angle
            this.l = l
            this.ang = a
            // Calculate coordinates for the next segment
            this.nextPos = {
                x: this.pos.x + this.l * Math.cos(this.ang),
                y: this.pos.y + this.l * Math.sin(this.ang),
            }
        }
        // Method to update segment position
        update(t) {
            // Calculate angle between segment and target point
            this.ang = Math.atan2(t.y - this.pos.y, t.x - this.pos.x)
            // Update position coordinates based on target point and angle
            this.pos.x = t.x + this.l * Math.cos(this.ang - Math.PI)
            this.pos.y = t.y + this.l * Math.sin(this.ang - Math.PI)
            // Update nextPos coordinates based on new position coordinates
            this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang)
            this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang)
        }
        // Method to return segment to initial position
        fallback(t) {
            // Set position coordinates to target point coordinates
            this.pos.x = t.x
            this.pos.y = t.y
            this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang)
            this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang)
        }
        show() {
            c.lineTo(this.nextPos.x, this.nextPos.y)
        }
    }

    // Define tentacle class
    class tentacle {
        // Constructor to initialize tentacle object
        constructor(x, y, l, n, a) {
            // Set tentacle top position coordinates
            this.x = x
            this.y = y
            // Set tentacle length
            this.l = l
            // Set number of tentacle segments
            this.n = n
            // Initialize tentacle target point object
            this.t = {}
            // Set random movement parameter for tentacle
            this.rand = Math.random()
            // Create first segment of the tentacle
            this.segments = [new segment(this, this.l / this.n, 0, true)]
            // Create other segments
            for (let i = 1; i < this.n; i++) {
                this.segments.push(
                    new segment(this.segments[i - 1], this.l / this.n, 0, false)
                )
            }
        }
        // Method to move tentacle to target point
        move(last_target, target) {
            // Calculate angle between tentacle top and target point
            this.angle = Math.atan2(target.y - this.y, target.x - this.x)
            // Calculate tentacle distance parameter
            this.dt = dist(last_target.x, last_target.y, target.x, target.y)
            // Calculate tentacle target point coordinates
            this.t = {
                x: target.x - 0.8 * this.dt * Math.cos(this.angle),
                y: target.y - 0.8 * this.dt * Math.sin(this.angle)
            }
            // If target point is calculated, update position coordinates of last segment object
            // Otherwise, update position coordinates of last segment object to target point coordinates
            if (this.t.x) {
                this.segments[this.n - 1].update(this.t)
            } else {
                this.segments[this.n - 1].update(target)
            }
            // Iterate through all segment objects, update their position coordinates
            for (let i = this.n - 2; i >= 0; i--) {
                this.segments[i].update(this.segments[i + 1].pos)
            }
            if (
                dist(this.x, this.y, target.x, target.y) <=
                this.l + dist(last_target.x, last_target.y, target.x, target.y)
            ) {
                this.segments[0].fallback({ x: this.x, y: this.y })
                for (let i = 1; i < this.n; i++) {
                    this.segments[i].fallback(this.segments[i - 1].nextPos)
                }
            }
        }
        show(target) {
            // If distance between tentacle and target point is less than tentacle length, draw tentacle
            if (dist(this.x, this.y, target.x, target.y) <= this.l) {
                // Set global composite operation to "lighter"
                c.globalCompositeOperation = "lighter"
                // Begin new path
                c.beginPath()
                // Start drawing line from tentacle starting position
                c.moveTo(this.x, this.y)
                // Iterate through all segment objects and use their show method to draw lines
                for (let i = 0; i < this.n; i++) {
                    this.segments[i].show()
                }
                // Set line style
                c.strokeStyle = "hsl(" + (this.rand * 60 + 180) +
                    ",100%," + (this.rand * 60 + 25) + "%)"
                // Set line width
                c.lineWidth = this.rand * 2
                // Set line cap style
                c.lineCap = "round"
                // Set line join style
                c.lineJoin = "round"
                // Draw line
                c.stroke()
                // Set global composite operation to "source-over"
                c.globalCompositeOperation = "source-over"
            }
        }
        // Method to draw tentacle's circular head
        show2(target) {
            // Begin new path
            c.beginPath()
            // If distance between tentacle and target point is less than tentacle length, draw white circle
            // Otherwise draw cyan circle
            if (dist(this.x, this.y, target.x, target.y) <= this.l) {
                c.arc(this.x, this.y, 2 * this.rand + 1, 0, 2 * Math.PI)
                c.fillStyle = "white"
            } else {
                c.arc(this.x, this.y, this.rand * 2, 0, 2 * Math.PI)
                c.fillStyle = "darkcyan"
            }
            // Fill circle
            c.fill()
        }
    }
    // Initialize variables
    let maxl = 400, // Maximum tentacle length
        minl = 50, // Minimum tentacle length
        n = 30, // Number of tentacle segments
        numt = 600, // Number of tentacles
        tent = [], // Array of tentacles
        clicked = false, // Whether mouse is pressed
        target = { x: 0, y: 0 }, // Tentacle target point
        last_target = {}, // Previous tentacle target point
        t = 0, // Current time
        q = 10; // Step length for each tentacle movement

    // Create tentacle objects
    for (let i = 0; i < numt; i++) {
        tent.push(
            new tentacle(
                Math.random() * w, // Tentacle x-coordinate
                Math.random() * h, // Tentacle y-coordinate
                Math.random() * (maxl - minl) + minl, // Tentacle length
                n, // Number of tentacle segments
                Math.random() * 2 * Math.PI, // Tentacle angle
            )
        )
    }
    // Method to draw image
    function draw() {
        // If mouse moves, calculate deviation between tentacle target point and current point
        if (mouse.x) {
            target.errx = mouse.x - target.x
            target.erry = mouse.y - target.y
        } else {
            // Otherwise, calculate x-coordinate of tentacle target point
            target.errx =
                w / 2 +
                ((h / 2 - q) * Math.sqrt(2) * Math.cos(t)) /
                (Math.pow(Math.sin(t), 2) + 1) -
                target.x;
            target.erry =
                h / 2 +
                ((h / 2 - q) * Math.sqrt(2) * Math.cos(t) * Math.sin(t)) /
                (Math.pow(Math.sin(t), 2) + 1) -
                target.y;
        }

        // Update tentacle target point coordinates
        target.x += target.errx / 10
        target.y += target.erry / 10

        // Update time
        t += 0.01;

        // Draw tentacle target point
        c.beginPath();
        c.arc(
            target.x,
            target.y,
            dist(last_target.x, last_target.y, target.x, target.y) + 5,
            0,
            2 * Math.PI
        );
        c.fillStyle = "hsl(210,100%,80%)"
        c.fill();

        // Draw center points of all tentacles
        for (i = 0; i < numt; i++) {
            tent[i].move(last_target, target)
            tent[i].show2(target)
        }
        // Draw all tentacles
        for (i = 0; i < numt; i++) {
            tent[i].show(target)
        }
        // Update previous tentacle target point coordinates
        last_target.x = target.x
        last_target.y = target.y
    }
    // Function to loop animation drawing
    function loop() {
        // Use requestAnimFrame function to loop
        window.requestAnimFrame(loop)

        // Clear canvas
        c.clearRect(0, 0, w, h)

        // Draw animation
        draw()
    }

    // Listen for window resize event
    window.addEventListener("resize", function () {
        // Reset canvas size
        w = canvas.width = window.innerWidth
        h = canvas.height = window.innerHeight

        // Loop animation drawing function
        loop()
    })

    // Loop animation drawing function
    loop()
    // Use setInterval function to loop
    setInterval(loop, 1000 / 60)

    // Listen for mouse move event
    canvas.addEventListener("mousemove", function (e) {
        // Record previous mouse position
        last_mouse.x = mouse.x
        last_mouse.y = mouse.y

        // Update current mouse position
        mouse.x = e.pageX - this.offsetLeft
        mouse.y = e.pageY - this.offsetTop
    }, false)

    // Listen for mouse leave event
    canvas.addEventListener("mouseleave", function (e) {
        // Set mouse to false
        mouse.x = false
        mouse.y = false
    })
}

ここで上記のコードの処理を大まかに整理します。

初期化フェーズ

  • initFunction: ページがロードされると、関数 init が呼び出されてキャンバス要素を取得し、その幅と高さをウィンドウのサイズに設定します。取得した 2D 描画コンテキストは、以降の描画に使用されます。
  • window.onload: ページがロードされた後、キャンバスを初期化し、コンテキストをマウスの初期状態に設定します。

触手オブジェクトの定義

  • segmentClass: これは触手のセグメントです。各セグメントには開始点 (pos)、長さ (l)、角度 (ang) があり、次のセグメントの位置は角度 (nextPos) によって計算されます。
  • tentacleClass: 複数のセグメントで構成される完全な触手を表します。触手の開始点は画面の中央にあり、各触手には複数のセグメントが含まれています。

触手の主な方法は次のとおりです:
move: マウスの位置に応じて各セグメントの位置を更新します。
show: 触手のパスを描画します。

イベント監視

  • Canvas.addEventListener('mousemove', ...): マウスが移動すると、マウスの位置がキャプチャされ、マウス変数に保存されます。マウスを動かすたびに、後続のアニメーションのマウスと last_mouse の座標が更新されます。

アニメーションループ

drawFunction : これはアニメーション効果を作成するために使用される再帰関数です。

  • まず、各フレームの半透明の背景でキャンバスを塗りつぶし、以前に描画されたコンテンツが徐々に消えて、スミア効果が作成されます。
  • 次に、すべての触手を反復処理し、移動メソッドと表示メソッドを呼び出し、位置を更新して各フレームを描画します。
  • 最後に、requestAnimFrame(draw) を使用して連続再帰描画呼び出しを行い、アニメーション ループを形成します。

触手の動作

  • 触手ムーブの動きは関数で実装されています。触手の最後のセグメントが最初にその位置を更新し、その後、他のセグメントが順番に続きます。
  • 触手の描画は show 関数を通じて行われ、すべてのセグメントを反復して線を描画し、最終的に画面に表示します。

これで電子スパイダーの製作が完了しました!!!

最後に、最終的な効果を見てみましょう:

Frontend Refresh Project - An Electronic Spider

以上がフロントエンド リフレッシュ プロジェクト - 電子スパイダーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。