社区!
我正在使用 HTML5 Canvas 和 JavaScript/jQuery 开发一个项目,其中网格上的点之间有多个动画路线。动画应该平滑地从 A 点到 B 点绘制线条,但我很难实现线条的平滑移动。
这是我正在使用的代码:
$(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 突然“绘制”的。我想要的是平滑地绘制这条线,就好像它随着时间的推移而被追踪,而不是一下子全部出现。
问题: 如何平滑该动画以使线条显得更加流畅?任何有关改进插值计算或如何绘制线条的提示将不胜感激!
提前感谢您的帮助!
我为你的代码做了片段。好像有效果?
$(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>