> 웹 프론트엔드 > JS 튜토리얼 > 프론트엔드 새로 고침 프로젝트 - 전자 스파이더

프론트엔드 새로 고침 프로젝트 - 전자 스파이더

DDD
풀어 주다: 2024-09-19 03:20:02
원래의
530명이 탐색했습니다.

서문: JavaScript를 학습한 후 JavaScript를 사용하여 몇 가지 흥미로운 효과를 얻을 수 있습니다. 이 문서에서는 웹페이지에 전자 스파이더를 구현하기 위해 JavaScript를 사용하는 방법을 설명합니다.

웹 스파이더 작성 방법을 배우기 전에 전자 스파이더가 어떻게 생겼는지 살펴보겠습니다.

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 코드 작성을 시작하기 전에 계획을 세우십시오.

전반적인 과정

  • 페이지가 로드되면 캔버스 요소와 그리기 컨텍스트가 초기화됩니다.

  • 촉수 개체를 정의합니다. 각 촉수는 여러 부분으로 구성됩니다.

  • 마우스 움직임 이벤트를 듣고 실시간으로 마우스 위치를 업데이트합니다.

  • 애니메이션 루프를 통해 촉수가 그려지며, 마우스의 위치에 따라 동적으로 변화하여 부드러운 애니메이션 효과를 만들어냅니다.

일반적인 프로세스는 위 단계이지만, 이 코드 작성을 완료하기 전에는 위 프로세스를 이해하지 못할 수도 있다고 생각하지만 어쨌든 문제가 되지 않으므로 웹 스파이더 작성을 시작하겠습니다.

서문: 코드의 논리를 더 잘 이해할 수 있도록 각 코드에 주석을 추가했습니다. 댓글의 도움으로 코드를 조금씩 이해하실 수 있기를 바랍니다:

자바스크립트 코드

// 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: 마우스 위치에 따라 각 세그먼트의 위치를 ​​업데이트합니다.
보여주기: 촉수의 경로를 그립니다.

이벤트 모니터링

  • canvas.addEventListener('mousemove', ...): 마우스가 움직일 때 마우스 위치가 캡처되어 마우스 변수에 저장됩니다. 마우스를 움직일 때마다 후속 애니메이션에 대한 마우스 및 last_mouse의 좌표가 업데이트됩니다.

애니메이션 루프

drawFunction : 애니메이션 효과를 만드는 데 사용되는 재귀 함수입니다.

  • 먼저 프레임마다 반투명한 배경으로 캔버스를 채워 이전에 그린 내용이 점차 사라지면서 스미어 효과를 만들어냅니다.
  • 그런 다음 모든 촉수를 반복하여 이동 및 표시 메소드를 호출하고 위치를 업데이트하고 각 프레임을 그립니다.
  • 마지막으로 requestAnimFrame(draw)을 사용하여 지속적인 재귀 그리기 호출을 만들어 애니메이션 루프를 형성합니다.

촉수 행동

  • 촉수무브의 움직임은 함수를 통해 구현됩니다. 촉수의 마지막 부분이 먼저 위치를 업데이트하고 다른 부분이 순서대로 업데이트됩니다.
  • 촉수 그리기는 모든 세그먼트를 반복하며 선을 그려 최종적으로 화면에 표시하는 show 기능을 통해 이루어집니다.

이렇게 전자거미 제작이 완료되었습니다!!!

마지막으로 최종 효과를 살펴보겠습니다.

Frontend Refresh Project - An Electronic Spider

위 내용은 프론트엔드 새로 고침 프로젝트 - 전자 스파이더의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿