목차
웹 애니메이션
웹 애니메이션을 만드는 방법은 다음과 같다.
- CSS 트랜지션(Transition, 전환)
- CSS 애니메이션(Animation)
- JavaScript 애니메이션(Animation)
1. CSS 트랜지션
CSS 트랜지션(Transition)은 속성 값이 다른 값으로 전환될 때 발생(트리거, trigger)한다.
- CSS 방식 트리거: 가상 클래스(:hover, :focus, :active)에 자주 사용
- JavaScript 방식 트리거: 클래스를 추가/제거하거나 style 속성으로 CSS를 변경
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 17 (그리드 레이아웃, CSS 애니메이션)
목차그리드 컨테이너그리드 아이템 - 라인 기반 배치그리드 아이템 - Name 기반 배치그리드 정렬CSS 애니메이션(Transition)오늘 하루를 돌아보며 그리드 컨테이너display 속성 값을 grid나 inline-grid로
knowledge.narn1kkori.com
2. CSS 애니메이션
애니메이션을 만들려면 @keyframes 구문을 사용하고, 원하는 만큼 백분율(%)로 추가할 수 있다. 애니메이션을 사용할 경우 animation 속성을 사용하고, 8개의 개별 속성을 지원한다.
멋쟁이사자처럼 프론트엔드 부트캠프 14기 - Day 18 (키프레임 애니메이션, Transform, CSS 마스크, 멀
목차CSS 키프레임 애니메이션TransformCSS 마스크멀티 컬럼 레이아웃오늘 하루를 돌아보며 CSS 키프레임 애니메이션애니메이션 효과를 줄 수 있는 2가지 방법 중 하나로, @keyframes at-rule을 선언하여 an
knowledge.narn1kkori.com
CSS 애니메이션은 복잡할 수 있기 때문에 다음 조건을 충족할 때만 사용하는 것이 좋다.
- 움직여야 할 속성이 여럿인 경우
- 반복 횟수, 방향, 종료 상태, 재생 제어 등이 필요할 때
- 화면이 로드될 때 애니메이션을 트리거해야 할 경우
위 조건을 충족하지 않을 때는 CSS 트랜지션을 사용하는 것이 좋다.
애니메이션 성능 고려
브라우저는 애니메이션 효과를 만들기 위해 프레임마다 다음 네 가지 작업을 수행한다.
- 스타일 재계산: 변경된 CSS를 다시 계산하기 때문에 비용이 가장 높다.
- 레이아웃: 요소의 위치와 크기를 다시 배치(Layout)하며, 비용이 아주 높다. 레이아웃 스래싱 위험이 있다.
- 페인트: 요소의 픽셀(색상, 텍스트 등)을 다시 그린다. 비용은 중간이다.
- 컴포지트: 레이어를 합쳐 화면에 최종 렌더링하며, 비용이 가장 낮다.
레이아웃 변경은 성능에 악영향을 미치기 때문에 가능하면 transform, opacity 속성을 사용하는 애니메이션을 구현하는 것이 좋다. 레이아웃과 페인트 단계를 피할 수 있어 성능 저하 없이 부드럽게 애니메이션이 작동하기 때문이다.
렌더링 단계 | 대표적인 영향 속성 | 설명 |
레이아웃 (Layout) |
width, height, top. left, margin, padding, border, display, position 등 | 요소의 크기 및 위치가 변경되면 발생 가장 큰 비용 발생 |
페인트 (Paint) |
background-color, box-shadow, border-radius, color, text-decoration 등 | 모양, 색상이 바뀌면 발생 중간 정도 비용 발생 |
컴포지트 (Composite) |
transform, opacity, filter (GPU 가속 가능 속성) | 실제 픽셀 계산 없이 레이어를 재배치 가장 비용이 낮고 빠름 |
3. JavaScript 애니메이션
JavaScript로 트랜지션이나 애니메이션 이벤트를 감지해야 할 경우 transition, animation 이벤트를 사용한다. 이러한 이벤트는 CSS 트랜지션, 애니메이션 시점과 JavaScript를 동기화해야 할 때 유용하다.
transition 이벤트는 CSS 트랜지션이 진행될 때 이벤트를 트리거한다. 2개 이상의 속성에 개별 트랜지션을 설정한 경우에는 각 속성마다 이벤트가 트리거된다.
// CSS 트랜지션이 처음 생성될 때(시작 전) 이벤트 트리거
element.addEventListener('transitionrun', (e) => {
console.log('run', e.propertyName, e.target)
})
// CSS 트랜지션이 시작될 때 이벤트 트리거
element.addEventListener('transitionstart', (e) => {
console.log('start', e.propertyName, e.target)
})
// CSS 트랜지션이 완료될 때 이벤트 트리거
element.addEventListener('transitionend', (e) => {
console.log('end', e.propertyName, e.target)
})
// CSS 트랜지션이 중간에 취소될 때 이벤트 트리거
element.addEventListener('transitioncancel', (e) => {
console.log('cancel', e.propertyName, e.target)
})
animation 이벤트는 CSS 애니메이션이 진행될 때 이벤트를 트리거한다. 애니메이션이 무한 반복(infinite)될 경우, animationend 이벤트는 트리거 되지 않는다.
// CSS 애니메이션이 시작할 때 이벤트 트리거
element.addEventListener('animationstart', (e) => {
console.log('start', e.animationName, e.target)
})
// CSS 애니메이션이 반복될 때 이벤트 트리거
element.addEventListener('animationiteration', (e) => {
console.log('iteration', e.animationName, e.target)
})
// CSS 애니메이션이 완료될 때 이벤트 트리거
element.addEventListener('animationend', (e) => {
console.log('end', e.animationName, e.target)
})
// CSS 애니메이션이 중간에 취소될 때 이벤트 트리거
element.addEventListener('animationcancel', (e) => {
console.log('cancel', e.animationName, e.target)
})
Web Animations API
CSS 트랜지션이나 애니메이션으로 만들기 어려운 것은 JavaScript 애니메이션으로 만드는 것이 낫다. 요소에 애니메이션을 적용하려면 element.animate() 메서드를 사용한다.
const element = document.querySelector('.rainbow')
element.animate(
// 키프레임(keyframes)
[
{ background: '#ff004d', offset: 0 },
{ background: '#ff77ab', offset: 0.20 },
{ background: '#00e756', offset: 0.5 },
{ background: '#29adff', offset: 0.80 },
{ background: '#ff77ab', offset: 1 },
],
// 옵션(options)
{
duration: 2000,
direction: 'alternate',
iterations: Infinity,
}
)
웹 애니메이션 API가 정말 유용한 부분은 애니메이션 제어다. 재생, 일시정지, 역재생 기능을 제어할 수 있다.
const animateElement = element.animate(/* ... */)
// 일시정지
function pauseAnimate() {
animateElement.pause()
}
// 재생
function playAnimate() {
animateElement.play()
}
// 재생속도 변경
function chnagePlaybackRate(rate) {
animateElement.updatePlaybackRate(rate)
}
// 가속
function moreFaster(ratio = 1.1) {
const currentPlaybackRate = animateElement.playbackRate;
animateElement.updatePlaybackRate(currentPlaybackRate * ratio)
}
// 감속
function moreSlower(ratio = 0.9) {
const currentPlaybackRate = animateElement.playbackRate;
animateElement.updatePlaybackRate(currentPlaybackRate * ratio)
}
// 역재생
function reverseAnimate() {
animateElement.reverse()
// animateElement.playbackRate = -1
// animateElement.play()
}
// 종료 여부
function isFinished() {
return animateElement.finished
}
// 특정 시간으로 이동
function goToFrame(frameTime /* ms */) {
animateElement.currentTime = frameTime
}
// 끝으로 이동
function goToEndFrame() {
animateElement.finish()
}
// 중단
function cancelAnimate() {
animateElement.cancel()
}
// 애니메이션 종료 이벤트
animateElement.addEventListener('finish', () => {
console.log('애니메이션 종료')
}
// 애니메이션 중단 이벤트
animateElement.addEventListener('cancel', () => {
console.log('애니메이션 중단')
}
오늘 하루를 돌아보며
강의 초반에 지난 주에 배웠던 이벤트 위임을 활용해서 모달 다이얼로그, 아코디언, 탭 메뉴, 캐러셀의 코드를 수정해 봤다. 실습을 진행하면서 이벤트 위임이 얼마나 유용한 기능인지 알게 됐다.
- 이벤트 위임을 사용해 코드가 더 간결하고 효율적으로 개선됨
- 요소의 개수에 상관 없이 리스너는 1개만 필요
- 동적으로 추가되는 요소에도 자동 대응 가능
- 유지보수 및 성능 측면에서 장점이 큼
물론, 이벤트 위임이 익숙해지도록 연습을 그만큼 많이 해야 하지만 말이다ㅠㅠ 지난 주 과제를 마무리하지 못하고 제출했는데, 이벤트 위임을 사용하면 해낼 수 있을 것 같다. 오늘 과제 못 한 거 다시 시도해 보려고 한다. 실패해도 다시 시도하는 용기를 내보자!