コードエディター 本文
<canvas id="bikeCanvas" width="320" height="100"></canvas>
カスタムJavaScript
const canvas = document.getElementById('bikeCanvas');
const ctx = canvas.getContext('2d');
let x = -60; // 自転車のX座標
const speed = 2; // 自転車の速度
let angle = 0; // タイヤとペダルの回転角度
let wobble = 0; // 揺れの効果のためのカウンター
// 雲のデータ(x, y, スピード)
const clouds = [
{ x: 50, y: 20, s: 0.5 },
{ x: 180, y: 35, s: 0.3 },
{ x: 300, y: 15, s: 0.4 }
];
function drawCloud(cx, cy) {
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(cx, cy, 10, 0, Math.PI * 2);
ctx.arc(cx + 10, cy - 5, 12, 0, Math.PI * 2);
ctx.arc(cx + 20, cy, 10, 0, Math.PI * 2);
ctx.fill();
}
function drawWheel(wx, wy, radius, rotation) {
ctx.save();
ctx.translate(wx, wy);
ctx.rotate(rotation);
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.PI * 2);
ctx.stroke();
for(let i = 0; i < 4; i++) {
ctx.beginPath();
ctx.moveTo(0, -radius);
ctx.lineTo(0, radius);
ctx.rotate(Math.PI / 4);
ctx.stroke();
}
ctx.restore();
}
// ペダルを描画する関数
function drawPedal(px, py, rotation) {
const pedalRadius = 5;
const crankLength = 10;
ctx.save();
ctx.translate(px, py); // ペダル軸へ移動
ctx.rotate(rotation); // 回転
// クランク
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(crankLength, 0);
ctx.lineWidth = 2;
ctx.stroke();
// ペダル本体
ctx.beginPath();
ctx.rect(crankLength - 3, -2, 6, 4); // 短い長方形でペダルを表現
ctx.fill();
ctx.stroke();
ctx.restore();
}
// 棒人間を描画する関数
function drawStickFigure(sx, sy) {
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
// 頭
ctx.beginPath();
ctx.arc(sx + 20, sy - 35, 6, 0, Math.PI * 2);
ctx.stroke();
// 体
ctx.beginPath();
ctx.moveTo(sx + 20, sy - 29);
ctx.lineTo(sx + 20, sy - 15);
ctx.stroke();
// 腕(ハンドルを握る形)
ctx.beginPath();
ctx.moveTo(sx + 20, sy - 28);
ctx.lineTo(sx + 25, sy - 22); // 肩から肘
ctx.lineTo(sx + 30, sy - 25); // 肘からハンドル
ctx.stroke();
// 足(ペダルを漕ぐ形)
ctx.beginPath();
ctx.moveTo(sx + 20, sy - 15);
ctx.lineTo(sx + 15, sy - 5); // 股関節から膝
ctx.lineTo(sx + 10, sy + 5); // 膝から足
ctx.stroke();
ctx.beginPath(); // もう一方の足
ctx.moveTo(sx + 20, sy - 15);
ctx.lineTo(sx + 25, sy - 5);
ctx.lineTo(sx + 30, sy + 5);
ctx.stroke();
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// --- 1. 雲の描画と更新 ---
clouds.forEach(c => {
drawCloud(c.x, c.y);
c.x -= c.s;
if (c.x < -40) c.x = 360;
});
// --- 2. 地面 ---
ctx.strokeStyle = "#555";
ctx.beginPath();
ctx.moveTo(0, 85); ctx.lineTo(320, 85);
ctx.stroke();
// --- 3. 自転車の描画 ---
ctx.strokeStyle = "#333";
ctx.fillStyle = "#333"; // ペダルの塗りつぶし用
ctx.lineWidth = 2;
// ガタガタ効果
const wobbleOffset = Math.sin(wobble) * 2; // -2から+2の範囲で上下に揺れる
const bikeY = 80 + wobbleOffset;
// タイヤ
drawWheel(x, bikeY, 12, angle); // 後輪
drawWheel(x + 40, bikeY, 12, angle); // 前輪
// ペダル
const pedalX = x + 20; // ペダルの中心X座標
const pedalY = bikeY; // ペダルの中心Y座標
drawPedal(pedalX, pedalY, angle); // クランクとペダルを回転
// フレーム
ctx.beginPath();
ctx.moveTo(x, bikeY);
ctx.lineTo(x + 15, bikeY - 15);
ctx.lineTo(x + 35, bikeY - 15);
ctx.lineTo(x + 20, bikeY);
ctx.lineTo(x, bikeY);
ctx.moveTo(x + 15, bikeY - 15); ctx.lineTo(x + 10, bikeY - 22);
ctx.moveTo(x + 5, bikeY - 22); ctx.lineTo(x + 15, bikeY - 22);
ctx.moveTo(x + 35, bikeY - 15); ctx.lineTo(x + 40, bikeY);
ctx.moveTo(x + 35, bikeY - 15); ctx.lineTo(x + 38, bikeY - 25);
ctx.moveTo(x + 33, bikeY - 25); ctx.lineTo(x + 43, bikeY - 25);
ctx.stroke();
// 棒人間
drawStickFigure(x, bikeY);
// --- 4. 座標の更新 ---
x += speed;
angle += speed * 0.1; // 進む距離に合わせてタイヤとペダルを回転
wobble += 0.1; // 揺れカウンターを増加
if (x > canvas.width + 40) {
x = -60;
}
requestAnimationFrame(animate);
}
animate();
カスタムCSS
canvas {
background: #87CEEB; /* 空の色 */
border: 2px solid #333;
width: 320px;
height: 100px;
}
