← back to Model Arena
data/artifacts/02a362bd0b06/kimi.html
354 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dragon Realm</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
}
canvas {
display: block;
cursor: crosshair;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let width, height;
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
const rand = (min, max) => Math.random() * (max - min) + min;
class SnowParticle {
constructor() {
this.reset();
this.y = rand(0, height);
}
reset() {
this.x = rand(0, width);
this.y = -10;
this.size = rand(1, 3.5);
this.speed = rand(1, 4);
this.drift = rand(-0.5, 0.5);
this.opacity = rand(0.4, 0.9);
}
update() {
this.y += this.speed;
this.x += this.drift + Math.sin(this.y * 0.01) * 0.5;
if (this.y > height) this.reset();
}
draw() {
ctx.fillStyle = `rgba(255,255,255,${this.opacity})`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
ctx.fill();
}
}
class FireParticle {
constructor(x, y, angle, intensity) {
this.x = x;
this.y = y;
const speed = rand(3, 8) * intensity;
const spread = rand(-0.4, 0.4);
this.vx = Math.cos(angle + spread) * speed;
this.vy = Math.sin(angle + spread) * speed;
this.life = 1;
this.decay = rand(0.015, 0.035);
this.size = rand(8, 20);
this.grow = 0.3;
this.type = Math.random() > 0.7 ? 'smoke' : 'fire';
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.08;
this.size += this.grow;
this.life -= this.decay;
if (this.type === 'smoke') {
this.vx *= 0.98;
this.vy *= 0.98;
}
}
draw() {
if (this.type === 'fire') {
const r = 255;
const g = Math.floor(255 * this.life);
const b = Math.floor(100 * this.life * this.life);
const a = this.life;
ctx.fillStyle = `rgba(${r},${g},${b},${a})`;
} else {
const c = Math.floor(100 * (1-this.life) + 50);
ctx.fillStyle = `rgba(${c},${c},${c},${this.life * 0.3})`;
}
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
ctx.fill();
}
}
class MountainLayer {
constructor(color, speed, amplitude, yOffset, complexity) {
this.color = color;
this.speed = speed;
this.offset = rand(0, 10000);
this.amplitude = amplitude;
this.yOffset = yOffset;
this.complexity = complexity;
}
getHeight(x) {
let y = 0;
for(let i=0; i<this.complexity.length; i++) {
const freq = this.complexity[i][0];
const amp = this.complexity[i][1];
y += Math.sin((x + this.offset) * freq) * amp;
}
return height - this.yOffset + y;
}
update() {
this.offset += this.speed;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(0, height);
for(let x=0; x<=width; x+=5) {
const y = this.getHeight(x);
ctx.lineTo(x, y);
}
ctx.lineTo(width, height);
ctx.closePath();
ctx.fill();
}
}
class Dragon {
constructor() {
this.x = width * 0.5;
this.y = height * 0.4;
this.angle = 0;
this.wingPhase = 0;
this.breathingFire = false;
this.fireTimer = 0;
this.targetX = width * 0.5;
this.targetY = height * 0.4;
}
update(time) {
const t = time * 0.0008;
this.targetX = width * 0.5 + Math.sin(t) * width * 0.25;
this.targetY = height * 0.35 + Math.sin(t * 1.3) * height * 0.12;
this.x += (this.targetX - this.x) * 0.02;
this.y += (this.targetY - this.y) * 0.02;
const dx = this.targetX - this.x;
const dy = this.targetY - this.y;
this.angle = Math.atan2(dy, dx) * 0.5;
this.wingPhase += 0.12;
if (this.breathingFire) {
this.fireTimer--;
if (this.fireTimer <= 0) this.breathingFire = false;
const mouthX = this.x + Math.cos(this.angle) * 60;
const mouthY = this.y + Math.sin(this.angle) * 10;
for(let i=0; i<4; i++) {
fireParticles.push(new FireParticle(mouthX, mouthY, this.angle, 1.5));
}
}
}
draw() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
const wingY = Math.sin(this.wingPhase) * 30;
const wingScale = 0.8 + Math.sin(this.wingPhase) * 0.2;
ctx.fillStyle = '#080808';
if (this.breathingFire) {
ctx.shadowColor = '#ff4500';
ctx.shadowBlur = 20;
} else {
ctx.shadowBlur = 0;
}
ctx.beginPath();
ctx.moveTo(-40, 0);
ctx.quadraticCurveTo(-60, wingY * wingScale, -80, -40 + wingY);
ctx.quadraticCurveTo(-50, -20 + wingY * 0.5, -20, -10);
ctx.quadraticCurveTo(-50, 20 - wingY * 0.5, -80, 40 + wingY);
ctx.quadraticCurveTo(-60, wingY * wingScale, -40, 5);
ctx.fill();
ctx.beginPath();
ctx.ellipse(0, 0, 45, 18, 0, 0, Math.PI*2);
ctx.fill();
ctx.beginPath();
ctx.moveTo(30, -8);
ctx.lineTo(55, -5);
ctx.lineTo(60, 0);
ctx.lineTo(55, 5);
ctx.lineTo(30, 8);
if (this.breathingFire) {
ctx.lineTo(50, 0);
} else {
ctx.lineTo(45, 0);
}
ctx.closePath();
ctx.fill();
ctx.fillStyle = this.breathingFire ? '#ff6600' : '#330000';
ctx.beginPath();
ctx.arc(45, -3, 3, 0, Math.PI*2);
ctx.fill();
ctx.beginPath();
ctx.moveTo(-40, -5);
for(let i=0; i<5; i++) {
const sx = -30 - i*12;
const sy = -8 - Math.abs(Math.sin(i + this.wingPhase*0.5)) * 8;
ctx.lineTo(sx, sy);
ctx.lineTo(sx-6, -5);
}
ctx.lineTo(-90, 0);
ctx.strokeStyle = '#080808';
ctx.lineWidth = 3;
ctx.stroke();
ctx.restore();
ctx.shadowBlur = 0;
}
breatheFire() {
this.breathingFire = true;
this.fireTimer = 90;
}
}
const snow = Array(200).fill().map(() => new SnowParticle());
const mountains = [
new MountainLayer('#0d1b2a', 0.1, 80, 150, [[0.003, 40], [0.01, 20], [0.03, 5]]),
new MountainLayer('#1b263b', 0.3, 60, 100, [[0.004, 30], [0.015, 15], [0.04, 4]]),
new MountainLayer('#415a77', 0.8, 40, 50, [[0.005, 25], [0.02, 10], [0.05, 3]])
];
const dragon = new Dragon();
let fireParticles = [];
canvas.addEventListener('mousedown', () => dragon.breatheFire());
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
dragon.breatheFire();
});
function drawMoon() {
const mx = width * 0.85;
const my = height * 0.15;
const glow = ctx.createRadialGradient(mx, my, 0, mx, my, 100);
glow.addColorStop(0, 'rgba(200,220,255,0.3)');
glow.addColorStop(0.5, 'rgba(200,220,255,0.1)');
glow.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = glow;
ctx.fillRect(mx-100, my-100, 200, 200);
ctx.fillStyle = '#e0e0e0';
ctx.beginPath();
ctx.arc(mx, my, 30, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = '#c0c0c0';
ctx.beginPath();
ctx.arc(mx-8, my-5, 5, 0, Math.PI*2);
ctx.arc(mx+10, my+8, 7, 0, Math.PI*2);
ctx.arc(mx-5, my+12, 4, 0, Math.PI*2);
ctx.fill();
}
function animate(time) {
const grad = ctx.createLinearGradient(0, 0, 0, height);
grad.addColorStop(0, '#050510');
grad.addColorStop(0.4, '#1a1a3e');
grad.addColorStop(0.8, '#2d1b4e');
grad.addColorStop(1, '#0f3460');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = 'white';
for(let i=0; i<60; i++) {
const x = (i * 137.5) % width;
const y = (i * 71.3) % (height * 0.6);
const twinkle = Math.sin(time * 0.001 + i) * 0.5 + 0.5;
ctx.globalAlpha = 0.2 + twinkle * 0.6;
ctx.fillRect(x, y, (i%2)+1, (i%2)+1);
}
ctx.globalAlpha = 1;
drawMoon();
mountains.forEach(m => {
m.update();
m.draw();
});
dragon.update(time);
if (dragon.breathingFire) {
const mx = dragon.x + Math.cos(dragon.angle) * 60;
const my = dragon.y + Math.sin(dragon.angle) * 10;
ctx.save();
ctx.globalCompositeOperation = 'overlay';
const fireLight = ctx.createRadialGradient(mx, my, 0, mx, my, 400);
fireLight.addColorStop(0, 'rgba(255,100,50,0.6)');
fireLight.addColorStop(0.3, 'rgba(255,50,0,0.2)');
fireLight.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = fireLight;
ctx.fillRect(0, 0, width, height);
ctx.restore();
}
dragon.draw();
ctx.globalCompositeOperation = 'lighter';
for(let i=fireParticles.length-1; i>=0; i--) {
const p = fireParticles[i];
p.update();
p.draw();
if(p.life <= 0) fireParticles.splice(i, 1);
}
ctx.globalCompositeOperation = 'source-over';
snow.forEach(s => {
s.update();
s.draw();
});
requestAnimationFrame(animate);
}
animate(0);
</script>
</body>
</html>