JavaScript-анÑмаÑÑÑ Ð¼Ð¾Ð¶ÑÑÑ ÑобиÑи Ñе, Ñо не пÑд ÑÐ¸Ð»Ñ CSS.
ÐапÑиклад, ÑÑÑ Ð°Ñи ÑоÑÑ Ð¿Ð¾ ÑкладнÑй ÑÑаÑкÑоÑÑÑ Ð· ÑÑнкÑÑÑÑ ÑаÑÑ, вÑдмÑÐ½Ð½Ð¾Ñ Ð²Ñд кÑÐ¸Ð²Ð¸Ñ ÐезÑÑ, або ÑÑвоÑÑваÑи анÑмаÑÑÑ Ð² елеменÑÑ canvas.
ÐикоÑиÑÑÐ°Ð½Ð½Ñ setInterval
ÐнÑмаÑÑÑ Ð¼Ð¾Ð¶Ðµ бÑÑи ÑеалÑзована Ñк поÑлÑдовнÑÑÑÑ ÐºÐ°Ð´ÑÑв â зазвиÑай Ñе Ð½ÐµÐ²ÐµÐ»Ð¸ÐºÑ Ð·Ð¼Ñни Ñ Ð²Ð»Ð°ÑÑивоÑÑÑÑ HTML/CSS.
ÐапÑиклад, змÑна style.left з 0px до 100px пеÑемÑÑÑÑ ÐµÐ»ÐµÐ¼ÐµÐ½Ñ. Ð ÑкÑо ми збÑлÑÑимо ÑÑ Ð² setInterval, змÑнÑÑÑи на 2px з маленÑÐºÐ¾Ñ Ð·Ð°ÑÑимкоÑ, напÑиклад, 50 ÑазÑв на ÑекÑндÑ, Ñо Ñе бÑде виглÑдаÑи плавно. Це Ñой Ñамий пÑинÑип, Ñо Ñ Ð² кÑно: 24 кадÑÑв на ÑекÑÐ½Ð´Ñ Ð´Ð¾ÑÑаÑнÑо, Ñоб вÑе виглÑдало плавно.
ÐÑевдокод може виглÑдаÑи Ñак:
let timer = setInterval(function() {
if (animation complete) clearInterval(timer);
else increase style.left by 2px
}, 20); // змÑнÑÑÑÑÑÑ Ð½Ð° 2 пÑкÑÐµÐ»Ñ ÐºÐ¾Ð¶Ð½Ñ 20 мÑ, Ñо пÑиблизно 50 кадÑÑв на ÑекÑндÑ
ÐÑлÑÑ Ð¿Ð¾Ð²Ð½Ð¸Ð¹ пÑиклад анÑмаÑÑÑ:
let start = Date.now(); // збеÑÑгаÑмо ÑÐ°Ñ Ð¿Ð¾ÑаÑкÑ
let timer = setInterval(function() {
// ÑкÑлÑки ÑаÑÑ Ð¿ÑойÑло вÑд поÑаÑкÑ?
let timePassed = Date.now() - start;
if (timePassed >= 2000) {
clearInterval(timer); // завеÑÑÑÑмо анÑмаÑÑÑ ÑеÑез 2 ÑекÑнди
return;
}
// малÑÑмо анÑмаÑÑÑ Ð² Ð¼Ð¾Ð¼ÐµÐ½Ñ ÑаÑÑ timePassed
draw(timePassed);
}, 20);
// коли timePassed змÑнÑÑÑÑÑÑ Ð²Ñд 0 до 2000
// left оÑÑимÑÑ Ð·Ð½Ð°ÑÐµÐ½Ð½Ñ Ð²Ñд 0px до 400px
function draw(timePassed) {
train.style.left = timePassed / 5 + 'px';
}
ÐлаÑнÑÑÑ, Ñоб побаÑиÑи демо-веÑÑÑÑ:
<!DOCTYPE HTML>
<html>
<head>
<style>
#train {
position: relative;
cursor: pointer;
}
</style>
</head>
<body>
<img id="train" src="https://js.cx/clipart/train.gif">
<script>
train.onclick = function() {
let start = Date.now();
let timer = setInterval(function() {
let timePassed = Date.now() - start;
train.style.left = timePassed / 5 + 'px';
if (timePassed > 2000) clearInterval(timer);
}, 20);
}
</script>
</body>
</html>ÐикоÑиÑÑÐ°Ð½Ð½Ñ requestAnimationFrame
УÑвÑмо, Ñо Ñ Ð½Ð°Ñ Ñ ÐºÑлÑка анÑмаÑÑй, запÑÑÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¾ÑаÑно.
ЯкÑо ми запÑÑÑимо ÑÑ
окÑемо, Ñо навÑÑÑ ÑкÑо кожен з ниÑ
Ð¼Ð°Ñ setInterval(..., 20), Ñо бÑаÑзеÑÑ Ð´Ð¾Ð²ÐµÐ´ÐµÑÑÑÑ Ð¿ÐµÑемалÑовÑваÑи набагаÑо ÑаÑÑÑÑе, нÑж ÐºÐ¾Ð¶Ð½Ñ 20ms.
Це ÑомÑ, Ñо вони маÑÑÑ ÑÑзний ÑÐ°Ñ Ð·Ð°Ð¿ÑÑкÑ, ÑÐ¾Ð¼Ñ âÐºÐ¾Ð¶Ð½Ñ 20 мÑâ вÑдÑÑзнÑÑÑÑÑÑ Ð´Ð»Ñ ÑÑзниÑ
анÑмаÑÑй. ÐнÑеÑвали не виÑÑвнÑнÑ. ÐÑже, ми маÑимемо кÑлÑка незалежниÑ
пÑогонÑв пÑоÑÑгом 20ms.
ÐнÑими Ñловами, Ñе:
setInterval(function() {
animate1();
animate2();
animate3();
}, 20)
â¦Ð»ÐµÐ³Ñе, нÑж ÑÑи Ð½ÐµÐ·Ð°Ð»ÐµÐ¶Ð½Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ¸:
setInterval(animate1, 20); // Ð½ÐµÐ·Ð°Ð»ÐµÐ¶Ð½Ñ Ð°Ð½ÑмаÑÑÑ
setInterval(animate2, 20); // в ÑÑзниÑ
мÑÑÑÑÑ
кодÑ
setInterval(animate3, 20);
Ð¦Ñ ÐºÑлÑка Ð½ÐµÐ·Ð°Ð»ÐµÐ¶Ð½Ð¸Ñ Ð¿ÐµÑемалÑовÑÐ²Ð°Ð½Ñ ÑлÑд згÑÑпÑваÑи Ñазом, Ñоб зÑобиÑи пеÑемалÑовÑÐ²Ð°Ð½Ð½Ñ Ð¿ÑоÑÑÑÑим Ð´Ð»Ñ Ð±ÑаÑзеÑа, а оÑже, зменÑиÑи наванÑÐ°Ð¶ÐµÐ½Ð½Ñ Ð½Ð° пÑоÑеÑÐ¾Ñ Ñ Ð²Ð¸Ð³Ð»ÑдаÑи бÑлÑÑ Ð¿Ð»Ð°Ð²Ð½Ð¾.
Ð Ñе одна ÑÑÑ, пÑо ÑÐºÑ ÑлÑд памâÑÑаÑи. ÐÐ½Ð¾Ð´Ñ Ð¿ÑоÑеÑÐ¾Ñ Ð¿ÐµÑеванÑажений, або Ñ ÑнÑÑ Ð¿ÑиÑини пеÑемалÑовÑваÑи ÑÑдÑе (напÑиклад, коли Ð²ÐºÐ»Ð°Ð´ÐºÑ Ð±ÑаÑзеÑа пÑиÑ
овано), ÑÐ¾Ð¼Ñ Ð½Ð°Ð¼ дÑйÑно не ÑлÑд запÑÑкаÑи його ÐºÐ¾Ð¶Ð½Ñ 20ms.
Ðле Ñк дÑзнаÑиÑÑ Ð¿Ñо Ñе в JavaScript? ÐÑнÑÑ ÑпеÑиÑÑкаÑÑÑ Animation timing, Ñка Ð½Ð°Ð´Ð°Ñ ÑÑнкÑÑÑ requestAnimationFrame. Ðона виÑÑÑÑÑ Ð²ÑÑ ÑÑ Ð¿Ð¸ÑÐ°Ð½Ð½Ñ Ñ Ð½Ð°Ð²ÑÑÑ Ð±ÑлÑÑе.
СинÑакÑиÑ:
let requestId = requestAnimationFrame(callback)
Це запланÑÑ Ð·Ð°Ð¿ÑÑк ÑÑнкÑÑÑ callback на найближÑий ÑаÑ, коли бÑаÑÐ·ÐµÑ Ð·Ð°Ñ
оÑе виконаÑи анÑмаÑÑÑ.
ЯкÑо ми зÑобимо змÑни в елеменÑаÑ
Ñ callback, Ñо вони бÑдÑÑÑ Ð·Ð³ÑÑÐ¿Ð¾Ð²Ð°Ð½Ñ Ñазом з ÑнÑими викликами requestAnimationFrame Ñ Ð· CSS-анÑмаÑÑÑми. Таким Ñином, бÑде виконано один пеÑеÑаÑ
Ñнок геомеÑÑÑÑ Ñа пеÑемалÑовÑÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¼ÑÑÑÑ Ð±Ð°Ð³Ð°ÑÑоÑ
.
ÐовеÑнÑÑе знаÑÐµÐ½Ð½Ñ requestId може бÑÑи викоÑиÑÑане Ð´Ð»Ñ ÑкаÑÑÐ²Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑ:
// ÑкаÑовÑÑмо заплановане Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ ÐºÐ¾Ð»Ð±ÐµÐºÑ
cancelAnimationFrame(requestId);
ФÑнкÑÑÑ callback оÑÑимÑÑ Ð¾Ð´Ð¸Ð½ аÑгÑÐ¼ÐµÐ½Ñ â ÑаÑ, Ñо пÑойÑов з поÑаÑÐºÑ Ð·Ð°Ð²Ð°Ð½ÑÐ°Ð¶ÐµÐ½Ð½Ñ ÑÑоÑÑнки в мÑлÑÑекÑндаÑ
. Цей ÑÐ°Ñ Ñакож можна оÑÑимаÑи за Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑ performance.now().
ÐазвиÑай callback виконÑÑÑÑÑÑ Ð½Ð°Ð¹Ð±Ð»Ð¸Ð¶Ñим ÑаÑом, ÑкÑо ÑÑлÑки пÑоÑеÑÐ¾Ñ Ð½Ðµ пеÑеванÑажений або баÑаÑÐµÑ Ð½Ð¾ÑÑбÑка майже ÑозÑÑдилаÑÑ, або ÑкÑо Ñ ÑнÑий пÑивÑд.
Ðод нижÑе показÑÑ ÑÐ°Ñ Ð¼Ñж пеÑÑими 10 викликами Ð´Ð»Ñ requestAnimationFrame. ÐазвиÑай Ñе 10-20 мÑ:
<script>
let prev = performance.now();
let times = 0;
requestAnimationFrame(function measure(time) {
document.body.insertAdjacentHTML("beforeEnd", Math.floor(time - prev) + " ");
prev = time;
if (times++ < 10) requestAnimationFrame(measure);
})
</script>
СÑÑÑкÑÑÑована анÑмаÑÑÑ
Ð¢ÐµÐ¿ÐµÑ Ð¼Ð¸ можемо зÑобиÑи бÑлÑÑ ÑнÑвеÑÑалÑÐ½Ñ ÑÑнкÑÑÑ Ð°Ð½ÑмаÑÑÑ Ð½Ð° оÑÐ½Ð¾Ð²Ñ requestAnimationFrame:
function animate({timing, draw, duration}) {
let start = performance.now();
requestAnimationFrame(function animate(time) {
// timeFraction змÑнÑÑÑÑÑÑ Ð²Ñд 0 до 1
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// обÑиÑлÑÑмо поÑоÑний ÑÑан анÑмаÑÑÑ
let progress = timing(timeFraction)
draw(progress); // малÑÑмо
if (timeFraction < 1) {
requestAnimationFrame(animate);
}
});
}
ФÑнкÑÑÑ animate пÑÐ¸Ð¹Ð¼Ð°Ñ 3 паÑамеÑÑи, ÑÐºÑ Ð¾Ð¿Ð¸ÑÑÑÑÑ Ð°Ð½ÑмаÑÑÑ:
duration-
ÐагалÑний ÑÐ°Ñ Ð°Ð½ÑмаÑÑÑ. ÐапÑиклад,
1000. timing(timeFraction)-
ФÑнкÑÑÑ ÑаÑÑ, подÑбна до CSS-влаÑÑивоÑÑÑ
transition-timing-function, Ñка оÑÑимÑÑ ÑаÑÑÐºÑ ÑаÑÑ, Ñо минÑв (0на поÑаÑкÑ,1в кÑнÑÑ) Ñ Ð¿Ð¾Ð²ÐµÑÑÐ°Ñ Ð·Ð°Ð²ÐµÑÑÐµÐ½Ð½Ñ Ð°Ð½ÑмаÑÑÑ (Ñкyна кÑивÑй ÐезÑÑ).ÐапÑиклад, лÑнÑйна ÑÑнкÑÑÑ Ð¾Ð·Ð½Ð°ÑаÑ, Ñо анÑмаÑÑÑ ÑÑÐ¸Ð²Ð°Ñ ÑÑвномÑÑно з Ð¾Ð´Ð½Ð°ÐºÐ¾Ð²Ð¾Ñ ÑвидкÑÑÑÑ:
function linear(timeFraction) { return timeFraction; }ÐÑ Ð³ÑаÑÑк:
Це Ñе Ñаме, Ñо й
transition-timing-function: linear. Також ÑÑнÑÑÑÑ ÑÑкавÑÑÑ Ð²Ð°ÑÑанÑи, Ð¿Ð¾ÐºÐ°Ð·Ð°Ð½Ñ Ð½Ð¸Ð¶Ñе. draw(progress)-
ФÑнкÑÑÑ, Ñка пÑÐ¸Ð¹Ð¼Ð°Ñ ÑÑан завеÑÑÐµÐ½Ð½Ñ Ð°Ð½ÑмаÑÑÑ Ñ Ð¼Ð°Ð»ÑÑ ÑÑ. ÐнаÑеннÑ
progress=0ознаÑÐ°Ñ Ð¿Ð¾ÑаÑковий ÑÑан анÑмаÑÑÑ, аprogress=1â кÑнÑевий.Це Ñа ÑÑнкÑÑÑ, Ñка ÑакÑиÑно малÑÑ Ð°Ð½ÑмаÑÑÑ.
Ðона може пеÑемÑÑаÑи елеменÑ:
function draw(progress) { train.style.left = progress + 'px'; }â¦Ðбо ÑобиÑи ÑоÑÑ ÑнÑе, ми можемо анÑмÑваÑи бÑдÑ-Ñо, Ñ Ð±ÑдÑ-Ñкий ÑпоÑÑб.
ÐавайÑе анÑмÑÑмо width елеменÑа вÑд 0 до 100% за Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ Ð½Ð°ÑÐ¾Ñ ÑÑнкÑÑÑ.
ÐаÑиÑнÑÑÑ Ð½Ð° ÐµÐ»ÐµÐ¼ÐµÐ½Ñ Ð´Ð»Ñ Ð´ÐµÐ¼Ð¾Ð½ÑÑÑаÑÑÑ:
function animate({duration, draw, timing}) {
let start = performance.now();
requestAnimationFrame(function animate(time) {
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
let progress = timing(timeFraction)
draw(progress);
if (timeFraction < 1) {
requestAnimationFrame(animate);
}
});
}<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<style>
progress {
width: 5%;
}
</style>
<script src="animate.js"></script>
</head>
<body>
<progress id="elem"></progress>
<script>
elem.onclick = function() {
animate({
duration: 1000,
timing: function(timeFraction) {
return timeFraction;
},
draw: function(progress) {
elem.style.width = progress * 100 + '%';
}
});
};
</script>
</body>
</html>Ðод до неÑ:
animate({
duration: 1000,
timing(timeFraction) {
return timeFraction;
},
draw(progress) {
elem.style.width = progress * 100 + '%';
}
});
Ðа вÑдмÑÐ½Ñ Ð²Ñд CSS-анÑмаÑÑй, ÑÑÑ Ð¼Ð¸ можемо ÑÑвоÑиÑи бÑдÑ-ÑÐºÑ ÑÑнкÑÑÑ ÑаÑÑ Ñ Ð±ÑдÑ-ÑÐºÑ ÑÑнкÑÑÑ Ð¼Ð°Ð»ÑваннÑ. ФÑнкÑÑÑ ÑаÑÑ Ð½Ðµ обмежÑÑÑÑÑÑ ÐºÑивими ÐезÑÑ. Ð draw може не ÑÑлÑки змÑнÑваÑи влаÑÑивоÑÑÑ ÐµÐ»ÐµÐ¼ÐµÐ½ÑÑв, а й ÑÑвоÑÑваÑи Ð½Ð¾Ð²Ñ ÐµÐ»ÐµÐ¼ÐµÐ½Ñи, напÑиклад, Ð´Ð»Ñ Ð°Ð½ÑмаÑÑÑ ÑеÑÑвеÑкÑв або ÑогоÑÑ Ð¿Ð¾Ð´Ñбного.
ФÑнкÑÑÑ ÑаÑÑ (timing functions)
ÐиÑе ми баÑили найпÑоÑÑÑÑÑ, лÑнÑÐ¹Ð½Ñ ÑÑнкÑÑÑ ÑаÑÑ.
ÐавайÑе ÑозглÑнемо ÑнÑÑ Ð²Ð°ÑÑанÑи. ТÑÑ Ð¼Ð¸ вÑдÑвоÑимо анÑмаÑÑÑ ÑÑÑ Ñ Ð· ÑÑзними ÑÑнкÑÑÑми ÑаÑÑ, Ñоб побаÑиÑи, Ñк вони пÑаÑÑÑÑÑ.
СÑепÑÐ½Ñ n
ЯкÑо ми Ñ
оÑемо пÑиÑкоÑиÑи анÑмаÑÑÑ, ми можемо викоÑиÑÑаÑи progress Ñ ÑÑÐµÐ¿ÐµÐ½Ñ n.
ÐапÑиклад, паÑаболÑÑна кÑива:
function quad(timeFraction) {
return Math.pow(timeFraction, 2)
}
ÐÑаÑÑк:
ÐобаÑиÑи в дÑÑ (наÑиÑнÑÑÑ, Ñоб акÑивÑваÑи):
â¦Ðбо кÑбÑÑна кÑива, або навÑÑÑ Ñе бÑлÑÑе знаÑÐµÐ½Ð½Ñ n. ÐбÑлÑÑÐµÐ½Ð½Ñ ÑÑÐµÐ¿ÐµÐ½Ñ Ð¿ÑизводиÑÑ Ð´Ð¾ ÑвидÑого пÑиÑкоÑеннÑ.
ÐÑÑ Ð³ÑаÑÑк progress в ÑÑÐµÐ¿ÐµÐ½Ñ 5:
У дÑÑ:
ÐÑга
ФÑнкÑÑÑ:
function circ(timeFraction) {
return 1 - Math.sin(Math.acos(timeFraction));
}
ÐÑаÑÑк:
Back: ÑÑÑÑлÑба з лÑка
Ð¦Ñ ÑÑнкÑÑÑ Ð²Ð¸ÐºÐ¾Ð½ÑÑ âÑÑÑÑлÑÐ±Ñ Ð· лÑкаâ. СпоÑаÑÐºÑ Ð¼Ð¸ âнаÑÑгÑÑмо ÑÑÑивÑâ, а поÑÑм âÑÑÑÑлÑÑмоâ.
Ðа вÑдмÑÐ½Ñ Ð²Ñд попеÑеднÑÑ
ÑÑнкÑÑй, вона залежиÑÑ Ð²Ñд додаÑкового паÑамеÑÑа x â âкоеÑÑÑÑÑнÑа елаÑÑиÑноÑÑÑâ. Саме ним визнаÑаÑÑÑÑÑ Ð²ÑдÑÑÐ°Ð½Ñ âнаÑÑÐ³Ñ ÑÑÑивиâ.
Ðод:
function back(x, timeFraction) {
return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)
}
ÐÑаÑÑк Ð´Ð»Ñ x = 1.5:.
ÐÐ»Ñ Ð°Ð½ÑмаÑÑÑ Ð¼Ð¸ викоÑиÑÑовÑÑмо його з певним знаÑеннÑм x. ÐÑиклад Ð´Ð»Ñ x = 1.5:
ÐÑдÑкок
УÑвÑÑÑ, Ñо ми кидаÑмо мâÑÑ. ÐÑн Ð¿Ð°Ð´Ð°Ñ Ð²Ð½Ð¸Ð·, поÑÑм кÑлÑка ÑазÑв вÑдÑкакÑÑ Ð½Ð°Ð·Ð°Ð´ Ñ Ð·ÑпинÑÑÑÑÑÑ.
ФÑнкÑÑÑ bounce ÑобиÑÑ Ñе Ñаме, але Ñ Ð·Ð²Ð¾ÑоÑÐ½Ð¾Ð¼Ñ Ð¿Ð¾ÑÑдкÑ: âвÑдÑкакÑваннÑâ поÑинаÑÑÑÑÑ Ð½ÐµÐ³Ð°Ð¹Ð½Ð¾. ÐÐ»Ñ ÑÑого викоÑиÑÑовÑÑÑÑÑÑ Ð´ÐµÐºÑлÑка ÑпеÑÑалÑниÑ
коеÑÑÑÑÑнÑÑв:
function bounce(timeFraction) {
for (let a = 0, b = 1; 1; a += b, b /= 2) {
if (timeFraction >= (7 - 4 * a) / 11) {
return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
}
}
}
У дÑÑ:
ÐлаÑÑиÑна анÑмаÑÑÑ
Ще одна âелаÑÑиÑнаâ ÑÑнкÑÑÑ, Ñка пÑÐ¸Ð¹Ð¼Ð°Ñ Ð´Ð¾Ð´Ð°Ñковий паÑамеÑÑ x Ð´Ð»Ñ âпоÑаÑкового дÑапазонÑâ.
function elastic(x, timeFraction) {
return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction)
}
ÐÑаÑÑк Ð´Ð»Ñ x=1.5:.
У дÑÑ Ð´Ð»Ñ x=1.5:
РевеÑÑ: ease*
ÐÑже, Ñ Ð½Ð°Ñ Ñ Ð½Ð°Ð±ÑÑ ÑÑнкÑÑй ÑаÑÑ. ÐÑ Ð¿ÑÑме викоÑиÑÑÐ°Ð½Ð½Ñ Ð½Ð°Ð·Ð¸Ð²Ð°ÑÑÑÑÑ âeaseInâ.
ÐÐ½Ð¾Ð´Ñ Ð½Ð°Ð¼ поÑÑÑбно показаÑи анÑмаÑÑÑ Ñ Ð·Ð²Ð¾ÑоÑÐ½Ð¾Ð¼Ñ Ð½Ð°Ð¿ÑÑмкÑ. Це ÑобиÑÑÑÑ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ ÑÑанÑÑоÑмаÑÑÑ âeaseOutâ.
easeOut
У ÑÐµÐ¶Ð¸Ð¼Ñ âeaseOutâ ÑÑнкÑÑÑ timing помÑÑаÑÑÑÑÑ Ð² обгоÑÑÐºÑ timingEaseOut:
timingEaseOut(timeFraction) = 1 - timing(1 - timeFraction)
ÐнÑими Ñловами, Ñ Ð½Ð°Ñ Ñ ÑÑнкÑÑÑ âпеÑеÑвоÑеннÑâ makeEaseOut, Ñка беÑе âзвиÑайнÑâ ÑÑнкÑÑÑ ÑаÑÑ Ñ Ð¿Ð¾Ð²ÐµÑÑÐ°Ñ Ð¾Ð±Ð³Ð¾ÑÑÐºÑ Ð½Ð°Ð²ÐºÐ¾Ð»Ð¾ неÑ:
// оÑÑимÑÑ ÑÑнкÑÑÑ ÑаÑÑ, повеÑÑÐ°Ñ Ð¿ÐµÑеÑвоÑений ваÑÑанÑ
function makeEaseOut(timing) {
return function(timeFraction) {
return 1 - timing(1 - timeFraction);
}
}
ÐапÑиклад, ми можемо взÑÑи ÑÑнкÑÑÑ bounce, опиÑÐ°Ð½Ñ Ð²Ð¸Ñе, Ñ Ð·Ð°ÑÑоÑÑваÑи ÑÑ:
let bounceEaseOut = makeEaseOut(bounce);
Ð¢Ð¾Ð´Ñ Ð²ÑдÑкÑк бÑде не на поÑаÑкÑ, а в кÑнÑÑ Ð°Ð½ÑмаÑÑÑ. ÐиглÑÐ´Ð°Ñ Ð½Ð°Ð²ÑÑÑ ÐºÑаÑе:
#brick {
width: 40px;
height: 20px;
background: #EE6B47;
position: relative;
cursor: pointer;
}
#path {
outline: 1px solid #E8C48E;
width: 540px;
height: 20px;
}<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="https://js.cx/libs/animate.js"></script>
</head>
<body>
<div id="path">
<div id="brick"></div>
</div>
<script>
function makeEaseOut(timing) {
return function(timeFraction) {
return 1 - timing(1 - timeFraction);
}
}
function bounce(timeFraction) {
for (let a = 0, b = 1; 1; a += b, b /= 2) {
if (timeFraction >= (7 - 4 * a) / 11) {
return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
}
}
}
let bounceEaseOut = makeEaseOut(bounce);
brick.onclick = function() {
animate({
duration: 3000,
timing: bounceEaseOut,
draw: function(progress) {
brick.style.left = progress * 500 + 'px';
}
});
};
</script>
</body>
</html>ТÑÑ Ð¼Ð¸ баÑимо, Ñк пеÑеÑвоÑÐµÐ½Ð½Ñ Ð·Ð¼ÑнÑÑ Ð¿Ð¾Ð²ÐµÐ´ÑÐ½ÐºÑ ÑÑнкÑÑÑ:
ЯкÑо на поÑаÑÐºÑ Ñ Ð°Ð½ÑмаÑÑйний еÑекÑ, напÑиклад, пÑдÑÑÑибÑÐ²Ð°Ð½Ð½Ñ â вÑн бÑде показаний в кÑнÑÑ.
Ðа гÑаÑÑÐºÑ Ð²Ð¸Ñе звиÑайний bounce Ð¼Ð°Ñ ÑеÑвоний колÑÑ, а easeOut bounce â ÑинÑй.
- ÐвиÑайний bounce â обâÑÐºÑ Ð²ÑдÑкакÑÑ Ð²Ð½Ð¸Ð·Ñ, а поÑÑм в кÑнÑÑ ÑÑзко пÑдÑÑÑибÑÑ Ð´Ð¾Ð³Ð¾Ñи.
- ÐÑÑлÑ
easeOutâ вÑн ÑпоÑаÑÐºÑ ÑÑÑÐ¸Ð±Ð°Ñ Ð²Ð³Ð¾ÑÑ, а поÑÑм пÑдÑÑÑибÑÑ Ñам.
easeInOut
Ðи Ñакож можемо показаÑи еÑÐµÐºÑ Ñк на поÑаÑкÑ, Ñак Ñ Ð² кÑнÑÑ Ð°Ð½ÑмаÑÑÑ. Така ÑÑанÑÑоÑмаÑÑÑ Ð½Ð°Ð·Ð¸Ð²Ð°ÑÑÑÑÑ âeaseInOutâ.
ÐнаÑÑи ÑÑнкÑÑÑ ÑаÑÑ, ми обÑиÑлÑÑмо ÑÑан анÑмаÑÑÑ Ñаким Ñином:
if (timeFraction <= 0.5) { // пеÑÑа половина анÑмаÑÑÑ
return timing(2 * timeFraction) / 2;
} else { // дÑÑга половина анÑмаÑÑÑ
return (2 - timing(2 * (1 - timeFraction))) / 2;
}
Ðод обгоÑÑки:
bounceEaseInOut в дÑÑ:
#brick {
width: 40px;
height: 20px;
background: #EE6B47;
position: relative;
cursor: pointer;
}
#path {
outline: 1px solid #E8C48E;
width: 540px;
height: 20px;
}<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="https://js.cx/libs/animate.js"></script>
</head>
<body>
<div id="path">
<div id="brick"></div>
</div>
<script>
function makeEaseInOut(timing) {
return function(timeFraction) {
if (timeFraction < .5)
return timing(2 * timeFraction) / 2;
else
return (2 - timing(2 * (1 - timeFraction))) / 2;
}
}
function bounce(timeFraction) {
for (let a = 0, b = 1; 1; a += b, b /= 2) {
if (timeFraction >= (7 - 4 * a) / 11) {
return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
}
}
}
let bounceEaseInOut = makeEaseInOut(bounce);
brick.onclick = function() {
animate({
duration: 3000,
timing: bounceEaseInOut,
draw: function(progress) {
brick.style.left = progress * 500 + 'px';
}
});
};
</script>
</body>
</html>ТÑанÑÑоÑмаÑÑÑ âeaseInOutâ обâÑднÑÑ Ð´Ð²Ð° гÑаÑÑки в один: easeIn (звиÑайний) Ð´Ð»Ñ Ð¿ÐµÑÑÐ¾Ñ Ð¿Ð¾Ð»Ð¾Ð²Ð¸Ð½Ð¸ анÑмаÑÑÑ Ñа easeOut (пеÑевеÑнÑÑий) â Ð´Ð»Ñ Ð´ÑÑÐ³Ð¾Ñ ÑаÑÑини.
ÐÑÐµÐºÑ Ð´Ð¾Ð±Ñе видно, ÑкÑо поÑÑвнÑÑи гÑаÑÑки easeIn, easeOut Ñа easeInOut ÑÑнкÑÑÑ ÑаÑÑ circ:
- ЧеÑвоний â звиÑайний ваÑÑанÑ
circ(easeIn). - Ðелений â
easeOut. - СинÑй â
easeInOut.
Як баÑимо, гÑаÑÑк пеÑÑÐ¾Ñ Ð¿Ð¾Ð»Ð¾Ð²Ð¸Ð½Ð¸ анÑмаÑÑÑ â Ñе зменÑений easeIn, а дÑÑÐ³Ð¾Ñ Ð¿Ð¾Ð»Ð¾Ð²Ð¸Ð½Ð¸ â зменÑений easeOut. Ð ÑезÑлÑÑаÑÑ Ð°Ð½ÑмаÑÑÑ Ð¿Ð¾ÑинаÑÑÑÑÑ Ñ Ð·Ð°ÐºÑнÑÑÑÑÑÑÑ Ð· однаковим еÑекÑом.
ÐÑлÑÑ ÑÑкава ÑÑнкÑÑÑ âdrawâ
ÐамÑÑÑÑ Ð¿ÐµÑемÑÑÐµÐ½Ð½Ñ ÐµÐ»ÐµÐ¼ÐµÐ½Ñа ми можемо зÑобиÑи ÑоÑÑ ÑнÑе. ÐÑе, Ñо нам поÑÑÑбно, Ñе напиÑаÑи пÑавилÑÐ½Ñ ÑÑнкÑÑÑ draw.
ÐÑÑ Ð°Ð½Ñмований ÑекÑÑ, Ñо âпÑдÑÑÑибÑÑâ пÑи набоÑÑ:
textarea {
display: block;
border: 1px solid #BBB;
color: #444;
font-size: 110%;
}
button {
margin-top: 10px;
}<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<script src="https://js.cx/libs/animate.js"></script>
</head>
<body>
<textarea id="textExample" rows="5" cols="60">He took his vorpal sword in hand:
Long time the manxome foe he soughtâ
So rested he by the Tumtum tree,
And stood awhile in thought.
</textarea>
<button onclick="animateText(textExample)">Run the animated typing!</button>
<script>
function animateText(textArea) {
let text = textArea.value;
let to = text.length,
from = 0;
animate({
duration: 5000,
timing: bounce,
draw: function(progress) {
let result = (to - from) * progress + from;
textArea.value = text.slice(0, Math.ceil(result))
}
});
}
function bounce(timeFraction) {
for (let a = 0, b = 1; 1; a += b, b /= 2) {
if (timeFraction >= (7 - 4 * a) / 11) {
return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
}
}
}
</script>
</body>
</html>ÐÑдÑÑмки
ÐнÑмаÑÑÑ, з Ñкими CSS не може впоÑаÑиÑÑ, або ÑÑ, Ñо поÑÑебÑÑÑÑ ÑÑÑкого конÑÑолÑ, можÑÑÑ Ð±ÑÑи ÑеалÑÐ·Ð¾Ð²Ð°Ð½Ñ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ JavaScript. JavaScript-анÑмаÑÑÑ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ð° бÑÑи ÑеалÑзована ÑеÑез requestAnimationFrame. Цей вбÑдований меÑод дозволÑÑ Ð¿ÐµÑедаваÑи колбек, Ñо бÑде виконÑваÑиÑÑ, коли бÑаÑÐ·ÐµÑ Ð±Ñде гоÑÑваÑиÑÑ Ð´Ð¾ пеÑемалÑовÑваннÑ. ÐазвиÑай Ñе вÑдбÑваÑÑÑÑÑ Ð´Ñже Ñвидко, але ÑоÑний ÑÐ°Ñ Ð·Ð°Ð»ÐµÐ¶Ð¸ÑÑ Ð²Ñд бÑаÑзеÑа.
Ðоли ÑÑоÑÑнка Ð·Ð½Ð°Ñ Ð¾Ð´Ð¸ÑÑÑÑ Ñ ÑÐ¾Ð½Ð¾Ð²Ð¾Ð¼Ñ ÑежимÑ, пеÑемалÑовÑÐ²Ð°Ð½Ð½Ñ Ð²Ð·Ð°Ð³Ð°Ð»Ñ Ð½Ðµ вÑдбÑваÑÑÑÑÑ, ÑÐ¾Ð¼Ñ ÐºÐ¾Ð»Ð±ÐµÐº не запÑÑÑиÑÑÑÑ: анÑмаÑÑÑ Ð±Ñде пÑизÑпинена Ñ Ð½Ðµ бÑде ÑпоживаÑи ÑеÑÑÑÑи. Ð Ñе ÑÑдово.
ÐÑÑ Ð´Ð¾Ð¿Ð¾Ð¼Ñжна ÑÑнкÑÑÑ animate Ð´Ð»Ñ Ð½Ð°Ð»Ð°ÑÑÑÐ²Ð°Ð½Ð½Ñ Ð±ÑлÑÑоÑÑÑ Ð°Ð½ÑмаÑÑй:
function animate({timing, draw, duration}) {
let start = performance.now();
requestAnimationFrame(function animate(time) {
// timeFraction змÑнÑÑÑÑÑÑ Ð²Ñд 0 до 1
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// обÑиÑлÑÑмо поÑоÑний ÑÑан анÑмаÑÑÑ
let progress = timing(timeFraction);
draw(progress); // малÑÑмо
if (timeFraction < 1) {
requestAnimationFrame(animate);
}
});
}
ÐаÑамеÑÑи:
durationâ загалÑний ÑÐ°Ñ Ð°Ð½ÑмаÑÑÑ Ñ Ð¼ÑлÑÑекÑÐ½Ð´Ð°Ñ .timingâ ÑÑнкÑÑÑ Ð´Ð»Ñ Ð¾Ð±ÑиÑÐ»ÐµÐ½Ð½Ñ Ð¿ÑогÑеÑÑ Ð°Ð½ÑмаÑÑÑ. ÐÑÑимÑÑ Ð¿ÑомÑжок ÑаÑÑ Ð²Ñд 0 до 1, повеÑÑÐ°Ñ Ð¿ÑогÑÐµÑ Ð°Ð½ÑмаÑÑÑ, зазвиÑай вÑд 0 до 1.drawâ ÑÑнкÑÑÑ Ð´Ð»Ñ Ð¼Ð°Ð»ÑÐ²Ð°Ð½Ð½Ñ Ð°Ð½ÑмаÑÑÑ.
ÐвиÑайно, ми могли б покÑаÑиÑи ÑÑ, додаÑи бÑлÑÑе навоÑоÑÑв, але JavaScript-анÑмаÑÑÑ Ð½Ðµ заÑÑоÑовÑÑÑÑÑÑ ÑоднÑ. Ðони викоÑиÑÑовÑÑÑÑÑÑ Ð´Ð»Ñ Ñого, Ñоб зÑобиÑи ÑоÑÑ ÑÑкаве Ñ Ð½ÐµÑÑандаÑÑне. Ð¢Ð¾Ð¼Ñ Ð²Ð°ÑÑо додаваÑи ÑÑнкÑÑÑ, ÑÐºÑ Ð²Ð°Ð¼ поÑÑÑбнÑ, ÑодÑ, коли вони вам поÑÑÑбнÑ.
Ð JavaScript анÑмаÑÑÑ Ð¼Ð¾Ð¶ÑÑÑ Ð²Ð¸ÐºÐ¾ÑиÑÑовÑваÑи бÑдÑ-ÑÐºÑ ÑÑнкÑÑÑ ÑаÑÑ. Ðи ÑозглÑнÑли багаÑо пÑикладÑв Ñ ÑÑанÑÑоÑмаÑÑй, Ñоб зÑобиÑи ÑÑ Ñе бÑлÑÑ ÑнÑвеÑÑалÑними. Ðа вÑдмÑÐ½Ñ Ð²Ñд CSS, ÑÑÑ Ð¼Ð¸ не обмежÑÑмоÑÑ ÐºÑивими ÐезÑÑ.
Те ж Ñаме ÑÑоÑÑÑÑÑÑÑ Ñ ÑÑнкÑÑÑ draw: ми можемо анÑмÑваÑи бÑдÑ-Ñо, а не лиÑе влаÑÑивоÑÑÑ CSS.
ÐоменÑаÑÑ
<code>, Ð´Ð»Ñ ÐºÑлÑÐºÐ¾Ñ ÑÑдкÑв â обгоÑнÑÑÑ ÑÑ Ñегом<pre>, Ð´Ð»Ñ Ð¿Ð¾Ð½Ð°Ð´ 10 ÑÑдкÑв â викоÑиÑÑовÑйÑе пÑÑоÑниÑÑ (plnkr, jsbin, codepenâ¦)