С помоÑÑÑ 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); // изменÑÑÑ Ð½Ð° 2px каждÑе 20ms, ÑÑо около 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);
ÐÑи незавиÑимÑе пеÑеÑиÑовки лÑÑÑе ÑгÑÑппиÑоваÑÑ Ð²Ð¼ÐµÑÑе, Ñогда они бÑдÑÑ Ð»ÐµÐ³Ñе Ð´Ð»Ñ Ð±ÑаÑзеÑа, а знаÑÐ¸Ñ â не гÑÑзиÑÑ Ð¿ÑоÑеÑÑÐ¾Ñ Ð¸ более плавно вÑглÑдеÑÑ.
СÑÑеÑÑвÑÐµÑ ÐµÑÑ Ð¾Ð´Ð½Ð° веÑÑ, пÑо коÑоÑÑÑ Ð½Ð°Ð´Ð¾ помниÑÑ: когда CPU пеÑегÑÑжен или еÑÑÑ Ð´ÑÑгие пÑиÑÐ¸Ð½Ñ Ð´ÐµÐ»Ð°ÑÑ Ð¿ÐµÑеÑиÑÐ¾Ð²ÐºÑ Ñеже (напÑимеÑ, когда вкладка бÑаÑзеÑа ÑкÑÑÑа), нам не ÑледÑÐµÑ Ð´ÐµÐ»Ð°ÑÑ ÐµÑ ÐºÐ°Ð¶Ð´Ñе 20ms.
Ðо как нам ÑзнаÑÑ Ð¾Ð± ÑÑом в JavaScript? СпеÑиÑикаÑÐ¸Ñ Animation timing опиÑÑÐ²Ð°ÐµÑ ÑÑнкÑÐ¸Ñ requestAnimationFrame, коÑоÑÐ°Ñ ÑеÑÐ°ÐµÑ Ð²Ñе опиÑаннÑе пÑÐ¾Ð±Ð»ÐµÐ¼Ñ Ð¸ Ð´ÐµÐ»Ð°ÐµÑ Ð´Ð°Ð¶Ðµ болÑÑе.
СинÑакÑиÑ:
let requestId = requestAnimationFrame(callback)
Такой вÑзов планиÑÑÐµÑ Ð·Ð°Ð¿ÑÑк ÑÑнкÑии callback на ближайÑее вÑемÑ, когда бÑаÑÐ·ÐµÑ ÑоÑÑÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñм оÑÑÑеÑÑвиÑÑ Ð°Ð½Ð¸Ð¼Ð°ÑиÑ.
ÐÑли в callback пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ðµ ÑлеменÑа, Ñогда оно бÑÐ´ÐµÑ ÑгÑÑппиÑовано Ñ Ð´ÑÑгими requestAnimationFrame и CSS-анимаÑиÑми. Таким обÑазом бÑаÑÐ·ÐµÑ Ð²ÑÐ¿Ð¾Ð»Ð½Ð¸Ñ Ð¾Ð´Ð¸Ð½ геомеÑÑиÑеÑкий пеÑеÑÑÑÑ Ð¸ оÑÑиÑовкÑ, вмеÑÑо неÑколÑкиÑ
.
ÐнаÑение requestId Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¸ÑполÑзовано Ð´Ð»Ñ Ð¾ÑÐ¼ÐµÐ½Ñ Ð°Ð½Ð¸Ð¼Ð°Ñии:
// оÑмена запланиÑованного запÑÑка callback
cancelAnimationFrame(requestId);
ФÑнкÑÐ¸Ñ callback Ð¸Ð¼ÐµÐµÑ Ð¾Ð´Ð¸Ð½ аÑгÑÐ¼ÐµÐ½Ñ â вÑÐµÐ¼Ñ Ð¿ÑоÑедÑее Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñа наÑала загÑÑзки ÑÑÑаниÑÑ Ð² миллиÑекÑндаÑ
. ÐÑо знаÑение Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿Ð¾Ð»ÑÑено Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ Ð²Ñзова performance.now().
Ðак пÑавило, callback запÑÑкаеÑÑÑ Ð¾ÑÐµÐ½Ñ ÑкоÑо, еÑли ÑолÑко не пеÑегÑÑжен CPU или не ÑазÑÑжена баÑаÑÐµÑ Ð½Ð¾ÑÑбÑка, или Ñ Ð±ÑаÑзеÑа Ð½ÐµÑ ÐºÐ°ÐºÐ¾Ð¹-Ñо еÑÑ Ð¿ÑиÑÐ¸Ð½Ñ Ð·Ð°Ð¼ÐµÐ´Ð»Ð¸ÑÑÑÑ.
Ðод ниже показÑÐ²Ð°ÐµÑ Ð²ÑÐµÐ¼Ñ Ð¼ÐµÐ¶Ð´Ñ Ð¿ÐµÑвÑми 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 Ð¸Ð¼ÐµÐµÑ ÑÑи аÑгÑменÑа, коÑоÑÑе опиÑÑваÑÑ Ð°Ð½Ð¸Ð¼Ð°ÑиÑ:
duration-
ÐÑодолжиÑелÑноÑÑÑ Ð°Ð½Ð¸Ð¼Ð°Ñии. ÐапÑимеÑ,
1000. timing(timeFraction)-
ФÑнкÑÐ¸Ñ ÑаÑÑÑÑа вÑемени, как CSS-ÑвойÑÑво
transition-timing-function, коÑоÑÐ°Ñ Ð±ÑÐ´ÐµÑ Ð²ÑÑиÑлÑÑÑ Ð¿ÑогÑеÑÑ Ð°Ð½Ð¸Ð¼Ð°Ñии (как оÑÑyÑ ÐºÑивой ÐезÑе) в завиÑимоÑÑи Ð¾Ñ Ð¿ÑоÑедÑего вÑемени (0в наÑале,1в конÑе).ÐапÑимеÑ, Ð»Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð·Ð½Ð°ÑиÑ, ÑÑо анимаÑÐ¸Ñ Ð¸Ð´ÑÑ Ñ Ð¾Ð´Ð½Ð¾Ð¹ и Ñой же ÑкоÑоÑÑÑÑ:
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 Ð¼Ð¾Ð¶ÐµÑ Ð¼ÐµÐ½ÑÑÑ Ð½Ðµ ÑолÑко ÑвойÑÑва, но и ÑоздаваÑÑ Ð½Ð¾Ð²Ñе ÑлеменÑÑ (напÑимеÑ, Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð°Ð½Ð¸Ð¼Ð°Ñии ÑейеÑвеÑка).
ФÑнкÑии ÑаÑÑÑÑа вÑемени
ÐÑ Ñже ÑаÑÑмоÑÑели ÑамÑй пÑоÑÑой пÑÐ¸Ð¼ÐµÑ Ð»Ð¸Ð½ÐµÐ¹Ð½Ð¾Ð¹ ÑÑнкÑии ÑаÑÑÑÑа вÑемени вÑÑе.
ÐавайÑе поÑмоÑÑим дÑÑгие. ÐÑ Ð¿Ð¾Ð¿ÑобÑем вÑполниÑÑ Ð°Ð½Ð¸Ð¼Ð°Ñии Ñ ÑазнÑми ÑÑнкÑиÑми ÑаÑÑÑÑа вÑемени, ÑÑÐ¾Ð±Ñ Ð¿Ð¾ÑмоÑÑеÑÑ ÐºÐ°Ðº они ÑабоÑаÑÑ.
СÑÐµÐ¿ÐµÐ½Ñ n
ÐÑли Ð¼Ñ Ñ
оÑим ÑÑкоÑиÑÑ Ð°Ð½Ð¸Ð¼Ð°ÑиÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ возвеÑÑи progress в ÑÑÐµÐ¿ÐµÐ½Ñ n.
ÐапÑимеÑ, паÑаболиÑеÑÐºÐ°Ñ ÐºÑиваÑ:
function quad(timeFraction) {
return Math.pow(timeFraction, 2)
}
ÐÑаÑик:
ÐоÑмоÑÑим в дейÑÑвии (нажмиÑе Ð´Ð»Ñ Ð°ÐºÑиваÑии):
â¦Ðли кÑбиÑеÑÐºÐ°Ñ ÐºÑиваÑ, или лÑбой дÑÑгой множиÑÐµÐ»Ñ n. ÐовÑÑение ÑÑепени ÑвелиÑÐ¸Ð²Ð°ÐµÑ ÑкоÑоÑÑÑ Ð°Ð½Ð¸Ð¼Ð°Ñии.
ÐÐ¾Ñ Ð³ÑаÑик Ð´Ð»Ñ ÑÑнкÑии progress в ÑÑепени 5:
РдейÑÑвии:
ÐÑга
ФÑнкÑиÑ:
function circ(timeFraction) {
return 1 - Math.sin(Math.acos(timeFraction));
}
ÐÑаÑик:
ÐбÑаÑно: вÑÑÑÑел из лÑка
ÐÑа ÑÑнкÑÐ¸Ñ ÑовеÑÑÐ°ÐµÑ Â«Ð²ÑÑÑÑел из лÑка». РнаÑале «наÑÑгиваеÑÑÑ ÑеÑива», а заÑем «вÑÑÑÑел».
РоÑлиÑие Ð¾Ñ Ð¿ÑедÑдÑÑей ÑÑнкÑии, ÑепеÑÑ Ð²ÑÑ Ð·Ð°Ð²Ð¸ÑÐ¸Ñ Ð¾Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸ÑелÑного паÑамеÑÑа 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>Ðиже Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑвидеÑÑ, как ÑÑанÑÑоÑмаÑии изменÑÑÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ ÑÑнкÑии:
ÐÑли ÑанÑÑе анимаÑионнÑй ÑÑÑекÑ, Ñакой как оÑÑкоки, бÑл в наÑале, Ñо поÑле ÑÑанÑÑоÑмаÑии он бÑÐ´ÐµÑ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð½ в конÑе.
Ðа гÑаÑике вÑÑе кÑаÑнÑм ÑвеÑом обознаÑена обÑÑÐ½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð¸ Ñиним â поÑле easeOut.
- ÐбÑÑнÑй ÑкаÑок â обÑÐµÐºÑ ÑнаÑала медленно ÑкаÑÐµÑ Ð²Ð½Ð¸Ð·Ñ, а заÑем Ñезко подпÑÑÐ³Ð¸Ð²Ð°ÐµÑ Ð²Ð²ÐµÑÑ .
- ÐбÑаÑнÑй
easeOutâ обÑÐµÐºÑ Ð²Ð½Ð°Ñале пÑÑÐ³Ð°ÐµÑ Ð²Ð²ÐµÑÑ , и заÑем ÑкаÑÐµÑ Ñам.
easeInOut
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ пÑимениÑÑ ÑÑÑÐµÐºÑ Ð´Ð²Ð°Ð¶Ð´Ñ â в наÑале и конÑе анимаÑии. Ð¢Ð°ÐºÐ°Ñ ÑÑанÑÑоÑмаÑÐ¸Ñ Ð½Ð°Ð·ÑваеÑÑÑ Â«easeInOut».
ÐÐ»Ñ ÑÑнкÑии ÑаÑÑÑÑа вÑемени, анимаÑÐ¸Ñ Ð±ÑÐ´ÐµÑ Ð²ÑÑиÑлÑÑÑÑÑ ÑледÑÑÑим обÑазом:
if (timeFraction <= 0.5) { // пеÑÐ²Ð°Ñ Ð¿Ð¾Ð»Ð¾Ð²Ð¸Ð½Ð° анимаÑии
return timing(2 * timeFraction) / 2;
} else { // вÑоÑÐ°Ñ Ð¿Ð¾Ð»Ð¾Ð²Ð¸Ð½Ð° анимаÑии
return (2 - timing(2 * (1 - timeFraction))) / 2;
}
Ðод ÑÑнкÑии-обÑÑÑки:
function makeEaseInOut(timing) {
return function(timeFraction) {
if (timeFraction < .5)
return timing(2 * timeFraction) / 2;
else
return (2 - timing(2 * (1 - timeFraction))) / 2;
}
}
bounceEaseInOut = makeEaseInOut(bounce);
РдейÑÑвии, 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">Ðо взÑл он меÑ, и взÑл он ÑиÑ,
ÐÑÑокиÑ
полон дyм.
РглÑÑÐ¾Ð±Ñ Ð¿ÑÑÑ ÐµÐ³Ð¾ лежиÑ
Ðод деÑево ТÑмÑÑм.
</textarea>
<button onclick="animateText(textExample)">ÐапÑÑÑиÑÑ Ð°Ð½Ð¸Ð¼Ð°ÑÐ¸Ñ Ð½Ð°Ð±Ð¾Ñа ÑекÑÑа!</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>ÐÑого
JavaScript Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð¼Ð¾ÑÑ Ð² ÑеÑ
ÑлÑÑаÑÑ
, когда CSS не ÑпÑавлÑеÑÑÑ Ð¸Ð»Ð¸ нÑжен жÑÑÑкий конÑÑÐ¾Ð»Ñ Ð½Ð°Ð´ анимаÑией. 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â¦)