SpriteJS的動(dòng)畫是基于Ti" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁(yè) > 營(yíng)銷資訊 > 網(wǎng)站運(yùn)營(yíng) > 時(shí)間之美 —— SpriteJS 的原創(chuàng)時(shí)間軸設(shè)計(jì)

時(shí)間之美 —— SpriteJS 的原創(chuàng)時(shí)間軸設(shè)計(jì)

時(shí)間:2023-07-21 20:21:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)

時(shí)間:2023-07-21 20:21:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)

時(shí)間之美 —— SpriteJS 的原創(chuàng)時(shí)間軸設(shè)計(jì):

SpriteJS提供與標(biāo)準(zhǔn)的Web Animation API相一致的動(dòng)畫API,使得熟悉css3 animation和Web Animation API的同學(xué)能快速上手實(shí)現(xiàn)復(fù)雜的動(dòng)畫。

SpriteJS動(dòng)畫

SpriteJS的動(dòng)畫是基于Timeline的,改變Layer的Timeline能夠批量改變精靈的動(dòng)畫行為,包括改變速度、方向和暫停動(dòng)畫,或者跳轉(zhuǎn)到特定時(shí)間。

批量改變精靈元素的動(dòng)畫

手動(dòng)控制動(dòng)畫的時(shí)間軸

SpriteJS動(dòng)畫這種靈活“控制時(shí)間”的能力是由一個(gè)底層庫(kù)賦予的,這個(gè)底層庫(kù)是sprite-timeline

sprite-timeline

sprite-timeline是一個(gè)控制時(shí)間的底層庫(kù),在之前我寫過(guò)一篇文章簡(jiǎn)單介紹過(guò)它。今天我們?cè)僭敿?xì)挖掘一下這個(gè)庫(kù)的設(shè)計(jì)思路和用法。

sprite-timeline可以單獨(dú)使用

sprite-timeline可以單獨(dú)使用

sprite-timeline可以單獨(dú)使用

重要的事情說(shuō)三遍。

說(shuō)sprite-timeline前,我們先來(lái)看一下不使用timeline的原生JS動(dòng)畫怎么實(shí)現(xiàn):

例子

let startTime = Date.now(), T = 2000requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / T ball.style.transform = `rotate(${360 * p}deg)` requestAnimationFrame(update)})上面的代碼我們實(shí)現(xiàn)一個(gè)小球圍繞圓心做勻速圓周運(yùn)動(dòng)運(yùn)動(dòng)的動(dòng)畫,這是一個(gè)基于時(shí)間的動(dòng)畫,我們根據(jù)當(dāng)前時(shí)間改變?cè)氐膖ransform樣式,通過(guò)requestAnimationFram來(lái)更新,這樣就實(shí)現(xiàn)了。

在這個(gè)例子中,如果我們要把小球的速度提高一倍,那么我們要做的也很簡(jiǎn)單,就是把周期修改為原先的一半,即把T=2000改成T=1000

例子

let startTime = Date.now(), T = 1000requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / T ball.style.transform = `rotate(${360 * p}deg)` requestAnimationFrame(update)})小球運(yùn)動(dòng)方向是順時(shí)針旋轉(zhuǎn),如果我們要讓小球逆時(shí)針旋轉(zhuǎn),我們可以改變r(jià)otate的符號(hào):

例子

let startTime = Date.now(), T = 1000requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / T ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})如果頁(yè)面上還有其他動(dòng)畫對(duì)象,我們同樣可以添加進(jìn)來(lái),比如我們?cè)谛∏蚺赃吿砑右粋€(gè)顏色從紅色漸變到藍(lán)色的方塊:

例子

const startTime = Date.now()requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / 2000 ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / 5000 p = Math.min(p, 1.0) let red = Math.round(255 * (1 - p)), blue = Math.round(255 * p) block.style.backgroundColor = `rgb(${red},0,${blue})` if(p < 1.0) { requestAnimationFrame(update) }})現(xiàn)在我們要改變動(dòng)畫的速度或者方向就稍微麻煩一點(diǎn)點(diǎn)了,因?yàn)槲覀冇袃蓚€(gè)獨(dú)立的動(dòng)畫要修改參數(shù)。但是如果有類似于將所有動(dòng)畫速度提升到原來(lái)的2倍這樣的需求時(shí),我們有一個(gè)最簡(jiǎn)單的方法,那就是修改時(shí)間流動(dòng)的速度。

有一些動(dòng)畫庫(kù)可以實(shí)現(xiàn)上面的要求,改變動(dòng)畫的時(shí)間軸,而sprite-timeline應(yīng)該是其中最簡(jiǎn)單的庫(kù)之一,而且sprite-timeline只是純粹地控制時(shí)間,不關(guān)心動(dòng)畫的其他部分,也因此,sprite-timeline的使用場(chǎng)景不僅限于動(dòng)畫。

讓我們先用sprite-timeline改寫上面的例子:

例子

const timeline = new Timeline()requestAnimationFrame(function update(){ let p = timeline.currentTime / 2000 ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})requestAnimationFrame(function update(){ let p = timeline.currentTime / 5000 p = Math.min(p, 1.0) let red = Math.round(255 * (1 - p)), blue = Math.round(255 * p) block.style.backgroundColor = `rgb(${red},0,${blue})` if(p < 1.0) { requestAnimationFrame(update) }})改寫后的代碼跟之前的代碼相比,幾乎沒(méi)有什么不同,唯一的不同之處是原先的Data.now() - startTime被更加簡(jiǎn)單的timeline.currentTime所取代了。

創(chuàng)建 Timeline 對(duì)象

Timeline對(duì)象的構(gòu)造器有兩個(gè)參數(shù),分別是playbackRate和originTime,它們的默認(rèn)值分別為1.0和0。playbackRate決定時(shí)間流逝的方向,正數(shù)是正向,負(fù)數(shù)是負(fù)向,0表示時(shí)間停止。originTime定義了時(shí)間的原點(diǎn)位置,比如originTime = 10000,表示時(shí)間的原點(diǎn)在當(dāng)前時(shí)間之后10秒鐘,那么該Timeline創(chuàng)建時(shí)的currentTime就是-originTime,即-10秒。

比如,我們創(chuàng)建一個(gè)Timeline,originTime是2000,playbackRate是0.5,那么:

例子

const timeline = new Timeline({playbackRate: 0.5, originTime: 2000})console.log(Math.round(timeline.currentTime / 100) / 10)setInterval(() => { console.log(Math.round(timeline.currentTime / 100) / 10)}, 1000)我們會(huì)看到實(shí)際上每隔1秒鐘,timeline的currentTime變化0.5秒,而且時(shí)間從-2秒開(kāi)始變化。

我們稍微改一下上面的那個(gè)動(dòng)畫的例子:

例子

const timeline = new Timeline({playbackRate: 0.5, originTime: 1500})requestAnimationFrame(function update(){ let p = timeline.currentTime / 2000 ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})requestAnimationFrame(function update(){ let p = timeline.currentTime / 5000 p = Math.min(p, 1.0) let red = Math.round(255 * (1 - p)), blue = Math.round(255 * p) if(p > 0) { block.style.backgroundColor = `rgb(${red},0,${blue})` } if(p < 1.0) { requestAnimationFrame(update) }})我們會(huì)看到,經(jīng)過(guò)這樣的修改之后,兩個(gè)元素動(dòng)畫的速度都減半,而且方塊在3秒鐘(真實(shí)世界的3秒,timeline世界的1.5秒)之后才開(kāi)始改變顏色。

timeline的奇妙之處還在于我們可以動(dòng)態(tài)改變它的currentTime和playbackRate,從而立即改變動(dòng)畫的行為,比如我們可以不改變p,僅通過(guò)改變currentTime來(lái)控制動(dòng)畫循環(huán)播放:

例子

const timeline = new Timeline({playbackRate: 2.0, originTime: 1500})requestAnimationFrame(function update(){ let p = timeline.currentTime / 2000 ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})requestAnimationFrame(function update(){ let p = timeline.currentTime / 6000 p = Math.min(p, 1.0) let red = Math.round(255 * (1 - p)), blue = Math.round(255 * p) if(p > 0) { block.style.backgroundColor = `rgb(${red},0,${blue})` } if(p >= 1.0) { timeline.currentTime = 0 } requestAnimationFrame(update)})我們可以改變playbackRate,動(dòng)畫速度(和方向)會(huì)立即改變,比如下面這個(gè)例子,我們把鼠標(biāo)移入右邊的方塊時(shí),動(dòng)畫的速度和方向都會(huì)立即改變:

例子

const timeline = new Timeline({playbackRate: 1.0, originTime: 1500})requestAnimationFrame(function update(){ let p = timeline.currentTime / 2000 ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})requestAnimationFrame(function update(){ let p = timeline.currentTime / 6000 p = Math.min(p, 1.0) let red = Math.round(255 * (1 - p)), blue = Math.round(255 * p) if(p > 0) { block.style.backgroundColor = `rgb(${red},0,${blue})` } if(p >= 1.0) { timeline.currentTime = 0 } else if(p < 0) { timeline.currentTime = 6000 } requestAnimationFrame(update)})block.addEventListener('mouseenter', (evt) => { timeline.playbackRate = -2.0})block.addEventListener('mouseleave', (evt) => { timeline.playbackRate = 1.0})

timeline timer

除了控制動(dòng)畫,我們可以直接給timeline設(shè)置timer,timeline自身支持setTimeout和setInterval,例如:

例子

const timeline = new Timeline({playbackRate: 2.0})setInterval(() => { globalTime.innerHTML = parseInt(globalTime.innerHTML, 10) + 1}, 1000)timeline.setInterval(() => { timelineTime.innerHTML = parseInt(timelineTime.innerHTML, 10) + 1}, 1000)這個(gè)例子timeline的setInterval實(shí)際觸發(fā)時(shí)間是500毫秒(1000毫秒/2.0)

與global的timer不同的是,timeline的Timer是有方向的,比如:

例子

const t1 = new Timeline({playbackRate: 1.0}), t2 = new Timeline({playbackRate: -1.0})t1.setTimeout(() => { console.log(t1.currentTime)}, 1000)t2.setTimeout(() => { console.log(t2.currentTime)}, -1000)t1和t2的timer幾乎同時(shí)觸發(fā),t1時(shí)間方向是正向,t2時(shí)間方向是負(fù)向。

如果我們把t1和t2的方向交換:

例子

const t1 = new Timeline({playbackRate: -1.0}), t2 = new Timeline({playbackRate: 1.0})t1.setTimeout(() => { console.log(t1.currentTime)}, 1000)t2.setTimeout(() => { console.log(t2.currentTime)}, -1000)兩個(gè)timer都立即觸發(fā),因?yàn)樗鼈兊?b>當(dāng)前時(shí)間都在timer的時(shí)間之后。

有時(shí)候,我們希望在設(shè)置定時(shí)器的時(shí)候不觸發(fā)在currentTime之前的那些定時(shí)器,我們可以給timer添加一個(gè)heading:false的配置:

例子

const t1 = new Timeline({playbackRate: -1.0}), t2 = new Timeline({playbackRate: 1.0})t1.setTimeout(() => { console.log(t1.currentTime)}, {delay: 1000, heading: false})t2.setTimeout(() => { console.log(t2.currentTime)}, -1000)上面的例子t1的timer不會(huì)觸發(fā),t2的timer會(huì)觸發(fā),因?yàn)閠1設(shè)置了heading:false。

我們可以動(dòng)態(tài)改變t1的playbackRate讓時(shí)間方向回頭,這樣還能夠觸發(fā)t1:

例子

const t1 = new Timeline({playbackRate: -1.0}), t2 = new Timeline({playbackRate: 1.0})t1.setTimeout(() => { console.log(t1.currentTime)}, {delay: 1000, heading: false})t2.setTimeout(() => { console.log(t2.currentTime) t1.playbackRate = 1.0}, 1000)

timeline fork

timeline 還有一個(gè)神奇的功能是可以fork

例子

const timeline = new Timeline(), forkedTimeline1 = timeline.fork(), forkedTimeline2 = timeline.fork()const startTime = Date.now()const globalTime = document.getElementById('globalTime'), localTime = document.getElementById('localTime'), forkedTime1 = document.getElementById('forkedTime1'), forkedTime2 = document.getElementById('forkedTime2')forkedTimeline1.playbackRate = 2.0forkedTimeline2.playbackRate = -2.0setInterval(() => { globalTime.innerHTML = `${Math.round((Date.now() - startTime) / 1000)}s` localTime.innerHTML = `${Math.round(timeline.currentTime / 1000)}s` forkedTime1.innerHTML = `${Math.round(forkedTimeline1.currentTime / 1000)}s` forkedTime2.innerHTML = `${Math.round(forkedTimeline2.currentTime / 1000)}s`}, 100)上面的例子里,forkedTimeline1和forkedTimeline2都是從localTimeline分支出來(lái)的,所以我們改變localTime,就能同時(shí)改變forkedTimeline1和forkedTimeline2,比如現(xiàn)在forkedTimeline1的時(shí)間線是正向,forkedTimeline2的時(shí)間線是負(fù)向,我們只要把localTimeline的playbackRate設(shè)置為-1,那么forkedTimeline1時(shí)間線變成負(fù)向,forkedTimeline2的時(shí)間線則變成正向。

再看一個(gè)例子:

例子

let timeline = new Timeline()function count(el, timeline, p = Infinity) { timeline.setInterval(() => { el.innerHTML = Math.round(timeline.currentTime / 1000) % p }, 1000)}count(ball0, timeline)count(ball1, timeline.fork({playbackRate: 10}), 10)count(ball2, timeline.fork({playbackRate: 100}), 10)

timeline與sprite animation

因?yàn)镾priteJS的Layer有自己的timeline,而Layer上的sprite在運(yùn)行動(dòng)畫的時(shí)候,會(huì)fork Layer的timeline,所以這樣就實(shí)現(xiàn)了layer動(dòng)畫的統(tǒng)一控制。

具體再看一下這個(gè)例子,因?yàn)榇a較長(zhǎng),就不全貼出來(lái)了。

關(guān)鍵代碼

const [speedupBtn, slowdownBtn, pauseBtn, playBtn] = ['Speed up', 'Slow down', 'Pause', 'Play'].map((type, i) => { const button = new Button(type) button.attr({ anchor: [0.5, 0.5], pos: [1300, 400 + 150 * i], size: [170, 50], font: '36px Arial', lineHeight: 50, textAlign: 'center', color: '#00e15e', border: [3, '#00e15e'], borderRadius: 25, padding: 30, }) bglayer.appendChild(button) return button }) speedupBtn.on('click', (evt) => { fglayer.timeline.playbackRate += 0.2 }) slowdownBtn.on('click', (evt) => { fglayer.timeline.playbackRate -= 0.2 }) pauseBtn.on('click', (evt) => { fglayer.timeline.playbackRate = 0 }) playBtn.on('click', (evt) => { fglayer.timeline.playbackRate = 1 })以上就是關(guān)于sprite-timeline的關(guān)鍵內(nèi)容,其他還有一些有趣的功能,在這里沒(méi)有列出,比如timeline的“熵”(entropy),以及entropy相關(guān)的用法,有興趣的同學(xué)可以關(guān)注GitHub repo自行研究。

關(guān)鍵詞:設(shè)計(jì)

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉