바이브코딩으로 만드는 웹아트 #3 – 이미지에 조명 효과 주기

이미지에 조명 효과를 주는 코딩을 해보았어요.
아래 소스코드를 참고해주세요. 다양한 이미지를 적용해도 좋은 느낌을 줄 수 있을 거 같네요.

< 소스코드>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive Explorer</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html, body {
            height: 100%;
            background: #000;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            cursor: none;
            overflow: hidden;
        }

        #container {
            position: relative;
            width: 100vw;
            height: 100vh;
        }

        #backdrop {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: url('https://cdn.midjourney.com/c6aec82c-5026-41a7-8899-a73da7366064/0_3.png');
            background-size: cover;
            background-position: center;
            filter: grayscale(100%) brightness(0.7);
        }

        #shadow-layer {
            position: absolute;
            inset: 0;
            background: radial-gradient(
                circle var(--size, 150px) at var(--posX, 50%) var(--posY, 50%),
                transparent 0%,
                transparent 40%,
                rgba(0, 0, 0, 0.85) 65%,
                rgba(0, 0, 0, 0.95) 80%,
                black 100%
            );
            pointer-events: none;
            transition: all 0.08s ease-out;
        }

        #glow-effect {
            position: absolute;
            inset: 0;
            background: radial-gradient(
                circle var(--glowSize, 200px) at var(--posX, 50%) var(--posY, 50%),
                rgba(255, 255, 255, 0.1) 0%,
                transparent 50%
            );
            pointer-events: none;
            mix-blend-mode: overlay;
        }

        #pointer {
            position: fixed;
            width: 24px;
            height: 24px;
            pointer-events: none;
            z-index: 1000;
            transform: translate(-50%, -50%);
        }

        #pointer::before {
            content: '';
            position: absolute;
            width: 100%;
            height: 100%;
            border: 2px solid rgba(255, 255, 255, 0.8);
            border-radius: 50%;
            animation: pulse 1.5s infinite;
        }

        #pointer::after {
            content: '';
            position: absolute;
            width: 6px;
            height: 6px;
            background: white;
            border-radius: 50%;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        @keyframes pulse {
            0%, 100% {
                transform: scale(1);
                opacity: 1;
            }
            50% {
                transform: scale(1.5);
                opacity: 0.3;
            }
        }

        #settings {
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(40, 40, 40, 0.8);
            backdrop-filter: blur(10px);
            padding: 15px 20px;
            border-radius: 12px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            z-index: 100;
            color: #ccc;
            font-size: 14px;
        }

        #settings label {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 10px;
        }

        #settings input[type="range"] {
            width: 120px;
            accent-color: #666;
        }

        #settings span {
            font-size: 12px;
            color: #888;
            min-width: 35px;
        }

        .particles {
            position: absolute;
            width: 2px;
            height: 2px;
            background: white;
            pointer-events: none;
            border-radius: 50%;
            opacity: 0;
        }

        @keyframes float {
            0% {
                transform: translate(0, 0) scale(0);
                opacity: 0;
            }
            20% {
                opacity: 0.6;
                transform: scale(1);
            }
            100% {
                transform: translate(var(--dx), var(--dy)) scale(0.3);
                opacity: 0;
            }
        }
    </style>
</head>
<body>
    <div id="container">
        <div id="backdrop"></div>
        <div id="shadow-layer"></div>
        <div id="glow-effect"></div>
        <div id="pointer"></div>
        
        <div id="settings">
            <label>
                조명 크기
                <input type="range" id="lightSize" min="80" max="400" value="150">
                <span id="sizeValue">150</span>
            </label>
            <label>
                밝기
                <input type="range" id="intensity" min="0.5" max="1" step="0.1" value="0.7">
                <span id="intensityValue">0.7</span>
            </label>
        </div>
    </div>

    <script>
        const shadowLayer = document.getElementById('shadow-layer');
        const glowEffect = document.getElementById('glow-effect');
        const pointer = document.getElementById('pointer');
        const lightSizeSlider = document.getElementById('lightSize');
        const intensitySlider = document.getElementById('intensity');
        const sizeDisplay = document.getElementById('sizeValue');
        const intensityDisplay = document.getElementById('intensityValue');
        const backdrop = document.getElementById('backdrop');
        const container = document.getElementById('container');

        let mouseX = window.innerWidth / 2;
        let mouseY = window.innerHeight / 2;
        let currentX = mouseX;
        let currentY = mouseY;
        let lightRadius = 150;
        let brightness = 0.7;
        let animationFrame;
        let particleCount = 0;

        function updatePosition(x, y) {
            mouseX = x;
            mouseY = y;
        }

        function smoothFollow() {
            currentX += (mouseX - currentX) * 0.12;
            currentY += (mouseY - currentY) * 0.12;

            const wobble = Math.sin(Date.now() * 0.002) * 2;
            const finalSize = lightRadius + wobble;

            shadowLayer.style.setProperty('--posX', currentX + 'px');
            shadowLayer.style.setProperty('--posY', currentY + 'px');
            shadowLayer.style.setProperty('--size', finalSize + 'px');
            
            glowEffect.style.setProperty('--posX', currentX + 'px');
            glowEffect.style.setProperty('--posY', currentY + 'px');
            glowEffect.style.setProperty('--glowSize', (finalSize * 1.3) + 'px');

            pointer.style.left = mouseX + 'px';
            pointer.style.top = mouseY + 'px';

            animationFrame = requestAnimationFrame(smoothFollow);
        }

        function createParticle(x, y) {
            if (particleCount > 20) return;
            
            const particle = document.createElement('div');
            particle.className = 'particles';
            particle.style.left = x + 'px';
            particle.style.top = y + 'px';
            
            const angle = Math.random() * Math.PI * 2;
            const distance = 30 + Math.random() * 50;
            particle.style.setProperty('--dx', Math.cos(angle) * distance + 'px');
            particle.style.setProperty('--dy', Math.sin(angle) * distance + 'px');
            
            particle.style.animation = 'float 1.5s ease-out';
            container.appendChild(particle);
            particleCount++;

            setTimeout(() => {
                particle.remove();
                particleCount--;
            }, 1500);
        }

        document.addEventListener('mousemove', (e) => {
            updatePosition(e.clientX, e.clientY);
            
            if (Math.random() > 0.9) {
                createParticle(e.clientX, e.clientY);
            }
        });

        document.addEventListener('touchmove', (e) => {
            if (e.touches[0]) {
                updatePosition(e.touches[0].clientX, e.touches[0].clientY);
            }
            e.preventDefault();
        }, { passive: false });

        lightSizeSlider.addEventListener('input', (e) => {
            lightRadius = Number(e.target.value);
            sizeDisplay.textContent = lightRadius;
        });

        intensitySlider.addEventListener('input', (e) => {
            brightness = Number(e.target.value);
            intensityDisplay.textContent = brightness;
            backdrop.style.filter = `grayscale(100%) brightness(${brightness})`;
        });

        document.addEventListener('click', (e) => {
            for (let i = 0; i < 8; i++) {
                setTimeout(() => {
                    createParticle(e.clientX + (Math.random() - 0.5) * 20, 
                                 e.clientY + (Math.random() - 0.5) * 20);
                }, i * 50);
            }
        });

        smoothFollow();
        updatePosition(window.innerWidth / 2, window.innerHeight / 2);
    </script>
</body>
</html>

댓글 남기기

AI, 코딩, 일상 및 다양한 정보 공유에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기