亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

使用JavaScript實(shí)現(xiàn)隨機(jī)曲線之間進(jìn)行平滑切換

 更新時(shí)間:2024年11月01日 09:01:29   作者:xvch  
今天,我運(yùn)用拉格朗日插值法繪制了一條曲線,然而,我并未止步于靜態(tài)展示,而是引入了一個(gè)定時(shí)器,每隔一段時(shí)間便對(duì)曲線上的點(diǎn)進(jìn)行動(dòng)態(tài)更新,從而賦予曲線生命般的動(dòng)態(tài)變化,本文介紹了使用JavaScript實(shí)現(xiàn)隨機(jī)曲線之間進(jìn)行平滑切換,感興趣的朋友可以參考下

介紹

今天,我運(yùn)用拉格朗日插值法繪制了一條曲線。然而,我并未止步于靜態(tài)展示,而是引入了一個(gè)定時(shí)器,每隔一段時(shí)間便對(duì)曲線上的點(diǎn)進(jìn)行動(dòng)態(tài)更新,從而賦予曲線生命般的動(dòng)態(tài)變化。

然而,在刷新過(guò)程中,我敏銳地察覺(jué)到曲線之間的切換顯得過(guò)于突兀,缺乏流暢感(請(qǐng)見(jiàn)下圖)。于是,一個(gè)大膽的想法在我腦海中閃現(xiàn):何不嘗試構(gòu)造一個(gè)曲線過(guò)渡算法,以實(shí)現(xiàn)曲線切換時(shí)的平滑過(guò)渡?這不僅將提升視覺(jué)效果,更將為動(dòng)態(tài)曲線的展示增添一抹細(xì)膩與和諧。

在具體實(shí)現(xiàn)之前,我們先了解下拉格朗日插值法。

拉格朗日插值法

拉格朗日插值法是一種用于在給定數(shù)據(jù)點(diǎn)之間進(jìn)行多項(xiàng)式插值的方法。

該方法可以找到一個(gè)多項(xiàng)式,該多項(xiàng)式恰好穿過(guò)二維平面上若干個(gè)給定數(shù)據(jù)點(diǎn)。

拉格朗日插值多項(xiàng)式

拉格朗日插值多項(xiàng)式的代碼實(shí)現(xiàn)

function lagrange(x, points) {
    const n = points.length;
    const result = [];

    for (let i = 0; i < n; i++) {
        let tmp = points[i].y;
        for (let j = 0; j < n; j++) {
            if (j !== i) {
                tmp *= (x - points[j].x) / (points[i].x - points[j].x);
            }
        }
        result.push(tmp);
    }

    return result.reduce((sum, cur) => sum + cur, 0);
}

實(shí)現(xiàn)曲線突兀切換

我們首先完整實(shí)現(xiàn)一下開(kāi)頭介紹部分圖片所展示的效果代碼:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
        }

        canvas {
            border-radius: 15px;
            background-color: #ffffff;
        }
    </style>
</head>

<body>
    <canvas id="demo-canvas" width="800" height="600"></canvas>

    <script>
        const canvas = document.getElementById('demo-canvas');
        const ctx = canvas.getContext('2d');

        let points = [];

        function drawLine(x1, y1, x2, y2, color) {
            ctx.beginPath();
            ctx.moveTo(x1, y1);
            ctx.lineTo(x2, y2);
            ctx.strokeStyle = color;
            ctx.stroke();
        }

        function lagrange(x, points) {
            const n = points.length;
            const result = [];

            for (let i = 0; i < n; i++) {
                let tmp = points[i].y;
                for (let j = 0; j < n; j++) {
                    if (j !== i) {
                        tmp *= (x - points[j].x) / (points[i].x - points[j].x);
                    }
                }
                result.push(tmp);
            }

            return result.reduce((sum, cur) => sum + cur, 0);
        }

        function fillPoints() {
            const randomNumber = (min, max) => {
                const randomBuffer = new Uint32Array(1);
                window.crypto.getRandomValues(randomBuffer);
                const number = randomBuffer[0] / (0xffffffff + 1);
                return number * (max - min + 1) + min;
            }

            points = [];

            const count = 7;

            for (let i = 0; i < count; i++) {
                points.push({
                    x: (i + 1) * 100,
                    y: randomNumber(200, 400)
                });
            }
        }

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

            fillPoints();

            const step = 1;

            for (let x = points[0].x; x < points[points.length - 1].x; x += step) {
                drawLine(x, lagrange(x, points), x + step, lagrange(x + step, points), 'red');
            }

            setTimeout(draw, 1000);
        }

        draw();
    </script>
</body>

</html>

實(shí)現(xiàn)曲線平滑切換

簡(jiǎn)單構(gòu)思一下,解決方案其實(shí)非常簡(jiǎn)單:只需保存當(dāng)前曲線與下一條曲線,然后在每個(gè)橫坐標(biāo) x 值上,兩條曲線分別具有兩個(gè)縱坐標(biāo) y 值,通過(guò)利用這兩個(gè) y 值,我們可以構(gòu)建一條 111 階貝塞爾曲線進(jìn)行插值,其他位置上的點(diǎn)重復(fù)同樣的步驟,在相同的時(shí)間內(nèi)完成插值即可實(shí)現(xiàn)曲線的平滑切換。

原理圖如下:

開(kāi)始行動(dòng),我們首先構(gòu)造 111 階貝塞爾曲線:

B(t)=(1−t)P  0 ?  +tP  1 ?  0≤t≤1

其中 P0為當(dāng)前曲線的縱坐標(biāo),P1為下一條曲線的縱坐標(biāo),ttt 為插值系數(shù)。

function bezier(t, y0, y1) {
    return (1 - t) * y0 + t * y1;
}

然后,我們構(gòu)造用于保存下一條曲線控制點(diǎn)的數(shù)組 nextPoints

let nextPoints = [];

對(duì)應(yīng)的填充曲線控制點(diǎn)的函數(shù) fillPoints 也需要做相應(yīng)調(diào)整:

function fillPoints() {
    const randomNumber = (min, max) => {
        const randomBuffer = new Uint32Array(1);
        window.crypto.getRandomValues(randomBuffer);
        const number = randomBuffer[0] / (0xffffffff + 1);
        return number * (max - min + 1) + min;
    }

    const count = 7;

    if (points.length === 0 && nextPoints.length === 0) {
        for (let i = 0; i < count; i++) {
            points.push({
                x: (i + 1) * 100,
                y: randomNumber(200, 400)
            });
            nextPoints.push({
                x: (i + 1) * 100,
                y: randomNumber(200, 400)
            });
        }
    }
    else {
        points = [];
        points = nextPoints;
        nextPoints = [];

        for (let i = 0; i < count; i++) {
            nextPoints.push({
                x: (i + 1) * 100,
                y: randomNumber(200, 400)
            });
        }
    }
}

fillPoints 函數(shù)在第一次運(yùn)行時(shí)填充兩條曲線控制點(diǎn),之后每次運(yùn)行時(shí),先將 nextPoints 中的數(shù)據(jù)復(fù)制到 points 中,最后填充下一條曲線控制點(diǎn)到 nextPoints 中。

然后,我們構(gòu)造用于平滑切換的動(dòng)畫函數(shù) animate

let t = 0;

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

    const step = 1;

    for (let x = points[0].x; x < points[points.length - 1].x; x += step) {
        const y = bezier(t, lagrange(x, points), lagrange(x, nextPoints));
        const y_step = bezier(t, lagrange(x + step, points), lagrange(x + step, nextPoints));

        drawLine(x, y, x + step, y_step, 'red');
    }

    t += 0.05;

    if (t < 1) {
        requestAnimationFrame(animate);
    }
}

animate 函數(shù)在每次調(diào)用中的第一次運(yùn)行時(shí)需要保證 t 值為 0,然后通過(guò)調(diào)用 requestAnimationFrame(animate) 函數(shù)反復(fù)執(zhí)行 animate 函數(shù)完成動(dòng)畫繪制,直到 t 值達(dá)到 1 時(shí),動(dòng)畫結(jié)束。

最后,我們對(duì)繪制函數(shù) draw 做相應(yīng)調(diào)整:

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

    fillPoints();

    const step = 1;
    t = 0;

    for (let x = points[0].x; x < points[points.length - 1].x; x += step) {
        drawLine(x, lagrange(x, points), x + step, lagrange(x + step, points), 'red');
    }

    animate();

    setTimeout(draw, 1000);
}

保證繪制完當(dāng)前的曲線后,立即調(diào)用 animate 函數(shù)完成平滑切換,最后通過(guò) setTimeout 函數(shù)定時(shí)反復(fù)調(diào)用 draw 函數(shù)完成動(dòng)畫循環(huán)。

完整代碼

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
        }

        canvas {
            border-radius: 15px;
            background-color: #ffffff;
        }
    </style>
</head>

<body>
    <canvas id="demo-canvas" width="800" height="600"></canvas>

    <script>
        const canvas = document.getElementById('demo-canvas');
        const ctx = canvas.getContext('2d');

        let points = [], nextPoints = [];

        function drawLine(x1, y1, x2, y2, color) {
            ctx.beginPath();
            ctx.moveTo(x1, y1);
            ctx.lineTo(x2, y2);
            ctx.strokeStyle = color;
            ctx.stroke();
        }

        function lagrange(x, points) {
            const n = points.length;
            const result = [];

            for (let i = 0; i < n; i++) {
                let tmp = points[i].y;
                for (let j = 0; j < n; j++) {
                    if (j !== i) {
                        tmp *= (x - points[j].x) / (points[i].x - points[j].x);
                    }
                }
                result.push(tmp);
            }

            return result.reduce((sum, cur) => sum + cur, 0);
        }

        function bezier(t, y0, y1) {
            return (1 - t) * y0 + t * y1;
        }

        function fillPoints() {
            const randomNumber = (min, max) => {
                const randomBuffer = new Uint32Array(1);
                window.crypto.getRandomValues(randomBuffer);
                const number = randomBuffer[0] / (0xffffffff + 1);
                return number * (max - min + 1) + min;
            }

            const count = 7;

            if (points.length === 0 && nextPoints.length === 0) {
                for (let i = 0; i < count; i++) {
                    points.push({
                        x: (i + 1) * 100,
                        y: randomNumber(200, 400)
                    });
                    nextPoints.push({
                        x: (i + 1) * 100,
                        y: randomNumber(200, 400)
                    });
                }
            }
            else {
                points = [];
                points = nextPoints;
                nextPoints = [];

                for (let i = 0; i < count; i++) {
                    nextPoints.push({
                        x: (i + 1) * 100,
                        y: randomNumber(200, 400)
                    });
                }
            }
        }

        let t = 0;

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

            const step = 1;

            for (let x = points[0].x; x < points[points.length - 1].x; x += step) {
                const y = bezier(t, lagrange(x, points), lagrange(x, nextPoints));
                const y_step = bezier(t, lagrange(x + step, points), lagrange(x + step, nextPoints));

                drawLine(x, y, x + step, y_step, 'red');
            }

            t += 0.05;

            if (t < 1) {
                requestAnimationFrame(animate);
            }
        }

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

            fillPoints();

            const step = 1;
            t = 0;

            for (let x = points[0].x; x < points[points.length - 1].x; x += step) {
                drawLine(x, lagrange(x, points), x + step, lagrange(x + step, points), 'red');
            }

            animate();

            setTimeout(draw, 1000);
        }

        draw();
    </script>
</body>

</html>

展示

以上就是使用JavaScript實(shí)現(xiàn)隨機(jī)曲線之間進(jìn)行平滑切換的詳細(xì)內(nèi)容,更多關(guān)于JavaScript曲線平滑切換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論