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