크리스마스 트리: 두 판 사이의 차이

(새 문서: <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Vibrant Math Tree</title> <style> @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&display=swap'); body { margin: 0; overflow: hidden; background-color: #020205; font-family: 'Fira Code', monospace...)
 
잔글편집 요약 없음
태그: 수동 되돌리기
 
(사용자 2명의 중간 판 6개는 보이지 않습니다)
1번째 줄: 1번째 줄:
<!DOCTYPE html>
[http://creeper0809.synology.me/]
<html lang="ko">
'''http로 접속할 것'''
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Vibrant Math Tree</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&display=swap');
        body {
            margin: 0;
            overflow: hidden;
            background-color: #020205;
            font-family: 'Fira Code', monospace;
            cursor: move;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
 
    const _0x5a1 = [70, 76, 65, 71, 123, 84, 119, 105, 110, 107, 108, 105, 110, 103, 95, 76, 105, 103, 104, 116, 115, 95, 65, 110, 100, 95, 66, 114, 105, 103, 104, 116, 95, 79, 114, 110, 97, 109, 101, 110, 116, 115, 125];
    const hiddenPathChars = _0x5a1.map(c => String.fromCharCode(c));
 
    let width, height;
    let particles = [];
    let snowParticles = [];
   
    let angleY = 0;
    let targetAngleY = 0;
    let isDragging = false;
    let lastMouseX = 0;
    let cameraZ = 550;
 
    let mathDecorations = [
        "x", "y", "z", "i", "e", "π", "ω", "t", "s", "f", "g",
        "sin", "cos", "tan", "log", "ln", "lim", "det", "max", "min",
        "f(t)", "F(ω)", "L(s)", "dy/dx", "x²+y²", "e^x", "sin(x)",
        "ℱ{f}", "ℒ{f}", "∫e⁻ˢᵗ", "∫e⁻ⁱωᵗ",
        "E=mc²", "F=ma", "V=IR", "A=πr²", "∇∙F", "∇×F",
        "e^(iπ)+1=0", "sin²θ+cos²θ=1", "d/dx(e^x)=e^x",
        "x=(-b±√D)/2a", "PV=nRT", "S=klogW"
    ];
    mathDecorations.sort((a, b) => a.length - b.length);
 
    const treeGreens = ["#00FF00", "#32CD32", "#00FA9A", "#98FB98", "#7CFC00"];
    const ornamentColors = ["#FF0000", "#FFD700", "#FF00FF", "#00FFFF"];
    const lightColors = ["#FF5555", "#55FF55", "#5555FF", "#FFFF55", "#FF55FF", "#55FFFF"];
    const snowSymbols = ["+", "-", "×", "÷", "=", "≠"];
 
    class Particle {
        constructor(x, y, z, text, color, type = 'leaf') {
            this.x = x;
            this.y = y;
            this.z = z;
            this.text = text;
            this.color = color;
            this.type = type;
           
            if (this.type === 'star') this.baseSize = 60;
            else if (this.type === 'light') this.baseSize = 24;
            else if (this.type === 'ornament') this.baseSize = 20;
            else if (this.type === 'trunk') this.baseSize = 14;
            else this.baseSize = 14;
 
            this.projX = 0;
            this.projY = 0;
            this.projScale = 0;
            this.visible = false;
            this.depth = 0;
            this.alpha = Math.random() * 0.5 + 0.5;
            this.blinkPhase = Math.random() * Math.PI * 2;
           
            if (this.type === 'light') {
                this.lightColor = lightColors[Math.floor(Math.random() * lightColors.length)];
            }
        }
        project(cx, cy, angle) {
            const cos = Math.cos(angle);
            const sin = Math.sin(angle);
            const rx = this.x * cos - this.z * sin;
            const rz = this.x * sin + this.z * cos;
            const focalLength = 450;
            const scale = focalLength / (focalLength + rz + cameraZ);
            this.projScale = scale;
            this.projX = rx * scale + cx;
            this.projY = this.y * scale + cy;
            this.visible = (scale > 0 && rz > -cameraZ);
            this.depth = rz;
        }
        draw(ctx) {
            if (!this.visible) return;
           
            let alpha = this.alpha;
            if (this.type === 'trunk') alpha = Math.max(alpha, 0.8);
           
            let drawAlpha = Math.max(0.1, Math.min(1, alpha * this.projScale));
           
            if(this.type === 'star' || this.type === 'light' || this.type === 'ornament') {
                drawAlpha = 1;
            }
 
            ctx.globalAlpha = drawAlpha;
            const fontSize = this.baseSize * this.projScale;
            ctx.font = `bold ${fontSize}px 'Fira Code', monospace`;
 
            if (this.type === 'star') {
                ctx.shadowBlur = 40 + Math.abs(Math.sin(Date.now() * 0.002)) * 20;
                ctx.shadowColor = "#FFD700";
                ctx.fillStyle = "#FFFF00";
            }
            else if (this.type === 'light') {
                const time = Date.now() * 0.005 + this.blinkPhase;
                let twinkle = Math.pow(Math.sin(time) * 0.5 + 0.5, 4);
               
                ctx.globalAlpha = 0.4 + twinkle * 0.6;
                ctx.shadowBlur = 10 + twinkle * 25;
                ctx.shadowColor = this.lightColor;
                ctx.fillStyle = "#FFFFFF";
            }
            else if (this.type === 'ornament') {
                ctx.shadowBlur = 15;
                ctx.shadowColor = this.color;
                ctx.fillStyle = this.color;
            }
            else if (this.projScale > 0.6) {
                ctx.shadowBlur = 5;
                ctx.shadowColor = this.color;
                ctx.fillStyle = this.color;
            } else {
                ctx.shadowBlur = 0;
                ctx.fillStyle = this.color;
            }
 
            ctx.fillText(this.text, this.projX, this.projY);
            ctx.globalAlpha = 1;
            ctx.shadowBlur = 0;
        }
    }
 
    class SnowParticle {
        constructor() {
            this.reset();
            this.y = Math.random() * height - height / 2;
        }
        reset() {
            this.x = (Math.random() - 0.5) * width * 1.5;
            this.y = -height / 2 - Math.random() * 200;
            this.z = (Math.random() - 0.5) * 1000;
            this.text = snowSymbols[Math.floor(Math.random() * snowSymbols.length)];
            this.color = "#FFFFFF";
            this.speed = Math.random() * 2 + 1;
            this.size = Math.random() * 10 + 8;
            this.drift = Math.random() * 0.5 - 0.25;
        }
        update() {
            this.y += this.speed;
            this.x += this.drift;
            if (this.y > height / 2 + 100) {
                this.reset();
            }
        }
        project(cx, cy, angle) {
            const cos = Math.cos(angle * 0.1);
            const sin = Math.sin(angle * 0.1);
            const rx = this.x * cos - this.z * sin;
            const rz = this.x * sin + this.z * cos;
            const focalLength = 450;
            const scale = focalLength / (focalLength + rz + cameraZ);
            this.projScale = scale;
            this.projX = rx * scale + cx;
            this.projY = this.y * scale + cy;
            this.visible = (scale > 0 && rz > -cameraZ);
        }
        draw(ctx) {
            if (!this.visible) return;
            ctx.globalAlpha = Math.min(1, this.projScale * 0.7);
            ctx.font = `${this.size * this.projScale}px 'Fira Code', monospace`;
            ctx.fillStyle = this.color;
            if (this.projScale > 0.7) {
                ctx.shadowBlur = 5;
                ctx.shadowColor = "#FFFFFF";
            }
            ctx.fillText(this.text, this.projX, this.projY);
            ctx.globalAlpha = 1;
            ctx.shadowBlur = 0;
        }
    }
 
    function createTree() {
        particles = [];
        const treeHeight = 650;
        const maxRadius = 280;
        const leafCount = 600;
        const phi = Math.PI * (3 - Math.sqrt(5));
 
        for (let i = 0; i < leafCount; i++) {
            const y = - (treeHeight / 2) + (i / leafCount) * treeHeight - 80;
            const radius = (i / leafCount) * maxRadius;
            const theta = phi * i;
            const x = Math.cos(theta) * radius;
            const z = Math.sin(theta) * radius;
 
            const progress = i / leafCount;
            const centerIndex = progress * (mathDecorations.length - 1);
            const noise = (Math.random() - 0.5) * 8;
            let index = Math.floor(centerIndex + noise);
            index = Math.max(0, Math.min(mathDecorations.length - 1, index));
            const text = mathDecorations[index];
           
            let color, type;
            if (Math.random() < 0.15) {
                color = ornamentColors[Math.floor(Math.random() * ornamentColors.length)];
                type = 'ornament';
            } else {
                color = treeGreens[Math.floor(Math.random() * treeGreens.length)];
                type = 'leaf';
            }
            particles.push(new Particle(x, y, z, text, color, type));
        }
 
        const flagLen = hiddenPathChars.length;
        for (let i = 0; i < flagLen; i++) {
            const y = - (treeHeight / 2) + (i / flagLen) * (treeHeight * 0.8) - 60;
            const radius = ((i + 2) / (flagLen + 5)) * maxRadius * 0.95;
            const theta = (i * 1.5) + 0;
            const x = Math.cos(theta) * radius;
            const z = Math.sin(theta) * radius;
            particles.push(new Particle(x, y, z, hiddenPathChars[i], "#FFFFFF", 'light'));
        }
 
        const trunkH = 160;
        const trunkW = 70;
        const trunkCount = 120;
        const trunkStartY = (treeHeight / 2) - 60;
 
        for(let i=0; i<trunkCount; i++) {
            const h = Math.random() * trunkH;
            const angle = Math.random() * Math.PI * 2;
            const r = trunkW;
            const x = Math.cos(angle) * r;
            const z = Math.sin(angle) * r;
            const y = trunkStartY + h;
            const text = Math.random() > 0.5 ? "1" : "0";
            particles.push(new Particle(x, y, z, text, "#D2691E", 'trunk'));
        }
       
        particles.push(new Particle(0, -treeHeight/2 - 120, 0, "★", "#FFFF00", 'star'));
    }
 
    function createSnow() {
        snowParticles = [];
        const snowCount = 200;
        for (let i = 0; i < snowCount; i++) {
            snowParticles.push(new SnowParticle());
        }
    }
 
    function resize() {
        width = canvas.width = window.innerWidth;
        height = canvas.height = window.innerHeight;
    }
 
    function animate() {
        ctx.fillStyle = "#020205";
        ctx.fillRect(0, 0, width, height);
        if (!isDragging) targetAngleY += 0.002;
        angleY += (targetAngleY - angleY) * 0.1;
 
        snowParticles.forEach(p => {
            p.update();
            p.project(width / 2, height / 2, angleY);
            p.draw(ctx);
        });
 
        particles.forEach(p => p.project(width / 2, height / 2, angleY));
        particles.sort((a, b) => b.depth - a.depth);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        particles.forEach(p => p.draw(ctx));
       
        requestAnimationFrame(animate);
    }
 
    window.addEventListener('resize', resize);
    window.addEventListener('wheel', (e) => {
        cameraZ += e.deltaY * 0.5;
        cameraZ = Math.max(200, Math.min(1000, cameraZ));
    });
 
    const onStart = (x) => { isDragging = true; lastMouseX = x; };
    const onMove = (x) => {
        if (!isDragging) return;
        const delta = (x - lastMouseX) * 0.005;
        targetAngleY += delta;
        angleY += delta;
        lastMouseX = x;
    };
    const onEnd = () => { isDragging = false; };
 
    canvas.addEventListener('mousedown', e => onStart(e.clientX));
    window.addEventListener('mousemove', e => onMove(e.clientX));
    window.addEventListener('mouseup', onEnd);
    canvas.addEventListener('touchstart', e => onStart(e.touches[0].clientX));
    window.addEventListener('touchmove', e => onMove(e.touches[0].clientX));
    window.addEventListener('touchend', onEnd);
 
    resize();
    createTree();
    createSnow();
    animate();
</script>
</body>
</html>

2025년 12월 23일 (화) 08:57 기준 최신판

[1] http로 접속할 것