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

JavaScript實(shí)現(xiàn)一個(gè)電子小蜘蛛

 更新時(shí)間:2024年10月03日 09:19:59   作者:秋刀魚不做夢(mèng)  
這篇文章主要介紹了JavaScript實(shí)現(xiàn)一個(gè)電子小蜘蛛,具體的樣子就是讓它會(huì)跟隨著我們的鼠標(biāo)進(jìn)行移動(dòng),那么我們?nèi)绾螌?shí)現(xiàn)這樣的效果呢,下面來詳細(xì)講解實(shí)現(xiàn)方法,需要的朋友可以參考下

前言

在學(xué)習(xí)完JavaScript之后,我們就可以使用JavaScript來實(shí)現(xiàn)一下好玩的效果了,本篇文章講解的是如何純使用JavaScript來實(shí)現(xiàn)一個(gè)網(wǎng)頁中的電子蜘蛛。

在開始學(xué)習(xí)如何編寫一個(gè)網(wǎng)頁蜘蛛之前,先讓我們看一下這個(gè)電子蜘蛛長(zhǎng)什么樣:

——我們可以看到,其會(huì)跟隨著我們的鼠標(biāo)進(jìn)行移動(dòng),那么我們?nèi)绾螌?shí)現(xiàn)這樣的效果呢?接下來讓我們開始講解。

HTML代碼

我們的html代碼十分的簡(jiǎn)單,就是創(chuàng)建一個(gè)畫布,而我們接下來的操作,都是在此上邊進(jìn)行操作的:

<!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>秋刀魚不做夢(mèng)</title>
    <!-- 引入外部的JavaScript文件 -->
    <script src="./test.js"></script>
    <style>
        /* 移除body的默認(rèn)外邊距和內(nèi)邊距 */
        body {
            margin: 0px;
            padding: 0px;
            position: fixed;
            /* 設(shè)置網(wǎng)頁背景顏色為黑色 */
            background: rgb(0, 0, 0);
        }
    </style>
</head>
<body>
    <!-- 創(chuàng)建一個(gè)畫布用于圖形繪制 -->
    <canvas id="canvas"></canvas>
</body>
</html>

可以看到我們的HTML代碼非常的簡(jiǎn)單,接下來讓我們開始在其上邊進(jìn)行操作!

JavaScript代碼

在開始編寫JavaScript代碼之前,先讓我們理清一下思路:

總體流程

  • 頁面加載時(shí),canvas 元素和繪圖上下文初始化。
  • 定義觸手對(duì)象,每條觸手由多個(gè)段組成。
  • 監(jiān)聽鼠標(biāo)移動(dòng)事件,實(shí)時(shí)更新鼠標(biāo)的位置。
  • 通過動(dòng)畫循環(huán)繪制觸手,觸手根據(jù)鼠標(biāo)的位置動(dòng)態(tài)變化,形成流暢的動(dòng)畫效果。

大致的流程就是上邊的步驟,但是我相信讀者在沒用自己完成此代碼的編寫之前,可能不能理解上邊的流程,不過沒關(guān)系,現(xiàn)在讓我們開始我們的網(wǎng)頁小蜘蛛的編寫:

寫在前面:為了讓讀者可以更好的理解代碼的邏輯,我們給沒一句代碼都加上了注釋,希望讀者可以根據(jù)注釋的幫助一點(diǎn)一點(diǎn)的理解代碼:

JavaScript代碼:

// 定義requestAnimFrame函數(shù)
window.requestAnimFrame = function () {
    // 檢查瀏覽器是否支持requestAnimFrame函數(shù)
    return (
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        // 如果所有這些選項(xiàng)都不可用,使用設(shè)置超時(shí)來調(diào)用回調(diào)函數(shù)
        function (callback) {
            window.setTimeout(callback)
        }
    )
}
// 初始化函數(shù),用于獲取canvas元素并返回相關(guān)信息
function init(elemid) {
    // 獲取canvas元素
    let canvas = document.getElementById(elemid)
    // 獲取2d繪圖上下文,這里d是小寫的
    c = canvas.getContext('2d')
    // 設(shè)置canvas的寬度為窗口內(nèi)寬度,高度為窗口內(nèi)高度
    w = (canvas.width = window.innerWidth)
    h = (canvas.height = window.innerHeight)
    // 設(shè)置填充樣式為半透明黑
    c.fillStyle = "rgba(30,30,30,1)"
    // 使用填充樣式填充整個(gè)canvas
    c.fillRect(0, 0, w, h)
    // 返回繪圖上下文和canvas元素
    return { c: c, canvas: canvas }
}
// 等待頁面加載完成后執(zhí)行函數(shù)
window.onload = function () {
    // 獲取繪圖上下文和canvas元素
    let c = init("canvas").c,
        canvas = init("canvas").canvas,
        // 設(shè)置canvas的寬度為窗口內(nèi)寬度,高度為窗口內(nèi)高度
        w = (canvas.width = window.innerWidth),
        h = (canvas.height = window.innerHeight),
        // 初始化鼠標(biāo)對(duì)象
        mouse = { x: false, y: false },
        last_mouse = {}
    // 定義計(jì)算兩點(diǎn)距離的函數(shù)
    function dist(p1x, p1y, p2x, p2y) {
        return Math.sqrt(Math.pow(p2x - p1x, 2) + Math.pow(p2y - p1y, 2))
    }
    // 定義 segment 類
    class segment {
        // 構(gòu)造函數(shù),用于初始化 segment 對(duì)象
        constructor(parent, l, a, first) {
            // 如果是第一條觸手段,則位置坐標(biāo)為觸手頂部位置
            // 否則位置坐標(biāo)為上一個(gè)segment對(duì)象的nextPos坐標(biāo)
            this.first = first
            if (first) {
                this.pos = {
                    x: parent.x,
                    y: parent.y,
                }
            } else {
                this.pos = {
                    x: parent.nextPos.x,
                    y: parent.nextPos.y,
                }
            }
            // 設(shè)置segment的長(zhǎng)度和角度
            this.l = l
            this.ang = a
            // 計(jì)算下一個(gè)segment的坐標(biāo)位置
            this.nextPos = {
                x: this.pos.x + this.l * Math.cos(this.ang),
                y: this.pos.y + this.l * Math.sin(this.ang),
            }
        }
        // 更新segment位置的方法
        update(t) {
            // 計(jì)算segment與目標(biāo)點(diǎn)的角度
            this.ang = Math.atan2(t.y - this.pos.y, t.x - this.pos.x)
            // 根據(jù)目標(biāo)點(diǎn)和角度更新位置坐標(biāo)
            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)
            // 根據(jù)新的位置坐標(biāo)更新nextPos坐標(biāo)
            this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang)
            this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang)
        }
        // 將 segment 回執(zhí)回初始位置的方法
        fallback(t) {
            // 將位置坐標(biāo)設(shè)置為目標(biāo)點(diǎn)坐標(biāo)
            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)
        }
    }
    // 定義 tentacle 類
    class tentacle {
        // 構(gòu)造函數(shù),用于初始化 tentacle 對(duì)象
        constructor(x, y, l, n, a) {
            // 設(shè)置觸手的頂部位置坐標(biāo)
            this.x = x
            this.y = y
            // 設(shè)置觸手的長(zhǎng)度
            this.l = l
            // 設(shè)置觸手的段數(shù)
            this.n = n
            // 初始化觸手的目標(biāo)點(diǎn)對(duì)象
            this.t = {}
            // 設(shè)置觸手的隨機(jī)移動(dòng)參數(shù)
            this.rand = Math.random()
            // 創(chuàng)建觸手的第一條段
            this.segments = [new segment(this, this.l / this.n, 0, true)]
            // 創(chuàng)建其他的段
            for (let i = 1; i < this.n; i++) {
                this.segments.push(
                    new segment(this.segments[i - 1], this.l / this.n, 0, false)
                )
            }
        }
        // 移動(dòng)觸手到目標(biāo)點(diǎn)的方法
        move(last_target, target) {
            // 計(jì)算觸手頂部與目標(biāo)點(diǎn)的角度
            this.angle = Math.atan2(target.y - this.y, target.x - this.x)
            // 計(jì)算觸手的距離參數(shù)
            this.dt = dist(last_target.x, last_target.y, target.x, target.y)
            // 計(jì)算觸手的目標(biāo)點(diǎn)坐標(biāo)
            this.t = {
                x: target.x - 0.8 * this.dt * Math.cos(this.angle),
                y: target.y - 0.8 * this.dt * Math.sin(this.angle)
            }
            // 如果計(jì)算出了目標(biāo)點(diǎn),則更新最后一個(gè)segment對(duì)象的位置坐標(biāo)
            // 否則,更新最后一個(gè)segment對(duì)象的位置坐標(biāo)為目標(biāo)點(diǎn)坐標(biāo)
            if (this.t.x) {
                this.segments[this.n - 1].update(this.t)
            } else {
                this.segments[this.n - 1].update(target)
            }
            // 遍歷所有segment對(duì)象,更新它們的位置坐標(biāo)
            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) {
            // 如果觸手與目標(biāo)點(diǎn)的距離小于觸手的長(zhǎng)度,則回執(zhí)觸手
            if (dist(this.x, this.y, target.x, target.y) <= this.l) {
                // 設(shè)置全局合成操作為lighter
                c.globalCompositeOperation = "lighter"
                // 開始新路徑
                c.beginPath()
                // 從觸手起始位置開始繪制線條
                c.moveTo(this.x, this.y)
                // 遍歷所有的segment對(duì)象,并使用他們的show方法回執(zhí)線條
                for (let i = 0; i < this.n; i++) {
                    this.segments[i].show()
                }
                // 設(shè)置線條樣式
                c.strokeStyle = "hsl(" + (this.rand * 60 + 180) +
                    ",100%," + (this.rand * 60 + 25) + "%)"
                // 設(shè)置線條寬度
                c.lineWidth = this.rand * 2
                // 設(shè)置線條端點(diǎn)樣式
                c.lineCap = "round"
                // 設(shè)置線條連接處樣式
                c.lineJoin = "round"
                // 繪制線條
                c.stroke()
                // 設(shè)置全局合成操作為“source-over”
                c.globalCompositeOperation = "source-over"
            }
        }
        // 繪制觸手的圓形頭的方法
        show2(target) {
            // 開始新路徑
            c.beginPath()
            // 如果觸手與目標(biāo)點(diǎn)的距離小于觸手的長(zhǎng)度,則回執(zhí)白色的圓形
            // 否則繪制青色的圓形
            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 = "whith"
            } else {
                c.arc(this.x, this.y, this.rand * 2, 0, 2 * Math.PI)
                c.fillStyle = "darkcyan"
            }
            // 填充圓形
            c.fill()
        }
    }
    // 初始化變量
    let maxl = 400,//觸手的最大長(zhǎng)度
        minl = 50,//觸手的最小長(zhǎng)度
        n = 30,//觸手的段數(shù)
        numt = 600,//觸手的數(shù)量
        tent = [],//觸手的數(shù)組
        clicked = false,//鼠標(biāo)是否被按下
        target = { x: 0, y: 0 }, //觸手的目標(biāo)點(diǎn)
        last_target = {},//上一個(gè)觸手的目標(biāo)點(diǎn)
        t = 0,//當(dāng)前時(shí)間
        q = 10;//觸手每次移動(dòng)的步長(zhǎng)
    // 創(chuàng)建觸手對(duì)象
    for (let i = 0; i < numt; i++) {
        tent.push(
            new tentacle(
                Math.random() * w,//觸手的橫坐標(biāo)
                Math.random() * h,//觸手的縱坐標(biāo)
                Math.random() * (maxl - minl) + minl,//觸手的長(zhǎng)度
                n,//觸手的段數(shù)
                Math.random() * 2 * Math.PI,//觸手的角度
            )
        )
    }
    // 繪制圖像的方法
    function draw() {
        // 如果鼠標(biāo)移動(dòng),則計(jì)算觸手的目標(biāo)點(diǎn)與當(dāng)前點(diǎn)的偏差
        if (mouse.x) {
            target.errx = mouse.x - target.x
            target.erry = mouse.y - target.y
        } else {
            // 否則,計(jì)算觸手的目標(biāo)點(diǎn)的橫坐標(biāo)
            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;
        }
        // 更新觸手的目標(biāo)點(diǎn)坐標(biāo)
        target.x += target.errx / 10
        target.y += target.erry / 10
        // 更新時(shí)間
        t += 0.01;
        // 繪制觸手的目標(biāo)點(diǎn)
        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();
        // 繪制所有觸手的中心點(diǎn)
        for (i = 0; i < numt; i++) {
            tent[i].move(last_target, target)
            tent[i].show2(target)
        }
        // 繪制所有觸手
        for (i = 0; i < numt; i++) {
            tent[i].show(target)
        }
        // 更新上一個(gè)觸手的目標(biāo)點(diǎn)坐標(biāo)
        last_target.x = target.x
        last_target.y = target.y
    }
    // 循環(huán)執(zhí)行繪制動(dòng)畫的函數(shù)
    function loop() {
        // 使用requestAnimFrame函數(shù)循環(huán)執(zhí)行
        window.requestAnimFrame(loop)
        // 清空canvas
        c.clearRect(0, 0, w, h)
        // 繪制動(dòng)畫
        draw()
    }
    // 監(jiān)聽窗口大小改變事件
    window.addEventListener("resize", function () {
        // 重置canvas的大小
        w = canvas.width = window.innerWidth
        w = canvas.height = window.innerHeight
        // 循環(huán)執(zhí)行回執(zhí)動(dòng)畫的函數(shù)
        loop()
    })
    // 循環(huán)執(zhí)行回執(zhí)動(dòng)畫的函數(shù)
    loop()
    // 使用setInterval函數(shù)循環(huán)
    setInterval(loop, 1000 / 60)
    // 監(jiān)聽鼠標(biāo)移動(dòng)事件
    canvas.addEventListener("mousemove", function (e) {
        // 記錄上一次的鼠標(biāo)位置
        last_mouse.x = mouse.x
        last_mouse.y = mouse.y
        // 更新點(diǎn)前的鼠標(biāo)位置
        mouse.x = e.pageX - this.offsetLeft
        mouse.y = e.pageY - this.offsetTop
    }, false)
    // 監(jiān)聽鼠標(biāo)離開事件
    canvas.addEventListener("mouseleave", function (e) {
        // 將mouse設(shè)為false
        mouse.x = false
        mouse.y = false
    })
}

這里我們?cè)诖笾碌氖崂硪幌律鲜龃a的流程:

初始化階段

  • init 函數(shù):當(dāng)頁面加載時(shí),init 函數(shù)被調(diào)用,獲取 canvas 元素并設(shè)置其寬高為窗口的大小。獲取到的 2D 繪圖上下文(context)用于后續(xù)繪制。
  • window.onload:頁面加載完成后,初始化 canvascontext,并設(shè)置鼠標(biāo)初始狀態(tài)。

觸手對(duì)象的定義

  • segment 類:這是觸手的一段,每個(gè)段有起始點(diǎn)(pos)、長(zhǎng)度(l)、角度(ang),并通過角度計(jì)算出下一段的位置(nextPos)。
  • tentacle 類:代表完整的觸手,由若干個(gè) segment 組成。觸手的起始點(diǎn)在屏幕中心,并且每個(gè)觸手包含多個(gè)段。tentacle 的主要方法有: move:根據(jù)鼠標(biāo)位置更新每一段的位置。show:繪制觸手的路徑。

事件監(jiān)聽

canvas.addEventListener("mousemove", ...):當(dāng)鼠標(biāo)移動(dòng)時(shí),捕捉鼠標(biāo)的位置并存儲(chǔ)在 mouse 變量中。每次鼠標(biāo)移動(dòng)會(huì)更新 mouselast_mouse 的坐標(biāo),用于后續(xù)的動(dòng)畫。

動(dòng)畫循環(huán)

  • draw 函數(shù):這是一個(gè)遞歸的函數(shù),用于創(chuàng)建動(dòng)畫效果。 首先,它會(huì)在每一幀中為畫布填充半透明背景,使得之前繪制的內(nèi)容逐漸消失,產(chǎn)生拖影效果。
  • 然后,遍歷所有觸手(tentacles),調(diào)用它們的 moveshow 方法,更新位置并繪制每一幀。
  • 最后,使用 requestAnimFrame(draw) 不斷遞歸調(diào)用 draw,形成一個(gè)動(dòng)畫循環(huán)。

觸手的行為

  • 觸手的運(yùn)動(dòng)是通過 move 函數(shù)實(shí)現(xiàn)的,觸手的最后一個(gè)段首先更新位置,然后其他段依次跟隨。
  • 觸手的繪制通過 show 函數(shù),遍歷所有段并繪制線條,最后顯示在屏幕上。

——這樣我們就完成了電子小蜘蛛的制作了?。?!

最后,在讓我們看一下最終效果:

到此這篇關(guān)于JavaScript實(shí)現(xiàn)一個(gè)電子小蜘蛛的文章就介紹到這了,更多相關(guān)JavaScript電子蜘蛛內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JavaScript中的對(duì)象和原型(一)

    JavaScript中的對(duì)象和原型(一)

    大家都知道在js中沒有類的概念,因此它的對(duì)象也與基于類的語言中的對(duì)象有所不同。所以大家要了解面向?qū)ο?,首先要了解js中的對(duì)象和原型,下面本文給大家介紹JavaScript中的對(duì)象和原型(一)知識(shí),一起看下吧
    2016-08-08
  • JavaScript實(shí)現(xiàn)數(shù)組隨機(jī)排序的方法

    JavaScript實(shí)現(xiàn)數(shù)組隨機(jī)排序的方法

    這篇文章主要介紹了JavaScript實(shí)現(xiàn)數(shù)組隨機(jī)排序的方法,涉及javascript數(shù)組遍歷與排序的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • JavaScript實(shí)現(xiàn)經(jīng)典貪吃蛇游戲

    JavaScript實(shí)現(xiàn)經(jīng)典貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)經(jīng)典貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • js將日期格式轉(zhuǎn)換為YYYY-MM-DD HH:MM:SS

    js將日期格式轉(zhuǎn)換為YYYY-MM-DD HH:MM:SS

    這篇文章主要介紹了js將日期格式轉(zhuǎn)換為YYYY-MM-DD HH:MM:SS,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 開啟BootStrap學(xué)習(xí)之旅

    開啟BootStrap學(xué)習(xí)之旅

    當(dāng)下最流行的前端開發(fā)框架Bootstrap,可大大簡(jiǎn)化網(wǎng)站開發(fā)過程,從而深受廣大開發(fā)者的喜歡,你如果也喜歡Bootstrap前端開發(fā)框架,不要錯(cuò)過這次旅行
    2016-05-05
  • 一個(gè)炫酷的Bootstrap導(dǎo)航菜單

    一個(gè)炫酷的Bootstrap導(dǎo)航菜單

    這篇文章主要為大家詳細(xì)介紹了一個(gè)炫酷的Bootstrap導(dǎo)航菜單的制作方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • js 獲取服務(wù)器控件值的代碼

    js 獲取服務(wù)器控件值的代碼

    js 獲取服務(wù)器控件值的代碼
    2010-03-03
  • uniapp微信小程序授權(quán)登錄并獲取手機(jī)號(hào)的方法

    uniapp微信小程序授權(quán)登錄并獲取手機(jī)號(hào)的方法

    這篇文章主要給大家介紹了關(guān)于uniapp微信小程序授權(quán)登錄并獲取手機(jī)號(hào)的相關(guān)資料,我們?cè)趗niapp開發(fā)微信小程序的過程中,經(jīng)常需要在微信端登錄,需要的朋友可以參考下
    2023-06-06
  • 如何寫JS數(shù)組sort的比較函數(shù)

    如何寫JS數(shù)組sort的比較函數(shù)

    我們知道,數(shù)組的sort方法可以對(duì)數(shù)組元素進(jìn)行排序,默認(rèn)是按ASCII字母表順序排序。如果要根據(jù)其他的順序排序就需要為sort方法提供一個(gè)比較函數(shù)作為參數(shù)。這里講的就是如何寫這個(gè)比較函數(shù)。
    2010-07-07
  • 原生JavaScript實(shí)現(xiàn)的簡(jiǎn)單放大鏡效果示例

    原生JavaScript實(shí)現(xiàn)的簡(jiǎn)單放大鏡效果示例

    這篇文章主要介紹了原生JavaScript實(shí)現(xiàn)的簡(jiǎn)單放大鏡效果,涉及javascript事件響應(yīng)及頁面元素屬性動(dòng)態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2018-02-02

最新評(píng)論