这是一个真实的私活儿项目,大帅老师整理好后拿来给我们做全员实训。基于
PIXI + GSAP来模仿vanmoof电商页面的刹车视觉动效,案例虽小,但里面的知识点很密集,基础的API基本都用到了,是个很好上手的小项目。
效果
分析
- 首先准备一些图片素材
- 其次用一块画布把素材装起来,排好布局
- 最后让素材动起来
方案
- PixiJS实现画布效果
- GSAP实现动画效果
实现
🎨 PixiJS
初始化PIXI实例,在页面上插入一个 canvas 元素
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0xffffff,
resizeTo: window,
});
document.querySelector(selector).appendChild(this.app.view);
初始化加载器 loader ,将资源添加到加载器队列。
开始加载排队的资源。
this.loader = new PIXI.Loader<img src="();" alt="" width="30%" />
this.loader.add("btn.png", "images/btn.png");
this.loader.add("btn_circle.png", "images/btn_circle.png");
this.loader.add("brake_lever.png", "images/brake_lever.png");
this.loader.add("brake_handlerbar.png", "images/brake_handlerbar.png");
this.loader.add("brake_bike.png", "images/brake_bike.png");
this.loader.load();
this.loader.onComplete.add(() => {
this.show();
});
用 Container 来创建图片资源的容器。它添加了对高级渲染功能的内置支持,例如遮罩和过滤。它是所有显示对象的基类,充当其他对象(包括 Graphics 和 Sprite)的容器。Sprite 对象是渲染到屏幕上的所有纹理对象的基础
// 创建按钮容器
createActionButton() {
let actionButton = new PIXI.Container();
let btnImage = new PIXI.Sprite(this.loader.resources["btn.png"].texture);
let btnCircle = new PIXI.Sprite(
this.loader.resources["btn_circle.png"].texture
);
let btnCircle2 = new PIXI.Sprite(
this.loader.resources["btn_circle.png"].texture
);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircle);
actionButton.addChild(btnCircle2);
// 改变对象在本父级本地的中心位置
btnImage.pivot.x = btnImage.pivot.y = btnImage.width / 2;
btnCircle.pivot.x = btnCircle.pivot.y = btnCircle.width / 2;
btnCircle2.pivot.x = btnCircle2.pivot.y = btnCircle2.width / 2;
// 改变在 x 轴上相对于父级本地坐标的位置
actionButton.x = actionButton.y = 400;
// 对象沿本地坐标轴放缩
btnCircle.scale.x = btnCircle.scale.y = 0.8;
return actionButton;
}
show() {
let actionButton = this.createActionButton();
actionButton.x = actionButton.y = 300;
// 创建自行车容器
const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);
bikeContainer.scale.x = bikeContainer.scale.y = 0.3;
const bikeImage = new PIXI.Sprite(
this.loader.resources["brake_bike.png"].texture
);
const bikeHandlerbarImage = new PIXI.Sprite(
this.loader.resources["brake_handlerbar.png"].texture
);
const bikeLeverImage = new PIXI.Sprite(
this.loader.resources["brake_lever.png"].texture
);
bikeContainer.addChild(bikeImage);
bikeContainer.addChild(bikeHandlerbarImage);
bikeContainer.addChild(bikeLeverImage);
bikeLeverImage.pivot.x = bikeLeverImage.pivot.y = 445;
bikeLeverImage.x = 722;
bikeLeverImage.y = 900;
this.stage.addChild(actionButton);
...
//创建粒子容器
let particleContainer = new PIXI.Container();
this.stage.addChild(particleContainer);
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;
}
Graphics 类主要用于将原始形状(如线条、圆形和矩形)渲染到显示器上,并对它们进行着色和填充。
show(){
...
// 创建粒子
let particles = [];
let colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0x818181, 0x000000];
for (let i = 0; i < 10; i++) {
let gr = new PIXI.Graphics();
//粒子有多个颜色
gr.beginFill(colors[Math.floor(Math.random() * colors.length)]);
gr.drawCircle(0, 0, 6);
gr.endFill();
let pItem = {
sx: Math.random() * window.innerWidth,
sy: Math.random() * window.innerHeight,
gr: gr,
};
gr.x = pItem.sx;
gr.y = pItem.sy;
particleContainer.addChild(gr);
particles.push(pItem);
}
...
}
📹 GSAP
用最基本的 to() 实现按钮外圈、外圈放大效果
createActionButton() {
...
// 给按钮添加动画
gsap.to(btnCircle.scale, {
duration: 1,
x: 1.3,
y: 1.3,
repeat: -1,
});
gsap.to(btnCircle, {
duration: 1,
alpha: 0,
repeat: -1,
});
return actionButton;
}
gsap.ticker 添加事件监听器,以在每次事件更新后运行自定义逻辑
show() {
...
//按住鼠标停止
//停止的时候还有一点回弹的效果
actionButton.interactive = true;
actionButton.buttonMode = true;
actionButton.on("mousedown", () => {
// bikeLeverImage.rotation = (Math.PI / 180) * -30;
gsap.to(bikeLeverImage, {
duration: 0.6,
rotation: (Math.PI / 180) * -30,
});
pause();
});
//松开鼠标继续
actionButton.on("mouseup", () => {
gsap.to(bikeLeverImage, {
duration: 0.6,
rotation: 0,
});
start();
});
let resize = () => {
bikeContainer.x = window.innerWidth - bikeContainer.width;
bikeContainer.y = window.innerHeight - bikeContainer.height;
};
window.addEventListener("resize", resize);
resize();
...
//向某一个角度持续移动
let speed = 0;
function loop() {
speed += 0.5;
speed = Math.min(speed, 20);
for (let i = 0; i < particles.length; i++) {
let pItem = particles[i];
pItem.gr.y += speed;
if (speed >= 20) {
pItem.gr.scale.y = 40;
pItem.gr.scale.x = 0.03;
}
//超出边界后回到顶部继续移动
if (pItem.gr.y > window.innerHeight) pItem.gr.y = 0;
}
}
function start() {
speed = 0;
gsap.ticker.add(loop);
}
function pause() {
gsap.ticker.remove(loop);
for (let i = 0; i < particles.length; i++) {
let pItem = particles[i];
pItem.gr.scale.y = 1;
pItem.gr.scale.x = 1;
gsap.to(pItem.gr, {
duration: 0.6,
x: pItem.sx,
y: pItem.sy,
ease: "elastic.out",
});
}
}
start();
}
总结
最后实现的效果是很丝滑的,用这里面涉及到的API就能够实现很多酷炫的动画效果,具体API的使用方法在官网都描述得很详尽,想要深入了解的同学可以移步官网
完整视频链接:www.bilibili.com/video/BV1q3…
在公众号里搜
大帅老猿,在大帅老师这里可以学到很多东西👍