Created by 손찬욱 / chanuk.son
setTimeout, setInterval
setTimeout은 main Thread의 작업에 영향을 받는다.
function animate(el, x, y, duration) {
var startTime = Date.now();
var now, per;
(function loop() {
now = Date.now();
if (now >= startTime + duration) {
// animation 종료 시점 (loop 종료 시점)
el.style.transform = "translate3d(" + x + "px," + y + "px,0)";
return;
}
per = (now - startTime) / duration;
// animation 진행. easing 처리
el.style.transform = "translate3d(" + (x * per) + "px," + (y * per) + "px,0)";
// loop 재호출
setTimeout(loop, 16);
})();
}
구분 | 기술 |
---|---|
Javascript | requestAnimationFrame |
CSS | CSS Transition |
CSS | CSS Animation |
SVG | SVG Animation |
requestAnimationFrame는 setTimeout과 사용법은
동일하지만 60 fps를 보장한다.
CSS 는 별도의 thread를 이용하기 때문에 빠르다.
requestAnimationFrame VS csstransitionCSS Transition을 이용하면 간단히 구현할수 있다.
.transition { transition: margin-left 1s linear, background-color 2s ease 1s; }
div.exampletransition:hover div.transition {
margin-left: 700px;
background-color: green;
}
애니메이션 중간 상태를 지정할 수 있다.
@keyframes slidein {
0% {
margin-left:100%;
width:300%;
}
75% {
font-size:300%;
margin-left:25%;
width:150%;
}
100% {
margin-left:0%;
width:100%;
}
}
.target {
animation-delay : 0;
animation-direction : normal; //normal, reverse, alternate, alternate-reverse
animation-duration : 3s;
animation-iteration-count : infinite;
animation-name : slidein;
animation-play-state : running; // paused, running
animation-timing-function : linear;
animation-fill-mode : none; // none, forwards, backwards, both
}
애니메이션 진행상태를 제어할수 있다.
animation(start/end/iteration) 이벤트를 제공
var e = document.getElementById("watchme");
// attach event
e.addEventListener("animationstart", listener, false);
e.addEventListener("animationend", listener, false);
e.addEventListener("animationiteration", listener, false);
// apply css animation
e.className = "slidein";
// event handler
function listener(e) {
var l = document.createElement("li");
switch(e.type) {
case "animationstart":
l.innerHTML = "Started: elapsed time is " + e.elapsedTime;
break;
case "animationend":
l.innerHTML = "Ended: elapsed time is " + e.elapsedTime;
break;
case "animationiteration":
l.innerHTML = "New loop started at time " + e.elapsedTime;
break;
}
document.getElementById("output").appendChild(l);
}
통합된 애니메이션 제어 모델을 제공하기 위한 자바스크립트 API
Web Animation API Spec-1
Animation = Element.animate(Keyframes, duration)
var player = document.getElementById("code1").animate([
{ transform: 'scale(1)', opacity: 1},
{ transform: 'scale(.5)', opacity: .5},
{ transform: 'scale(.667)', opacity: .667},
{ transform: 'scale(.6)', opacity: .6}
], 3000);
Animation=Element.animate(Keyframes, AnimationEffectTiming)
offset을 이용하여 animation의 중간 단계를 지정할수 있다.
player = document.getElementById("code2").animate([
{ offset: 0, transform: 'translate3d(0px,0px,0)' },
{ offset: 0.25, transform: 'translate3d(100px,0px,0)' },
{ offset: 0.5, transform: 'translate3d(100px,100px,0)' },
{ offset: 0.75, transform: 'translate3d(0,100px,0)' },
{ offset: 1, transform: 'translate3d(0,0px,0)' }
], {
duration: 1500, //milliseconds
easing: "ease-in-out", // "linear", "ease", "ease-in", "ease-out" a bezier curve, etc.
delay: 10, //milliseconds
iterations: Infinity, //or a number
direction: 'alternate' //'normal', 'reverse', 'alternate', 'alternate-reverse' etc.
});
interface Animation {
attribute AnimationEffectReadOnly effect;
attribute AnimationTimeline timeline;
attribute double startTime;
attribute double currentTime;
attribute double playbackRate;
readonly attribute AnimationPlayState playState;
readonly attribute Promise Animation ready;
readonly attribute Promise Animation finished;
void cancel ();
void finish ();
void play ();
void pause ();
void reverse ();
};
enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
Animation 동작상태를 조절할수 있다.
player = document.getElementById("code3").animate([
{ offset: 0, transform: 'scale(1)', opacity: 1 },
{ offset: 0.3, transform: 'scale(.5)', opacity: 0.5 },
{ offset: 0.7875, transform: 'scale(.667)', opacity: 0.667 },
{ offset: 1, transform: 'scale(.6)', opacity: 0.6 }
], {
duration: 1500, //milliseconds
easing: "ease-in-out", // "linear", "ease", "ease-in", "ease-out" a bezier curve, etc.
delay: 10, //milliseconds
iterations: Infinity, //or a number
direction: 'alternate' //'normal', 'reverse', 'alternate', 'alternate-reverse' etc.
});
playbackRate, currentTime으로 Timeline을 조절할수 있다.
player = document.getElementById("code3_1").animate([
{ offset: 0, transform: 'translate3d(0px,0px,0)' },
{ offset: 0.25, transform: 'translate3d(100px,0px,0)' },
{ offset: 0.5, transform: 'translate3d(100px,100px,0)' },
{ offset: 0.75, transform: 'translate3d(0,100px,0)' },
{ offset: 1, transform: 'translate3d(0,0px,0)' }
], {
duration: 5000, //milliseconds
easing: 'ease-in-out', //'linear', a bezier curve, etc.
iterations: 1000, //or a number
direction: 'normal', //'normal', 'reverse', 'alternate' etc.
});
player = document.getElementById("code4").animate([
{ offset: 0, transform: 'scale(1)', opacity: 1 },
{ offset: 0.3, transform: 'scale(.5)', opacity: 0.5 },
{ offset: 0.7875, transform: 'scale(.667)', opacity: 0.667 },
{ offset: 1, transform: 'scale(.6)', opacity: 0.6 }
], {
duration: 1500, //milliseconds
easing: "ease-in-out", // "linear", "ease", "ease-in", "ease-out" a bezier curve, etc.
delay: 10, //milliseconds
iterations: 1000, //or a number
direction: 'alternate' //'normal', 'reverse', 'alternate', 'alternate-reverse' etc.
});
var message = document.getElementById("mcode4");
// ready promise
player.ready.then(function(value) {
message.innerHTML += "[성공] 애니메이션 시작" + "<br>";
}, function(reason) {
message.innerHTML += "[실패] 애니메이션 시작" + "<br>";
});
// finished promise
player.finished.then(function(value) {
message.innerHTML += "[성공] 애니메이션 종료" + "<br>";
}, function(reason) {
message.innerHTML += "[실패] 애니메이션 종료" + "<br>";
});
var str=message.innerHTML;
Web Animation API Spec-2
Animation의 timeline을 관리함
interface AnimationTimeline {
readonly attribute double currentTime;
sequence Animation getAnimations();
};
[Constructor (DOMHighResTimeStamp originTime)]
interface DocumentTimeline : AnimationTimeline {
void play();
};
실행 중인 Animation 애니메이션을 얻음
var players = document.timeline.getAnimations();
players.forEach(function(player, i, ra) {
//player.pause();
//player.play();
});
선언된 Animation Effect를 Timeline으로 관리한다.
document.timeline.currentTime;
var animation = document.timeline.play(effect);
Animation 객체를 반환하여 애니메이션 제어가 가능하다
Element.animation과 유사하나, 선언시 바로 실행되지 않는다.
// define
var effect = new KeyframeEffect(document.getElementById("code5"),
[
{ opacity: 1 },
{ opacity: 0 }
], {
duration: 1000
});
// play
document.timeline.play(effect);
/*
document.getElementById("code5").animate(
[
{ opacity: 1 },
{ opacity: 0 }
], {
duration: 1000
});
*/
#old1 { transition: opacity 1s ease;}
#old2 { transition: opacity 1s ease;}
#old3 { transition: opacity 1s ease;}
.fade { opacity:0; }
var els = [ document.getElementById("old1"),
document.getElementById("old2"),
document.getElementById("old3") ];
els.forEach(function(v,i) {
v.classList.add("fade");
});
Group 단위로 애니메이션을 제어할수 있다.
Keyframe/Group/SequenceEffect를 포함할 수 있다.
// define
var keyframe = [
{ opacity: 1 },
{ opacity: 0 }
];
var timings = {
duration: 1000,
easing : "ease",
fill : "both"
}
// define animation effect
var group = new GroupEffect([
new KeyframeEffect(document.getElementById("code6"), keyframe, timings),
new KeyframeEffect(document.getElementById("code7"), keyframe, timings),
new KeyframeEffect(document.getElementById("code8"), keyframe, timings)
]);
document.timeline.play(group);
지연 시간을 이용한 애니메이션 호출
#old4 { transition: opacity 1s ease;}
#old5 { transition: opacity 1s ease 1s;}
#old6 { transition: opacity 1s ease 2s;}
.fade { opacity:0; }
var els = [ document.getElementById("old4"),
document.getElementById("old5"),
document.getElementById("old6") ];
els.forEach(function(v,i) {
v.classList.add("fade");
});
순차적 애니메이션의 조합
// define
var keyframe = [
{ opacity: 1 },
{ opacity: 0 }
];
var timings = {
duration: 1000,
easing : "ease",
fill : "both"
}
// define animation effect
var sequence = new SequenceEffect([
new KeyframeEffect(document.getElementById("code9"), keyframe, timings),
new KeyframeEffect(document.getElementById("code10"), keyframe, timings),
new KeyframeEffect(document.getElementById("code11"), keyframe, timings)
]);
document.timeline.play(sequence);
var sEffectA = new SequenceEffect([
new KeyframeEffect(el, keyframe, timings);
]);
var sEffectB = new SequenceEffect([
new KeyframeEffect(el, keyframe, timings);
]);
var gEffectC = new GroupEffect([sEffectA, sEffectB]);
var animation = document.timeline.play(gEffectC);
Motion path를 따라가는 animation 구현도 가능
.target {
motion-path: path('M93.9,46.4c9.3,9.5,13.8,17.9,23.5,17.9s17.5-7.8,17.5-17.5s-7.8-17.6-17.5-17.5c-9.7,0.1-13.3,7.2-22.1,17.1 c-8.9,8.8-15.7,17.9-25.4,17.9s-17.5-7.8-17.5-17.5s7.8-17.5,17.5-17.5S86.2,38.6,93.9,46.4z');
}
Web Animation Spec-1 구현 완료, Spec-2 일부 구현
#Enable Experimental Web Platform Feature 활성화 적용시https://github.com/web-animations/web-animations-js