Created
January 1, 2025 16:18
-
-
Save Teemu/91903b5e538734443d9d75ccf0fc3c62 to your computer and use it in GitHub Desktop.
Snowman DMC demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>#dmc Dancing Snowman by Teemu</title> | |
<style> | |
html, | |
body { | |
margin: 0; | |
padding: 0; | |
background: #000; | |
overflow: hidden; | |
height: 100%; | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="demoCanvas" width="360" height="640"></canvas> | |
<script> | |
// Grab canvas and context | |
const canvas = document.getElementById("demoCanvas"); | |
const ctx = canvas.getContext("2d"); | |
const W = canvas.width; | |
const H = canvas.height; | |
// Adjust these for more/less swirling effect | |
const swirlFrequency = 0.002; | |
const swirlAmplitude = 80; | |
// Snowflake parameters | |
const maxSnowflakes = 100; | |
const snowflakes = []; | |
for (let i = 0; i < maxSnowflakes; i++) { | |
snowflakes.push({ | |
x: Math.random() * W, | |
y: Math.random() * (H + 200) - 100, | |
r: 2 + Math.random() * 5, | |
speed: 1 + Math.random() * 1.5, | |
angle: Math.random() * 360, // For rotation | |
rotationSpeed: (Math.random() - 0.5) * 2, // Rotation speed in radians per frame | |
}); | |
} | |
// Animate arms with a sine wave | |
let frameCount = 0; | |
/** | |
* Draw the swirling background | |
*/ | |
function drawSwirlingBackground(time) { | |
const swirlFrequency = 0.002; | |
const swirlAmplitude = 80; | |
const gradient = ctx.createRadialGradient( | |
W / 2, | |
H / 2, | |
100, | |
W / 2 + swirlAmplitude * Math.sin(time * swirlFrequency), | |
H / 2 + swirlAmplitude * Math.cos(time * swirlFrequency), | |
Math.max(W, H), | |
); | |
gradient.addColorStop(0, "skyblue"); | |
gradient.addColorStop(1, "dodgerblue"); | |
ctx.fillStyle = gradient; | |
ctx.fillRect(0, 0, W, H); | |
} | |
function drawElements() { | |
// Example: Simple pine trees | |
const treeCount = 5; | |
for (let i = 0; i < treeCount; i++) { | |
const treeX = 90 + i * 40; | |
const treeY = H - 170; | |
drawTree(treeX, treeY, 30); | |
} | |
// Smaller trees | |
} | |
function drawTree(x, y, size) { | |
ctx.fillStyle = "#006400"; // Dark green | |
ctx.beginPath(); | |
ctx.moveTo(x, y - size * 3); | |
ctx.lineTo(x - size, y); | |
ctx.lineTo(x + size, y); | |
ctx.closePath(); | |
ctx.fill(); | |
ctx.beginPath(); | |
ctx.moveTo(x, y - size * 2); | |
ctx.lineTo(x - size * 0.8, y + size * 0.2); | |
ctx.lineTo(x + size * 0.8, y + size * 0.2); | |
ctx.closePath(); | |
ctx.fill(); | |
ctx.beginPath(); | |
ctx.moveTo(x, y - size); | |
ctx.lineTo(x - size * 0.6, y + size * 0.8); | |
ctx.lineTo(x + size * 0.6, y + size * 0.8); | |
ctx.closePath(); | |
ctx.fill(); | |
} | |
/** | |
* Draw falling snow with more complexity | |
*/ | |
function drawSnow(frameCount) { | |
for (let i = 0; i < snowflakes.length; i++) { | |
const s = snowflakes[i]; | |
ctx.save(); | |
ctx.globalAlpha = s.opacity; | |
ctx.translate(s.x, s.y); | |
ctx.rotate((s.angle * Math.PI) / 180); | |
// Draw a more complex snowflake (e.g., a six-branch snowflake) | |
ctx.strokeStyle = "white"; | |
ctx.lineWidth = 4; | |
for (let j = 0; j < 6; j++) { | |
ctx.beginPath(); | |
ctx.moveTo(0, 0); | |
ctx.lineTo(0, -s.r * 2); | |
ctx.stroke(); | |
ctx.rotate(Math.PI / 3); | |
} | |
ctx.restore(); | |
// Update snowflake position and rotation | |
s.y += s.speed; | |
s.angle += s.rotationSpeed; | |
if (s.y > H + 100) { | |
s.y = -100; | |
s.x = Math.random() * W; | |
} | |
} | |
} | |
/** | |
* Draw dancing snowman | |
*/ | |
function drawSnowman(x, y, size, time) { | |
const danceX = size > 30 ? 3 * 10 * Math.sin(time * 0.1) : 0; | |
const danceY = size > 30 ? 3 * 5 * Math.cos(time * 0.2) : 0; | |
ctx.save(); | |
ctx.translate(danceX, danceY); | |
const bottomRadius = size; | |
const middleRadius = size * 0.7; | |
const headRadius = size * 0.5; | |
const bottomY = y; | |
const middleY = y - bottomRadius * 1.4; | |
const headY = middleY - middleRadius * 1.4; | |
// Body circles | |
ctx.beginPath(); | |
ctx.arc(x, bottomY, bottomRadius, 0, 2 * Math.PI); | |
ctx.fillStyle = "white"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
ctx.fill(); | |
ctx.beginPath(); | |
ctx.arc(x, middleY, middleRadius, 0, 2 * Math.PI); | |
ctx.fillStyle = "white"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
ctx.fill(); | |
ctx.beginPath(); | |
ctx.arc(x, headY, headRadius, 0, 2 * Math.PI); | |
ctx.fillStyle = "white"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
ctx.fill(); | |
// Eyes | |
ctx.fillStyle = "black"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
const eyeOffsetX = size * 0.2; | |
const eyeOffsetY = size * -0.1; | |
ctx.beginPath(); | |
ctx.arc( | |
x - eyeOffsetX, | |
headY + eyeOffsetY, | |
size * 0.05, | |
0, | |
2 * Math.PI, | |
); | |
ctx.arc( | |
x + eyeOffsetX, | |
headY + eyeOffsetY, | |
size * 0.05, | |
0, | |
2 * Math.PI, | |
); | |
ctx.fill(); | |
// Carrot nose | |
ctx.beginPath(); | |
ctx.moveTo(x, headY); | |
ctx.lineTo(x + size * 0.3, headY + size * 0.1); | |
ctx.lineTo(x, headY + size * 0.1); | |
ctx.fillStyle = "orange"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
ctx.fill(); | |
// Mouth | |
ctx.beginPath(); | |
ctx.arc(x, headY + size * 0.15, size * 0.2, 0, Math.PI, false); | |
ctx.strokeStyle = "black"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
ctx.lineWidth = size * 0.02; | |
ctx.stroke(); | |
// Arms | |
ctx.strokeStyle = "white"; | |
if (size < 30) | |
ctx.strokeStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
ctx.lineWidth = size * 0.2; | |
ctx.lineCap = "round"; | |
const leftArmAngle = -Math.PI / 4 + 0.6 * Math.sin(time * 0.1) + 4; | |
const leftArmLength = middleRadius * 1.5; | |
ctx.beginPath(); | |
ctx.moveTo(x - middleRadius * 0.8, middleY); | |
ctx.lineTo( | |
x - middleRadius * 0.8 + leftArmLength * Math.cos(leftArmAngle), | |
middleY + leftArmLength * Math.sin(leftArmAngle), | |
); | |
ctx.stroke(); | |
// Right arm | |
const rightArmAngle = | |
(-3 * Math.PI) / 4 + 0.6 * Math.sin(time * 0.1) - 4; | |
const rightArmLength = middleRadius * 1.5; | |
ctx.beginPath(); | |
ctx.moveTo(x + middleRadius * 0.8, middleY); | |
ctx.lineTo( | |
x + middleRadius * 0.8 + rightArmLength * Math.cos(rightArmAngle), | |
middleY + rightArmLength * Math.sin(rightArmAngle), | |
); | |
ctx.stroke(); | |
// Hat | |
const hatWidth = size * 0.8; | |
const hatHeight = size * 0.3; | |
const hatBrimWidth = size; | |
const hatBrimHeight = size * 0.05; | |
const hatY = headY - headRadius - hatHeight; | |
ctx.fillStyle = "black"; | |
if (size < 30) ctx.fillStyle = `hsl(${(time * 0.02) % 360}, 90%, 40%)`; | |
// Brim | |
ctx.fillRect( | |
x - hatBrimWidth / 2, | |
hatY + hatHeight, | |
hatBrimWidth, | |
hatBrimHeight, | |
); | |
// Top hat | |
ctx.fillRect(x - hatWidth / 2, hatY, hatWidth, hatHeight); | |
ctx.restore(); | |
} | |
function drawSnowflakes() { | |
ctx.save(); | |
ctx.fillStyle = "white"; | |
for (let i = 0; i < snowflakes.length; i++) { | |
const s = snowflakes[i]; | |
ctx.globalAlpha = s.opacity; | |
ctx.beginPath(); | |
ctx.arc(s.x, s.y, s.r, 0, 2 * Math.PI); | |
ctx.fill(); | |
s.y += s.speed; | |
if (s.y > H) { | |
s.y = -s.r; | |
s.x = Math.random() * W; | |
} | |
} | |
ctx.restore(); | |
} | |
function drawGround() { | |
ctx.fillStyle = "white"; | |
ctx.beginPath(); | |
for (let i = 0; i < W + 20; i += 5) { | |
const heightVariation = Math.sin(i * 0.05 + frameCount * 0.02) * 10; | |
ctx.lineTo(i, H - 170 - heightVariation); | |
} | |
ctx.lineTo(W, H); | |
ctx.lineTo(0, H); | |
ctx.closePath(); | |
ctx.fill(); | |
} | |
function update() { | |
frameCount++; | |
drawSwirlingBackground(frameCount); | |
drawElements(); | |
drawSnow(frameCount); | |
drawGround(); | |
drawSnowman(W / 2, H * 0.7, 50, frameCount); | |
requestAnimationFrame(update); | |
} | |
// Start | |
update(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment