VanilaJS 로 Fade In / Out 구현하기
- Published on
웹 페이지에 이미지를 동적으로 표시하는 것은 사용자의 관심을 끌고, 웹 사이트의 시각적 매력을 향상시킬 수 있는 훌륭한 방법입니다. 이미지가 서서히 나타나고 (Fade In) 사라지는 (Fade Out) 효과는 사용자 경험에 부드러움을 더해 줍니다. 이 글에서는 VanillaJS로 이미지에 Fade In 및 Fade Out 효과를 적용하는 방법을 단계별로 설명합니다.
시작하기
HTML
먼저 HTML 구조를 만듭니다. 이미지를 표시할 img 태그와 효과를 트리거할 버튼 두 개가 필요합니다.
<div>
<button id="btn-fade-in">Fade In</button>
<button id="btn-fade-out">Fade Out</button>
</div>
<img
src="https://images.unsplash.com/photo-1707343843598-39755549ac9a?q=80&w=600&auto=format&fit=crop"
width="600"
id="image"
style="display: none"
/>
자바스크립트로 동작 추가하기
fadeIn 함수 내부에서 requestAnimationFrame으로 지정된 duration 동안 애니메이션을 실행합니다. 애니메이션에 대한 정보는 element의 fadeState에 추가하며 이 정보는 추후에 애니메이션을 취소하기 위해서 필요합니다.
function fadeIn(element, duration = 300) {
// 기존에 fade 애니메이션이 있으면 취소
cancelFade(element);
// element css 초기화
element.style.opacity = 0;
element.style.display = 'block';
const state = {
startTime: performance.now(), // 애니메이션 시작 시간
animationFrameId: null, // requestAnimationFrame의 id
};
function animate(currentTime) {
const elapsed = currentTime - state.startTime;
element.style.opacity = Math.min(1, elapsed / duration); // 0 - 1
if (elapsed < duration) {
// duration 보다 이전 시간이면 애니메이션 실행
state.animationFrameId = requestAnimationFrame(animate);
} else {
console.log('finished fade in');
}
}
state.animationFrameId = requestAnimationFrame(animate);
element.fadeState = state; // 애니메이션 취소를 위한 애니메이션 정보
}
function fadeOut(element, duration = 300) {
// 기존에 fade 애니메이션이 있으면 취소
cancelFade(element);
const state = {
startTime: performance.now(), // 애니메이션 시작 시간
animationFrameId: null, // requestAnimationFrame의 id
};
function animate(currentTime) {
const elapsed = currentTime - state.startTime;
element.style.opacity = Math.max(0, 1 - elapsed / duration); // 1 - 0
if (elapsed < duration) {
state.animationFrameId = requestAnimationFrame(animate);
} else {
element.style.display = 'none';
console.log('finished fade out');
}
}
state.animationFrameId = requestAnimationFrame(animate);
element.fadeState = state; // 애니메이션 취소를 위한 애니메이션 정보
}
기존에 실행중인 fade 애니메이션이 있다면 취소하는 함수입니다. element의 fadeState를 찾아서 null이 아니면 fade 애니메이션을 취소합니다.
// fade in/out 애니메이션을 취소
function cancelFade(element) {
const state = element.fadeState;
if (state && state.animationFrameId) {
cancelAnimationFrame(state.animationFrameId);
element.fadeState = null;
}
}
Fade In / Fade Out 버튼에 EventListener를 추가합니다.
#btn-fade-in
버튼을 클릭하면 이미지의 opacity
가 0에서 1로 0.3초간 변경됩니다. 반면 #btn-fade-out
버튼을 클릭하면 이미지의 opacity
가 1에서 0으로 0.3초간 변경됩니다.
// Fade In 버튼 클릭
document.querySelector('#btn-fade-in').addEventListener('click', function () {
fadeIn(document.querySelector('#image'), 300);
});
// Fade Out 버튼 클릭
document.querySelector('#btn-fade-out').addEventListener('click', function () {
fadeOut(document.querySelector('#image'), 300);
});
전체 코드
아래는 전체 코드입니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="initial-scale=1.0, minimum-scale=1.0, width=device-width, viewport-fit=cover"
/>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
</style>
<title>VanilaJS Fade In / Fade Out</title>
</head>
<body>
<div>
<button id="btn-fade-in">Fade In</button>
<button id="btn-fade-out">Fade Out</button>
</div>
<img
src="https://images.unsplash.com/photo-1707343843598-39755549ac9a?q=80&w=600&auto=format&fit=crop"
width="600"
id="image"
style="display: none"
/>
<script>
function fadeIn(element, duration = 300) {
// 기존에 fade 애니메이션이 있으면 취소
cancelFade(element);
// element css 초기화
element.style.opacity = 0;
element.style.display = 'block';
const state = {
startTime: performance.now(), // 애니메이션 시작 시간
animationFrameId: null, // requestAnimationFrame의 id
};
function animate(currentTime) {
const elapsed = currentTime - state.startTime;
element.style.opacity = Math.min(1, elapsed / duration);
if (elapsed < duration) {
state.animationFrameId = requestAnimationFrame(animate);
} else {
console.log('finished fade in');
}
}
state.animationFrameId = requestAnimationFrame(animate);
element.fadeState = state; // 애니메이션 취소를 위한 애니메이션 정보
}
function fadeOut(element, duration = 300) {
// 기존에 fade 애니메이션이 있으면 취소
cancelFade(element);
const state = {
startTime: performance.now(), // 애니메이션 시작 시간
animationFrameId: null, // requestAnimationFrame의 id
};
function animate(currentTime) {
const elapsed = currentTime - state.startTime;
element.style.opacity = Math.max(0, 1 - elapsed / duration);
if (elapsed < duration) {
state.animationFrameId = requestAnimationFrame(animate);
} else {
element.style.display = 'none';
console.log('finished fade out');
}
}
state.animationFrameId = requestAnimationFrame(animate);
element.fadeState = state; // 애니메이션 취소를 위한 애니메이션 정보
}
// fade in/out 애니메이션을 취소
function cancelFade(element) {
const state = element.fadeState;
if (state && state.animationFrameId) {
cancelAnimationFrame(state.animationFrameId);
element.fadeState = null;
}
}
// Fade In 버튼 클릭
document
.querySelector('#btn-fade-in')
.addEventListener('click', function () {
fadeIn(document.querySelector('#image'), 300);
});
// Fade Out 버튼 클릭
document
.querySelector('#btn-fade-out')
.addEventListener('click', function () {
fadeOut(document.querySelector('#image'), 300);
});
</script>
</body>
</html>