制作刹车动效分几步

制作刹车动效总共分3步。
- 第一步:是使用Pixi上的Application对象创建一个矩形显示区域。 它会自动生成一个HTML
<canvas>元素 - 第二步:您需要创建一个称为
stage的特殊Pixi容器对象在canvas画布上显示图像。 - 第三步:创建
Sprite显示在stage上, 这样你就可以看到完美的视图了。

创建一个应用
首先引入 pixi, gasp
<script src="https://pixijs.download/release/pixi.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"></script>
创建一个 app 并添加到容器 dom 中
<div id = 'container'></div>
<script>
const container = document.querySelector('#container');
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0xff0000,
})
container.appendChild(app.view);
</script>
这个时候通过审查元素,你应该可以看到一个 canvas 已经插入到 dom 中。
创建一个 Sprite 并显示在 stage 中
const loader = PIXI.loader;
const stage = app.stage;
loader
.add("btn.png", "images/btn.png") // add(name, url, optionObject, callbackFunction)
.add("btn_circle.png", "images/btn_circle.png") // 一个圆圈
.load(setup);
function setup() {
const btnSprite = new PIXI.Sprite(PIXI.loader.resources['btn.png'].texture);
const btnCircleSprite = new PIXI.Sprite(PIXI.loader.resources['btn_circle.png'].texture);
app.stage.addChild(btnSprite)
app.stage.addChild(btnCircleSprite)
}
并调整一个默认位置, 按照视图左右居中, 为了方便布局, 把这两个 Sprite 作为一个整体
const actionContainer = new PIXI.Container();
actionContainer.x = actionContainer.y = window.innerWidth / 2; // 让容器左右居中
// 让按钮和圆圈在容器中居中
btnSprite.pivot.x = btnSprite.pivot.y = btnSprite.width / 2;
btnCircleSprite.pivot.x = btnCircleSprite.pivot.y = btnCircleSprite.width / 2;
btnCircleSprite2.pivot.x = btnCircleSprite2.pivot.y = btnCircleSprite2.width / 2;
function setup() {
const btnSprite = new PIXI.Sprite(PIXI.loader.resources['btn.png'].texture);
const btnCircleSprite = new PIXI.Sprite(PIXI.loader.resources['btn_circle.png'].texture);
actionContainer.addChild(btnSprite)
actionContainer.addChild(btnCircleSprite)
app.stage.addChild(actionContainer)
}
再加入一些涟漪波纹的效果, 这里使用 gsap 做动画效果
btnCircleSprite.scale.x = btnCircleSprite.scale.y = 0.7; // 设置初始缩放为 0.7
gsap.to(btnCircleSprite.scale, { // 经过1s 从 0.7 变成 1.3 并且无限循环
duration: 1,
x: 1.3,
y: 1.3,
repeat: -1,
});
gsap.to(btnCircleSprite, { // 另外加了一个透明度的渐变
duration: 1,
alpha: 0,
repeat: -1,
});
最终的效果是这样的

创建更多的 Sprite 到 stage 中
使用Pixi的 Sprite 类来创建精灵。创建方法有三种:
- 通过
单个图像文件。 - 通过
雪碧图。 - 通过
纹理贴图。
我这里都是通过单个图像文件创建的。
loader.add("brake_bike.png", "images/brake_bike.png")
.add("brake_handlerbar.png", "images/brake_handlerbar.png")
.add("brake_lever.png", "images/brake_lever.png")
.load(setup);
// 同理创建一个 bikeContainer
const bikeContainer = new PIXI.Container();
function setup() {
const brakeBikeSprite = new PIXI.Sprite(PIXI.loader.resources['brake_bike.png'].texture);
const brakeHandlerbarSprite = new PIXI.Sprite(PIXI.loader.resources['brake_handlerbar.png'].texture);
const brakeLeverSprite = new PIXI.Sprite(PIXI.loader.resources['brake_lever.png'].texture);
bikeContainer.addChild(brakeBikeSprite)
bikeContainer.addChild(brakeHandlerbarSprite)
bikeContainer.addChild(brakeLeverSprite)
app.stage.addChild(bikeContainer)
}
所有的资源都加载进来了, 并且车轮和把手都是固定的, 只有手刹是可以的活动的,我想大家都骑过自行车,手刹的活动就是围绕一点的旋转

添加手刹的动效
- 让按钮可以点击
actionContainer.interactive = true; // 激活按钮点击功能
actionContainer.buttonMode = true; // 鼠标放在按钮上是 cursor: pointer; 的效果
- 点击按钮手刹旋转 -30°,松开按钮手刹恢复原状 0°
actionContainer.on("mousedown", () => {
gsap.to(brakeLeverSprite, {
duration: 0.6,
rotation: (Math.PI / 180) * -30,
});
});
actionContainer.on("mouseup", () => {
gsap.to(brakeLeverSprite, {
duration: 0.6,
rotation: 0,
});
});
添加一些速度斜线, 给人高速行驶的感觉
- 随机生成 15 个小点, 然后小点按照一定的加速度向下移动,把这些点看成一个整体放到一个容器内。
- 旋转容器到一定的角度
- 小点的 y 轴大小大于容器后,再重置 y 值为 0, 一直这样无限循环
- 小点是通过 canvas 绘制的,并随机了几个颜色

const particleContainer = new PIXI.Container();
// 设置容器的中心和 x,y 的位置
particleContainer.pivot.x = window.innerWidth / 2;
particleContainer.pivot.y = window.innerHeight / 2;
particleContainer.x = window.innerWidth / 2;
particleContainer.y = window.innerHeight / 2;
particleContainer.rotation = (35 * Math.PI) / 180; // 容器旋转 35°
const particles = [];
const colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0x818181, 0x000000];
let speed = 0;
for (let index = 0; index < 20; index++) {
const gr = new PIXI.Graphics();
const i = Math.floor(Math.random() * colors.length);
gr.beginFill(colors[i]);
gr.drawCircle(0, 0, 3);
gr.endFill();
gr.x = Math.random() * window.innerWidth;
gr.y = Math.random() * window.innerHeight;
const pItem = {
sx: Math.random() * window.innerWidth,
sy: Math.random() * window.innerHeight,
gr,
};
particles.push(pItem);
particleContainer.addChild(gr);
}
const loop = () => {
speed += 0.5;
speed = Math.min(speed, 20);
for (let index = 0; index < particles.length; index++) {
const pItem = particles[index];
pItem.gr.y += speed;
// 小点的速度为最大值 20 时, 小点变成一条小直线
if (speed === 20) {
pItem.gr.scale.x = 0.1;
pItem.gr.scale.y = 30;
}
if (pItem.gr.y > window.innerHeight) {
pItem.gr.y = 0;
}
}
};
gsap.ticker.add(loop);
// 把 start 和 pause 两个方法分别添加到actionContainer mouseup 和 mousedown 事件中
// 松开手刹小点开始移动
function start() {
speed = 0;
gsap.ticker.add(loop);
}
// 按住手刹小点暂停
function pause() {
gsap.ticker.remove(loop);
for (let index = 0; index < particles.length; index++) {
const pItem = particles[index];
pItem.gr.y += speed;
pItem.gr.scale.x = 1;
pItem.gr.scale.y = 1;
gsap.to(pItem.gr, {
duration: 0.6,
x: pItem.sx,
y: pItem.sy,
ease: "elastic.out",
});
}
}
来看看最终的效果

这只是特效中的一部分。加入我们一起搞事情。