时间:2023-07-21 20:21:01 | 来源:网站运营
时间:2023-07-21 20:21:01 来源:网站运营
时间之美 —— SpriteJS 的原创时间轴设计:let startTime = Date.now(), T = 2000requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / T ball.style.transform = `rotate(${360 * p}deg)` requestAnimationFrame(update)})
上面的代码我们实现一个小球围绕圆心做匀速圆周运动运动的动画,这是一个基于时间的动画,我们根据当前时间改变元素的transform样式,通过requestAnimationFram来更新,这样就实现了。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)})
小球运动方向是顺时针旋转,如果我们要让小球逆时针旋转,我们可以改变rotate的符号:let startTime = Date.now(), T = 1000requestAnimationFrame(function update(){ let p = (Date.now() - startTime) / T ball.style.transform = `rotate(${-360 * p}deg)` requestAnimationFrame(update)})
如果页面上还有其他动画对象,我们同样可以添加进来,比如我们在小球旁边添加一个颜色从红色渐变到蓝色的方块: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) }})
现在我们要改变动画的速度或者方向就稍微麻烦一点点了,因为我们有两个独立的动画要修改参数。但是如果有类似于将所有动画速度提升到原来的2倍这样的需求时,我们有一个最简单的方法,那就是修改时间流动的速度。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) }})
改写后的代码跟之前的代码相比,几乎没有什么不同,唯一的不同之处是原先的Data.now() - startTime
被更加简单的timeline.currentTime
所取代了。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)
我们会看到实际上每隔1秒钟,timeline的currentTime变化0.5秒,而且时间从-2秒开始变化。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) }})
我们会看到,经过这样的修改之后,两个元素动画的速度都减半,而且方块在3秒钟(真实世界的3秒,timeline世界的1.5秒)之后才开始改变颜色。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,动画速度(和方向)会立即改变,比如下面这个例子,我们把鼠标移入右边的方块时,动画的速度和方向都会立即改变: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})
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)
这个例子timeline的setInterval实际触发时间是500毫秒(1000毫秒/2.0)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几乎同时触发,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)
两个timer都立即触发,因为它们的当前时间都在timer的时间之后。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不会触发,t2的timer会触发,因为t1设置了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) t1.playbackRate = 1.0}, 1000)
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分支出来的,所以我们改变localTime,就能同时改变forkedTimeline1和forkedTimeline2,比如现在forkedTimeline1的时间线是正向,forkedTimeline2的时间线是负向,我们只要把localTimeline的playbackRate设置为-1,那么forkedTimeline1时间线变成负向,forkedTimeline2的时间线则变成正向。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)
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 })
以上就是关于sprite-timeline的关键内容,其他还有一些有趣的功能,在这里没有列出,比如timeline的“熵”(entropy),以及entropy相关的用法,有兴趣的同学可以关注GitHub repo自行研究。关键词:设计