Fabric.js 动画
2018-11-27 20:22:12
2380次阅读
0个评论
一般大家提到 canvas 一定会想到一些酷炫的动画效果,Fabricjs 当然也少不了这些啦!
Fabricjs 提供了简易的动画 Api 让我们可以做一些简单的动画效果,透过操作我们自己所新增的物件,让物件动起来,今天就让我们一起来玩玩 Fabricjs 提供的动画吧。
Fabricjs 所有的物件上都能使用 animate 这个方法,就如同 set 这个常用的方法一样,这边马上来试试。
object.animate
可以看到我们简单的透过 animate() 这个方法就轻鬆的让矩形旋转 360 度,我们稍微看一下带入的参数:
第一个参数 'angle' 是想要被改变的物件属性,可以是:angle、top、left (缺点是我们没办法做出顏色渐变的动画)
第二个参数 360 就是改变的目标值,也可使用 '+=XXX' 来设定要增加或减少多少值
第三个参数 {} 传入物件,这边是 animate 各种细部的设定,如 duration、动画时间效果、callbacks
onChange
為什麼我们要在第三个参数中加入 onChange 呢?这是因為在我们呼叫了 animate 方法后,canvas 会一直更新物件的状态,angle 会慢慢的从 0~360,所以我们必须要在每个影格都重新绘製物件,让画面才有动画的感觉。
duration 动画执行时间
我们在第三个参数内可以设定动画执行的经过时间要多久。
结果
easing 动画效果
可以在 animate 第三个参数内加入 easing 来变更动画进行时的效果,这边可用 fabric.util.ease 所提供的一些效果。
可以看到我们加上了 easing: fabric.util.ease.easeInOutBack 后,呈现了不一样的动画效果。
这边 fabric.util.ease 还提供了更多有趣的动画效果
fabricjs doc - http://fabricjs.com/docs/fabric.util.ease.html
累加数值
我们也可以透过相对位置累加的方式,来做动画移动的效果。
'+=500'
'-=500'
组合动画效果
可以把动画效果合在一起,做出更丰富的效果
这边结合上面两个动画
移动
旋转
实作练习
控制 100 颗球球的位置、大小、透明度。
设定画布和操控动画的变数
首先我们要產生静态的 canvas 因為我们不需要去操作他们,并且定义一个 playing 的 Boolean 型态变数方便我们之后去操控动画的动或不动。
产生乱数
我们需要一个方便產生乱数的函数,让我们动画更加有变化性
产生 100 个圆形并设定动画
使用迴圈产生 100 个圆形,并且為他们设定动画,这边我把动画放在另外一个函数,须把 circle 物件传入 setAnimate
setAnimate(circle)
这边就是最重要的设定动画的函数啦!
这边分别设定了以下四个动画效果,结合刚刚的 getRandomInt,让动画更加随机更有趣!
radius
opacity
left
top
完整函数
重点在最后一个动画设置,帮大家拿出来看一下。
我们只需要在最后一个 circle.animate 设定动画来加入 onChange 以及 onComplete 函数,不然会重复设置 4 次,造成动画的效能降低。
我们再来一一的看一下他们发生了什麼。
onChange
这边需要判断是否為最后一个被新增的 circle 物件,否则会重复呼叫 canvas.renderAll() 这个重整函数。(100 个物件就会重复呼叫 99 次),造成效能变差。
onComplete
这边很直觉的就是当我们 circle 物件动画做完后就继续做新的一次动画,让我们看起来动画是连续的。
暂停、继续按钮
透过一开始设置的 playing 变数,来控制动画是否继续,canvas.getObjects() 抓取所有在 canvas 之下的所有 circle 物件。透过 forEach(),在一次地将所有 circle 物件加入动画效果。
Fabricjs 提供了简易的动画 Api 让我们可以做一些简单的动画效果,透过操作我们自己所新增的物件,让物件动起来,今天就让我们一起来玩玩 Fabricjs 提供的动画吧。
Fabricjs 所有的物件上都能使用 animate 这个方法,就如同 set 这个常用的方法一样,这边马上来试试。
object.animate
// 动画练习-角度转换
rect.animate('angle', 360, {
onChange: canvas.renderAll.bind(canvas)
})
可以看到我们简单的透过 animate() 这个方法就轻鬆的让矩形旋转 360 度,我们稍微看一下带入的参数:
第一个参数 'angle' 是想要被改变的物件属性,可以是:angle、top、left (缺点是我们没办法做出顏色渐变的动画)
第二个参数 360 就是改变的目标值,也可使用 '+=XXX' 来设定要增加或减少多少值
第三个参数 {} 传入物件,这边是 animate 各种细部的设定,如 duration、动画时间效果、callbacks
onChange
為什麼我们要在第三个参数中加入 onChange 呢?这是因為在我们呼叫了 animate 方法后,canvas 会一直更新物件的状态,angle 会慢慢的从 0~360,所以我们必须要在每个影格都重新绘製物件,让画面才有动画的感觉。
duration 动画执行时间
我们在第三个参数内可以设定动画执行的经过时间要多久。
rect.animate('angle', 360, {
duration: 3000, // 三秒才完成动画
onChange: canvas.renderAll.bind(canvas)
})
结果
easing 动画效果
可以在 animate 第三个参数内加入 easing 来变更动画进行时的效果,这边可用 fabric.util.ease 所提供的一些效果。
rect.animate('angle', 360, {
duration: 1000,
onChange: canvas.renderAll.bind(canvas),
easing: fabric.util.ease.easeInOutBack
})
可以看到我们加上了 easing: fabric.util.ease.easeInOutBack 后,呈现了不一样的动画效果。
这边 fabric.util.ease 还提供了更多有趣的动画效果
fabricjs doc - http://fabricjs.com/docs/fabric.util.ease.html
累加数值
我们也可以透过相对位置累加的方式,来做动画移动的效果。
'+=500'
'-=500'
// 动画练习-角度转换
rect.animate('left', '+=500', {
onChange: canvas.renderAll.bind(canvas)
})
组合动画效果
可以把动画效果合在一起,做出更丰富的效果
这边结合上面两个动画
移动
旋转
实作练习
控制 100 颗球球的位置、大小、透明度。
设定画布和操控动画的变数
首先我们要產生静态的 canvas 因為我们不需要去操作他们,并且定义一个 playing 的 Boolean 型态变数方便我们之后去操控动画的动或不动。
// 视窗大小
const windowSize = {
width: window.innerWidth,
height: window.innerHeight
}
// 产生静态 canvas
const canvas = new fabric.StaticCanvas('canvas', {
height: windowSize.height, // 让画布同视窗大小
width: windowSize.width
})
let playing = true // 预设开启
产生乱数
我们需要一个方便產生乱数的函数,让我们动画更加有变化性
// 取得乱数
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
产生 100 个圆形并设定动画
使用迴圈产生 100 个圆形,并且為他们设定动画,这边我把动画放在另外一个函数,须把 circle 物件传入 setAnimate
这边有一个重点是必须要记录哪个圆形是最后被產生的,方便我们之后重整画布。
circle.lastAdd = i === 99 这边
// add 100 circle
for (let i = 0; i<100; i++) {
// 产生圆形,并给定随机起始值
let circle = new fabric.Circle({
radius: getRandomInt(2, 15),
left: getRandomInt(0, windowSize.width),
top: getRandomInt(0, windowSize.height),
opacity: getRandomInt(0.1, 1)
})
// 纪录一下自己是第几个被產生的 circle
circle.lastAdd = i === 99
canvas.add(circle)
// 设定动画
playing && setAnimate(circle)
}
setAnimate(circle)
这边就是最重要的设定动画的函数啦!
这边分别设定了以下四个动画效果,结合刚刚的 getRandomInt,让动画更加随机更有趣!
radius
opacity
left
top
完整函数
// 设定动画函数
function setAnimate (circle) {
// 变化半径
circle.animate('radius', getRandomInt(2, 15), {
duration: getRandomInt(1000, 5000)
})
// 变化透明度
circle.animate('opacity', getRandomInt(0, 1), {
duration: getRandomInt(1000, 5000)
})
// 变化座标
circle.animate('left', getRandomInt(0, windowSize.width), {
easing: fabric.util.ease.easeInOutCubic,
duration: getRandomInt(1000, 5000)
})
// 变化座标
circle.animate('top', getRandomInt(0, windowSize.height), {
onChange: () => {
// 不需要每个 circle 都呼叫 canvas.renderAll()
// 只有最后一个被新增的物件 onChange 去更新画布
if (circle.lastAdd) canvas.renderAll()
},
onComplete: () => playing && setAnimate(circle),
easing: fabric.util.ease.easeInOutCubic,
duration: getRandomInt(1000, 5000)
})
}
重点在最后一个动画设置,帮大家拿出来看一下。
circle.animate('top', getRandomInt(0, windowSize.height), {
onChange: () => {
// 不需要每个 circle 都呼叫 canvas.renderAll()
// 只有最后一个被新增的物件 onChange 去更新画布
if (circle.lastAdd) canvas.renderAll()
},
onComplete: () => playing && setAnimate(circle),
easing: fabric.util.ease.easeInOutCubic,
duration: getRandomInt(1000, 5000)
})
我们只需要在最后一个 circle.animate 设定动画来加入 onChange 以及 onComplete 函数,不然会重复设置 4 次,造成动画的效能降低。
我们再来一一的看一下他们发生了什麼。
onChange
这边需要判断是否為最后一个被新增的 circle 物件,否则会重复呼叫 canvas.renderAll() 这个重整函数。(100 个物件就会重复呼叫 99 次),造成效能变差。
onComplete
这边很直觉的就是当我们 circle 物件动画做完后就继续做新的一次动画,让我们看起来动画是连续的。
暂停、继续按钮
透过一开始设置的 playing 变数,来控制动画是否继续,canvas.getObjects() 抓取所有在 canvas 之下的所有 circle 物件。透过 forEach(),在一次地将所有 circle 物件加入动画效果。
// 左上方按钮
document.querySelector('#toggle').addEventListener('click', (e) => {
const targetEl = e.target
if (playing) {
targetEl.innerHTML = 'start'
} else {
targetEl.innerHTML = 'stop'
// 让所有物件在一次动起来
canvas.getObjects().forEach(circle => setAnimate(circle))
}
playing = !playing
})
10