← 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>