<style>
#animationCanvas {
background-color: #0f0f1a;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
max-width: 90vw;
max-height: 70vh;
touch-action: none;
}
</style>
<canvas id="animationCanvas" class="w-full rounded-lg"></canvas>
<script>
(function() {
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let frame = 0;
// --- Responsiveness ---
function resizeCanvas() {
canvas.width = window.innerWidth * 0.9;
canvas.height = window.innerHeight * 0.6;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// --- Drawing functions ---
function drawSigns() {
ctx.font = '24px Inter';
ctx.textAlign = 'center';
const signSpeed = 5;
const sign1Text = "The focus is person the only stationary object";
const sign2Text = "Why does the person seem to be moving when he is locked in place?";
const signSpacing = 700;
const textPadding = 20;
// Function to draw a cloud shape
function drawCloud(x, y, text) {
const textMetrics = ctx.measureText(text);
const textWidth = textMetrics.width;
const cloudWidth = textWidth + textPadding * 2;
const cloudHeight = 60;
const radius = cloudHeight / 2;
ctx.beginPath();
ctx.moveTo(x - cloudWidth / 2 + radius, y);
// Left arcs
ctx.arc(x - cloudWidth / 2 + radius, y + radius, radius, Math.PI, Math.PI * 1.5);
ctx.arc(x - cloudWidth / 2 + radius * 2.5, y + radius, radius * 1.5, Math.PI, Math.PI * 1.5);
// Top line
ctx.lineTo(x + cloudWidth / 2 - radius * 1.5, y);
// Right arcs
ctx.arc(x + cloudWidth / 2 - radius * 1.5, y + radius, radius * 1.5, Math.PI * 1.5, Math.PI * 2);
ctx.arc(x + cloudWidth / 2 - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2);
// Bottom line
ctx.lineTo(x + cloudWidth / 2, y + cloudHeight);
ctx.lineTo(x - cloudWidth / 2, y + cloudHeight);
ctx.closePath();
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.fill();
ctx.strokeStyle = '#E2E8F0';
ctx.lineWidth = 2;
ctx.stroke();
// Draw the text inside the cloud
ctx.fillStyle = '#E2E8F0';
ctx.fillText(text, x, y + radius + 8);
}
let sign1X = canvas.width - ((frame * signSpeed) % (canvas.width + 700));
let sign2X = canvas.width + signSpacing - ((frame * signSpeed) % (canvas.width + signSpacing + 700));
// Draw sign 1
drawCloud(sign1X, canvas.height * 0.2, sign1Text);
// Draw sign 2
drawCloud(sign2X, canvas.height * 0.4, sign2Text);
}
function drawWavyRoad() {
ctx.beginPath();
ctx.moveTo(0, canvas.height * 0.8);
const amplitude = canvas.height * 0.05;
const frequency = 0.005;
const yOffset = canvas.height * 0.8;
// Draw the road as a scrolling sine wave
for (let x = 0; x <= canvas.width; x++) {
const y = yOffset + amplitude * Math.sin((x + frame * 5) * frequency);
ctx.lineTo(x, y);
}
ctx.strokeStyle = '#4A90E2';
ctx.lineWidth = 4;
ctx.stroke();
ctx.closePath();
}
function getRoadY(x) {
// Calculate the y-coordinate of the road at a given x-position
const amplitude = canvas.height * 0.05;
const frequency = 0.005;
const yOffset = canvas.height * 0.8;
return yOffset + amplitude * Math.sin((x + frame * 5) * frequency);
}
function drawWheel(x, y) {
// Main circle
ctx.beginPath();
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.fillStyle = '#f56565';
ctx.fill();
ctx.strokeStyle = '#e53e3e';
ctx.lineWidth = 3;
ctx.stroke();
// Spokes
ctx.strokeStyle = '#e53e3e';
ctx.lineWidth = 2;
ctx.beginPath();
const spokeAngle = (frame * 0.1) % (Math.PI * 2);
for(let i = 0; i < 6; i++) {
ctx.moveTo(x, y);
const angle = (Math.PI * 2 / 6) * i + spokeAngle;
ctx.lineTo(x + 20 * Math.cos(angle), y + 20 * Math.sin(angle));
}
ctx.stroke();
}
function drawPerson(x, y) {
// A more detailed stick figure for a better graphic
ctx.strokeStyle = '#ECC94B';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
// Head
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.stroke();
// Body
ctx.beginPath();
ctx.moveTo(x, y + 10);
ctx.lineTo(x, y + 40);
ctx.stroke();
// Arms
ctx.beginPath();
ctx.moveTo(x, y + 15);
ctx.lineTo(x - 15, y + 25);
ctx.moveTo(x, y + 15);
ctx.lineTo(x + 15, y + 25);
ctx.stroke();
// Legs
ctx.beginPath();
ctx.moveTo(x, y + 40);
ctx.lineTo(x - 10, y + 55);
ctx.moveTo(x, y + 40);
ctx.lineTo(x + 10, y + 55);
ctx.stroke();
// Hand
ctx.beginPath();
ctx.arc(x - 15, y + 25, 3, 0, Math.PI * 2);
ctx.fillStyle = '#ECC94B';
ctx.fill();
}
function drawPoles(personX, personY, wheelY) {
// Outer pole (stationary, wider hole)
const poleWidth = 10;
const poleHeight = 80;
const outerPoleX = personX + 15; // Offset from the person's hand
const outerPoleY = personY + 15; // Aligned with the person's hand
ctx.fillStyle = '#63B3ED';
ctx.fillRect(outerPoleX - poleWidth / 2, outerPoleY, poleWidth, poleHeight);
// Inner pole (moves)
const innerPoleX = outerPoleX;
const innerPoleY = wheelY;
const innerPoleHeight = outerPoleY - innerPoleY + 10;
ctx.fillStyle = '#2C5282';
ctx.fillRect(innerPoleX - 2, innerPoleY, 4, innerPoleHeight);
}
// --- Animation loop ---
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawSigns();
drawWavyRoad();
// Fixed positions for the stationary elements
const personX = canvas.width * 0.4;
const personY = canvas.height * 0.4;
const wheelX = personX + 15; // Wheel's x-position is fixed and aligned with the pole
// Calculate wheel's y-position based on the road at its fixed x-position
const roadY = getRoadY(wheelX);
const wheelY = roadY - 20;
drawPoles(personX, personY, wheelY);
drawPerson(personX, personY);
drawWheel(wheelX, wheelY);
frame++;
requestAnimationFrame(animate);
}
// Start animation
animate();
})();
</script>