纯 Canvas 实现摩天轮动画 —— 完整代码解析
·
一个可直接运行的摩天轮动画页面,使用 Canvas + 原生 JavaScript 实现,无外部依赖。
一、实现方案
-
技术栈:HTML5 Canvas + 原生 JavaScript
-
渲染方式:逐帧动画(
requestAnimationFrame) -
自适应:监听
resize事件,动态调整画布尺寸和元素比例 -
交互反馈:点击/触摸画面可使摩天轮轻微加速
二、核心代码模块
1. 画布初始化与自适应
javascript
const canvas = document.getElementById('ferrisCanvas');
const ctx = canvas.getContext('2d');
function resize() {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
const base = Math.min(W, H) * 0.42;
wheelRadius = Math.min(base, 340);
centerX = W / 2;
centerY = H / 2 + 20;
generateStars();
}
-
画布宽高与视口同步
-
摩天轮半径取视口最小尺寸的 42%,上限 340px
2. 星空背景(动态闪烁)
javascript
function generateStars() {
stars = [];
const count = Math.floor((W * H) / 3600);
for (let i = 0; i < count; i++) {
stars.push({
x: Math.random() * W,
y: Math.random() * H * 0.75,
r: Math.random() * 1.6 + 0.6,
a: Math.random() * 0.7 + 0.3,
speed: 0.004 + Math.random() * 0.008,
phase: Math.random() * Math.PI * 2,
});
}
}
function drawStars(time) {
for (const s of stars) {
const flicker = 0.6 + 0.4 * Math.sin(time * s.speed + s.phase);
const alpha = s.a * flicker;
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 245, 230, ${alpha})`;
ctx.fill();
}
}
-
星星数量随视口面积自动调整
-
每颗星星独立闪烁频率和相位
3. 摩天轮主体结构
3.1 辐条与轿厢位置计算
const count = 12; for (let i = 0; i < count; i++) { const a = (i / count) * Math.PI * 2 + angle; const x = cx + Math.cos(a) * (R - 10); const y = cy + Math.sin(a) * (R - 10); // x, y 即为第 i 个轿厢的位置 }
-
angle为当前旋转角度,每帧递增0.0018(约 18 秒转一圈) -
辐条从中心连接到轿厢位置
3.2 轿厢绘制(始终保持垂直)
javascript
ctx.save(); ctx.translate(x, y); ctx.rotate(0); // 关键:不随轮盘旋转,始终保持竖直 // 绘制轿厢主体(圆角矩形) const w = 22, h = 28, r = 5; ctx.beginPath(); ctx.moveTo(-w/2 + r, -h/2); ctx.lineTo(w/2 - r, -h/2); ctx.quadraticCurveTo(w/2, -h/2, w/2, -h/2 + r); ctx.lineTo(w/2, h/2 - r); ctx.quadraticCurveTo(w/2, h/2, w/2 - r, h/2); ctx.lineTo(-w/2 + r, h/2); ctx.quadraticCurveTo(-w/2, h/2, -w/2, h/2 - r); ctx.lineTo(-w/2, -h/2 + r); ctx.quadraticCurveTo(-w/2, -h/2, -w/2 + r, -h/2); ctx.closePath(); ctx.fill(); ctx.restore();
4. 轿厢呼吸灯
javascript
const blink = 0.5 + 0.5 * Math.sin(time * 0.006 + i * 0.8);
ctx.beginPath();
ctx.arc(0, h/2 + 2, 3, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 200, 150, ${blink * 0.4})`;
ctx.fill();
-
每个轿厢底部有独立相位差的小灯
-
亮度随时间正弦变化,形成呼吸效果
5. 交互反馈
javascript
canvas.addEventListener('click', () => { angle += 0.02; });
canvas.addEventListener('touchstart', () => { angle += 0.02; });
-
点击/触摸屏幕,旋转角度瞬时增加,产生轻微加速感
6. 颜色工具函数
javascript
function lighten(hex, amt) {
let c = hexToRgb(hex);
return `rgb(${Math.min(255, c.r + amt)}, ${Math.min(255, c.g + amt)}, ${Math.min(255, c.b + amt)})`;
}
function darken(hex, amt) {
let c = hexToRgb(hex);
return `rgb(${Math.max(0, c.r - amt)}, ${Math.max(0, c.g - amt)}, ${Math.max(0, c.b - amt)})`;
}
-
支持十六进制颜色的明暗调整,用于轿厢渐变
三、参数速查
| 参数 | 值 | 说明 |
|---|---|---|
| 轿厢数量 | 12 | 可调整 CABIN_COUNT |
| 旋转速度 | 0.0018 rad/frame | 约 18 秒/圈 |
| 轿厢尺寸 | 22×28 px | 可调整宽高 |
| 星星密度 | 每 3600 px² 一颗 | 随视口自动缩放 |
| 加速增量 | 0.02 rad/次 | 点击/触摸触发 |
更多推荐
所有评论(0)