.filmstrip {
animation: slide 1s infinite steps(7);
}
@keyframes slide {
to { transform: translate(-700px); }
}
.filmstrip {
/* steps(7) = 마지막을 포함하여 7번의 '변경' */
animation: slide 1s infinite steps(7);
}
@keyframes slide {
to { transform: translate(-700px); }
}
.filmstrip {
animation: slide 1s infinite steps(6);
}
@keyframes slide {
to { transform: translate(-700px); }
}
.filmstrip {
/* steps(6) = steps(6, end) */
animation: slide 1s infinite steps(6);
}
@keyframes slide {
to { transform: translate(-700px); }
}
.filmstrip {
animation: slide 1s infinite steps(6, start);
}
@keyframes slide {
to { transform: translate(-700px); }
}
.filmstrip {
/* 신규! jump-none! */
animation: slide 1s infinite steps(7, jump-none);
}
@keyframes slide {
to { transform: translate(-700px); }
}
jump-none
, jump-both
transitionrun
→ transition이 생성됩니다 🆕
→ transitionend
를 기다려도 괜찮습니다 🙆♂️
transitioncancel
→ 요소가 사라집니다 🗑️ (제거되거나, 재생성되거나, display:none
되는 등)
→ transitionend
를 기다리지 마세요 ❌
transitionstart
(c.f. animationstart
)
animationcancel
(c.f. transitioncancel
)
transitioncancel
과 친구들animationcancel
transitionstart
animationcancel
Element.animate
.
const uniqueKeyframesName = generateUuid();
document.styleSheets[0].insertRule(
`@keyframes ${uniqueKeyframesName} {
from { transform: scale(0) }
95% { transform: scale(${fullSize}) }
to { transform: scale(0) }
}`
);
star.style.animation =
`${uniqueKeyframesName} ${duration}ms ` +
`${delay}ms infinite`;
// 정리를 잊지 마세요!
transitionrun
, transitioncancel
등
Element.animate()
사용
star.animate(
[
{ transform: 'scale(0)' },
{ transform: `scale(${fullSize})`, offset: 0.95 },
{ transform: 'scale(0)' },
],
{ easing, duration, delay, iterations: Infinity }
);
Element.animate()
사용
const starAnimation = star.animate(
[
{ transform: 'scale(0)' },
{ transform: `scale(${fullSize})`, offset: 0.95 },
{ transform: 'scale(0)' },
],
{ easing, duration, delay, iterations: Infinity }
);
// starAnimation.reverse();
// starAnimation.cancel();
// starAnimation.updatePlaybackRate(0.5);
starAnimation.finished.then(() => { ... });
Element.animate()
.body, .head {
animation-timing-function: steps(4);
}
.head {
animation-timing-function: steps(4);
}
@keyframes drop-head {
60% { transform: rotate(45deg) }
}
@keyframes drop-head {
0% {
transform: none;
animation-timing-function: steps(4);
}
50% {
transform: rotate(45deg);
animation-timing-function: steps(4);
}
100% { transform: none; }
}
head.animate(
{
// Chrome/Firefox는 아직 암시적 0%/100%
// 키프레임을 지원하지 않습니다
transform: ['none', 'rotate(60deg)', 'none'],
offset: [0, 0.6, 1],
},
{
duration: 2000,
iterations: Infinite,
easing: 'steps(4)'
}
);
Element.animate
.
head.animate(
{
transform: ['none', 'rotate(60deg)', 'none'],
offset: [0, 0.6, 1],
easing: 'ease-in',
},
{
duration: 2000,
iterations: Infinite,
easing: 'steps(4)'
}
);
Element.animate
.
Element.getAnimations()
document.addEventListener('transitionrun', evt => {
if (evt.propertyName !== 'transform') {
return;
}
const transition = evt.target
.getAnimations()
.find(animation => animation.transitionProperty === 'transform');
const keyframes = transition.effect.getKeyframes();
const distance = calculateDistance(
keyframes[0].transform,
keyframes[1].transform
);
// 초당 700px로 이동
const duration = (distance / 700) * 1000;
transition.effect.updateTiming({ duration });
// 참고: transition.updatePlaybackRate()가 더 좋습니다
});
Element.getAnimations
.
Element.getAnimations
to return
transform transition endpoints.
Element.getAnimations()
CSSTransition.setKeyframes()
document.addEventListener('transitionrun', evt => {
if (evt.propertyName !== 'fill') {
return;
}
const transition = evt.target
.getAnimations()
.find(animation => animation.transitionProperty === 'fill');
const keyframes = transition.effect.getKeyframes();
const hslKeyframes = generateHslKeyframes(
keyframes[0].fill,
keyframes[1].fill
);
transition.effect.setKeyframes(hslKeyframes);
});
Element.getAnimations
.
setKeyframes
on
transitions.
CSSTransition.setKeyframes() (Element.getAnimations)
offset-path
.
offset-path
.goose {
offset-path: path('M100 100...');
animation: fly 10s linear infinite;
}
@keyframes fly {
to { offset-distance: 100%; }
}
offset-path
offset-anchor
은 구현 진행 중)
offset-anchor
은 구현 진행 중)
transform
과 opacity
offset-path
.
prefers-reduced-motion: reduce | no-preference
div {
animation: ...
transition: ...
}
@media (prefers-reduced-motion: reduce) {
* {
animation-name: none !important;
transition-property: none !important;
/* transition 이벤트가 여전히 필요하다면
* transition-duration: 0.01s !important;
* transition-delay: 0s !important;
* ...
*/
}
}
// 브라우저 지원을 확인하세요
if (!('animate' in elem)) {
return;
}
// 사용자 지원을 확인하세요
if (matchMedia('(prefers-reduced-motion)').matches) {
return;
}
// 애니메이션을 작성하세요
prefers-reduced-motion
jump-none
transitioncancel
Element.animate
Element.animate
Element.getAnimations
Element.getAnimations
offset-path
prefers-reduced-motion