如何平滑HTML5画布中的线条动画?

问题描述 投票:0回答:1

社区!

我正在使用 HTML5 Canvas 和 JavaScript/jQuery 开发一个项目,其中网格上的点之间有多个动画路线。动画应该平滑地从 A 点到 B 点绘制线条,但我很难实现线条的平滑移动。

enter image description here

这是我正在使用的代码:

$(document).ready(function () {
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const points = [];
    const routes = [];
    const pointRadius = 10;
    const gridSize = 60; // Spacing between points
    const numRows = Math.floor(canvas.width / gridSize);
    const numCols = Math.floor(canvas.height / gridSize);
    const animationDuration = 2000; // Total animation time (adjustable)

    // Function to draw a point
    function drawPoint(x, y, color = 'black') {
        ctx.beginPath();
        ctx.arc(x, y, pointRadius, 0, 2 * Math.PI);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.strokeStyle = color;
        ctx.stroke();
    }

    // Function to draw the full curve from A to B
    function drawFullCurve(pointA, pointB, colorStart, colorEnd) {
        const midX = (pointA.x + pointB.x) / 2;
        const midY = (pointA.y + pointB.y) / 2 - 50; // Curve elevation

        const gradient = ctx.createLinearGradient(pointA.x, pointA.y, pointB.x, pointB.y);
        gradient.addColorStop(0, colorStart);
        gradient.addColorStop(1, colorEnd);

        ctx.beginPath();
        ctx.moveTo(pointA.x, pointA.y);
        ctx.quadraticCurveTo(midX, midY, pointB.x, pointB.y);
        ctx.strokeStyle = gradient;
        ctx.lineWidth = 2;
        ctx.stroke();
    }

    // Function to animate the reveal of the curve from A to B
    function animateCurve(pointA, pointB, colorStart, colorEnd, duration) {
        let startTime = performance.now();

        function animateStep(timestamp) {
            const elapsedTime = timestamp - startTime;
            const progress = Math.min(elapsedTime / duration, 1); // Progress from 0 to 1

            // Do not clear the entire canvas! Keep previous routes
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(pointA.x, pointA.y);

            // Calculate intermediate position using linear interpolation
            const t = progress;
            const midX = (pointA.x + pointB.x) / 2;
            const midY = (pointA.y + pointB.y) / 2 - 50;

            const revealX = (1 - t) * ((1 - t) * pointA.x + t * midX) + t * ((1 - t) * midX + t * pointB.x);
            const revealY = (1 - t) * ((1 - t) * pointA.y + t * midY) + t * ((1 - t) * midY + t * pointB.y);

            ctx.quadraticCurveTo(midX, midY, revealX, revealY);
            ctx.clip();

            drawFullCurve(pointA, pointB, colorStart, colorEnd);

            ctx.restore();

            drawPoint(pointA.x, pointA.y, colorStart);
            drawPoint(pointB.x, pointB.y, colorEnd);

            if (progress < 1) {
                requestAnimationFrame(animateStep);
            }
        }

        requestAnimationFrame(animateStep);
    }

    // Generate points
    for (let i = 0; i < numRows; i++) {
        for (let j = 0; j < numCols; j++) {
            const x = i * gridSize + gridSize / 2;
            const y = j * gridSize + gridSize / 2;
            points.push({ x, y });
            drawPoint(x, y);
        }
    }

    // Generate 20 random routes
    function generateRandomRoutes() {
        for (let i = 0; i < 20; i++) {
            const startPoint = points[Math.floor(Math.random() * points.length)];
            const endPoint = points[Math.floor(Math.random() * points.length)];
            routes.push({ startPoint, endPoint });
        }
    }

    generateRandomRoutes();

    // Function to animate routes simultaneously
    function animateRoutes() {
        routes.forEach(route => {
            const colorStart = 'blue';
            const colorEnd = 'red';
            const { startPoint, endPoint } = route;

            // Execute the animation for each route without clearing the canvas
            animateCurve(startPoint, endPoint, colorStart, colorEnd, animationDuration);
        });
    }

    // Execute the animation of the routes
    animateRoutes();
});

问题: 动画并不像我想要的那么流畅。相反,这条线似乎是从 A 到 B 突然“绘制”的。我想要的是平滑地绘制这条线,就好像它随着时间的推移而被追踪,而不是一下子全部出现。

问题: 如何平滑该动画以使线条显得更加流畅?任何有关改进插值计算或如何绘制线条的提示将不胜感激!

提前感谢您的帮助!

javascript canvas html5-canvas
1个回答
0
投票

我为你的代码做了片段。好像有效果?

$(document).ready(function () {
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const points = [];
    const routes = [];
    const pointRadius = 10;
    const gridSize = 60; // Spacing between points
    const numRows = Math.floor(canvas.width / gridSize);
    const numCols = Math.floor(canvas.height / gridSize);
    const animationDuration = 2000; // Total animation time (adjustable)

    // Function to draw a point
    function drawPoint(x, y, color = 'black') {
        ctx.beginPath();
        ctx.arc(x, y, pointRadius, 0, 2 * Math.PI);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.strokeStyle = color;
        ctx.stroke();
    }

    // Function to draw the full curve from A to B
    function drawFullCurve(pointA, pointB, colorStart, colorEnd) {
        const midX = (pointA.x + pointB.x) / 2;
        const midY = (pointA.y + pointB.y) / 2 - 50; // Curve elevation

        const gradient = ctx.createLinearGradient(pointA.x, pointA.y, pointB.x, pointB.y);
        gradient.addColorStop(0, colorStart);
        gradient.addColorStop(1, colorEnd);

        ctx.beginPath();
        ctx.moveTo(pointA.x, pointA.y);
        ctx.quadraticCurveTo(midX, midY, pointB.x, pointB.y);
        ctx.strokeStyle = gradient;
        ctx.lineWidth = 2;
        ctx.stroke();
    }

    // Function to animate the reveal of the curve from A to B
    function animateCurve(pointA, pointB, colorStart, colorEnd, duration) {
        let startTime = performance.now();

        function animateStep(timestamp) {
            const elapsedTime = timestamp - startTime;
            const progress = Math.min(elapsedTime / duration, 1); // Progress from 0 to 1

            // Do not clear the entire canvas! Keep previous routes
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(pointA.x, pointA.y);

            // Calculate intermediate position using linear interpolation
            const t = progress;
            const midX = (pointA.x + pointB.x) / 2;
            const midY = (pointA.y + pointB.y) / 2 - 50;

            const revealX = (1 - t) * ((1 - t) * pointA.x + t * midX) + t * ((1 - t) * midX + t * pointB.x);
            const revealY = (1 - t) * ((1 - t) * pointA.y + t * midY) + t * ((1 - t) * midY + t * pointB.y);

            ctx.quadraticCurveTo(midX, midY, revealX, revealY);
            ctx.clip();

            drawFullCurve(pointA, pointB, colorStart, colorEnd);

            ctx.restore();

            drawPoint(pointA.x, pointA.y, colorStart);
            drawPoint(pointB.x, pointB.y, colorEnd);

            if (progress < 1) {
                requestAnimationFrame(animateStep);
            }
        }

        requestAnimationFrame(animateStep);
    }

    // Generate points
    for (let i = 0; i < numRows; i++) {
        for (let j = 0; j < numCols; j++) {
            const x = i * gridSize + gridSize / 2;
            const y = j * gridSize + gridSize / 2;
            points.push({ x, y });
            drawPoint(x, y);
        }
    }

    // Generate 20 random routes
    function generateRandomRoutes() {
        for (let i = 0; i < 20; i++) {
            const startPoint = points[Math.floor(Math.random() * points.length)];
            const endPoint = points[Math.floor(Math.random() * points.length)];
            routes.push({ startPoint, endPoint });
        }
    }

    generateRandomRoutes();

    // Function to animate routes simultaneously
    function animateRoutes() {
        routes.forEach(route => {
            const colorStart = 'blue';
            const colorEnd = 'red';
            const { startPoint, endPoint } = route;

            // Execute the animation for each route without clearing the canvas
            animateCurve(startPoint, endPoint, colorStart, colorEnd, animationDuration);
        });
    }

    // Execute the animation of the routes
    animateRoutes();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

<canvas id="canvas" width="400" height="200"></canvas>

© www.soinside.com 2019 - 2024. All rights reserved.