通过PIXI+GSAP仿写vanmoof刹车动效 | 猿创营
环境搭建
通过代码仓库获取项目素材
最终效果
先来看下最终效果
技术选型
通过PIXI
绘制2d图像,使用gsap
实现动画效果
初始化画布
首先通过PIXI
初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
创建资源加载器
通过PIXI
的资源加载器加载资源
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.load();
this.loader.onComplete.add(()=>{
const btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
this.app.stage.addChild(btnImage);
});
优化资源加载器
我们可以将加载按钮资源通过show
方法提到外面去
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
let actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
const btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
this.stage.addChild(btnImage);
}
}
加载按钮外圈资源并更改位置
将按钮的资源加入到按钮组图层中,这样就可以更好的控制按钮的位置了
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
let actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
actionButton.x = actionButton.y = 200;
}
}
使用gsap实现动画效果
使用gsap优化过渡效果
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
let actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
actionButton.x = actionButton.y = 200;
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
}
简单优化代码结构
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
let actionButton = this.createActionButton()
actionButton.x = actionButton.y = 200;
}
createActionButton() {
// 创建按钮图层
let actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
return actionButton
}
}
加载自行车元素
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.add('brake_bike.png', 'images/brake_bike.png');
this.loader.add('brake_handlerbar.png', 'images/brake_handlerbar.png');
this.loader.add('brake_lever.png', 'images/brake_lever.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
// 创建自行车图层
const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);
bikeContainer.scale.x = bikeContainer.scale.y = 0.3
const brakeBikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture);
const brakeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture);
const brakeHandlerbarImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture);
bikeContainer.addChild(brakeBikeImage);
bikeContainer.addChild(brakeLeverImage);
bikeContainer.addChild(brakeHandlerbarImage);
// 调整车闸的位置
brakeLeverImage.pivot.x = 455
brakeLeverImage.pivot.y = 455
brakeLeverImage.x = 722
brakeLeverImage.y = 900
// 按钮图层
let actionButton = this.createActionButton()
actionButton.x = actionButton.y = 400;
}
createActionButton() {
// 创建按钮图层
const actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
return actionButton
}
}
按钮与车闸的交互
通过PIXI
中on
方法添加按钮与车闸之间的交互
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.add('brake_bike.png', 'images/brake_bike.png');
this.loader.add('brake_handlerbar.png', 'images/brake_handlerbar.png');
this.loader.add('brake_lever.png', 'images/brake_lever.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
// 创建自行车图层
const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);
bikeContainer.scale.x = bikeContainer.scale.y = 0.3
const brakeBikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture);
const brakeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture);
const brakeHandlerbarImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture);
bikeContainer.addChild(brakeBikeImage);
bikeContainer.addChild(brakeLeverImage);
bikeContainer.addChild(brakeHandlerbarImage);
// 调整车闸的位置
brakeLeverImage.pivot.x = 455
brakeLeverImage.pivot.y = 455
brakeLeverImage.x = 722
brakeLeverImage.y = 900
// 按钮图层
let actionButton = this.createActionButton()
actionButton.x = actionButton.y = 400;
// 按钮与车闸的交互
actionButton.interactive = true
actionButton.buttonMode = true
actionButton.on("mousedown", () => {
// brakeLeverImage.rotation = Math.PI / 180 * -30
gsap.to(brakeLeverImage, { duration: .8, rotation: Math.PI / 180 * -30 })
})
actionButton.on("mouseup", () => {
gsap.to(brakeLeverImage, { duration: .6, rotation: 0 })
})
}
createActionButton() {
// 创建按钮图层
const actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
return actionButton
}
}
创建骑行粒子
使用for
创建粒子,粒子特点为多颜色,固定角度一直运动,按钮按住时停止运动并由回弹效果
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.add('brake_bike.png', 'images/brake_bike.png');
this.loader.add('brake_handlerbar.png', 'images/brake_handlerbar.png');
this.loader.add('brake_lever.png', 'images/brake_lever.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
// 创建自行车图层
const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);
bikeContainer.scale.x = bikeContainer.scale.y = 0.2
const brakeBikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture);
const brakeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture);
const brakeHandlerbarImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture);
bikeContainer.addChild(brakeBikeImage);
bikeContainer.addChild(brakeLeverImage);
bikeContainer.addChild(brakeHandlerbarImage);
// 调整车闸的位置
brakeLeverImage.pivot.x = 455
brakeLeverImage.pivot.y = 455
brakeLeverImage.x = 722
brakeLeverImage.y = 900
// 按钮图层
let actionButton = this.createActionButton()
actionButton.x = actionButton.y = 400;
// 按钮与车闸的交互
actionButton.interactive = true
actionButton.buttonMode = true
actionButton.on("mousedown", () => {
// brakeLeverImage.rotation = Math.PI / 180 * -30
gsap.to(brakeLeverImage, { duration: .8, rotation: Math.PI / 180 * -30 })
})
actionButton.on("mouseup", () => {
gsap.to(brakeLeverImage, { duration: .6, rotation: 0 })
})
let resize = () => {
bikeContainer.x = window.innerWidth - bikeContainer.width
bikeContainer.y = window.innerHeight - bikeContainer.height
}
window.addEventListener('resize', resize)
resize()
// 实现骑行效果
let particlesContainer = new PIXI.Container();
this.stage.addChild(particlesContainer);
particlesContainer.pivot.x = window.innerWidth / 2
particlesContainer.pivot.y = window.innerHeight / 2
particlesContainer.x = window.innerWidth / 2
particlesContainer.y = window.innerHeight / 2
particlesContainer.rotation = 38 * Math.PI / 180
// 创建粒子
let particles = []
let colors = [0xf1cf54, 0xb5cea8, 0x333333] // 多颜色
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 parItem = {
sx: Math.random() * window.innerWidth,
sy: Math.random() * window.innerHeight,
gr: gr
}
gr.x = parItem.sx;
gr.y = parItem.sy;
particlesContainer.addChild(gr);
particles.push(parItem)
}
function loop() {
for (let i = 0; particles.length; i++) {
let pItem = particles[i]
pItem.gr.y += 20
if (pItem.gr.y > window.innerHeight) {
pItem.gr.y = 0
}
}
}
gsap.ticker.add(loop)
// 一直移动,超出边界回到顶部,按住鼠标停止回弹效果,松开继续
}
createActionButton() {
// 创建按钮图层
const actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
return actionButton
}
}
优化骑行动画
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.add('brake_bike.png', 'images/brake_bike.png');
this.loader.add('brake_handlerbar.png', 'images/brake_handlerbar.png');
this.loader.add('brake_lever.png', 'images/brake_lever.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
// 创建自行车图层
const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);
bikeContainer.scale.x = bikeContainer.scale.y = 0.2
const brakeBikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture);
const brakeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture);
const brakeHandlerbarImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture);
bikeContainer.addChild(brakeBikeImage);
bikeContainer.addChild(brakeLeverImage);
bikeContainer.addChild(brakeHandlerbarImage);
// 调整车闸的位置
brakeLeverImage.pivot.x = 455
brakeLeverImage.pivot.y = 455
brakeLeverImage.x = 722
brakeLeverImage.y = 900
// 按钮图层
let actionButton = this.createActionButton()
actionButton.x = actionButton.y = 400;
// 按钮与车闸的交互
actionButton.interactive = true
actionButton.buttonMode = true
actionButton.on("mousedown", () => {
// brakeLeverImage.rotation = Math.PI / 180 * -30
gsap.to(brakeLeverImage, { duration: .8, rotation: Math.PI / 180 * -30 })
})
actionButton.on("mouseup", () => {
gsap.to(brakeLeverImage, { duration: .6, rotation: 0 })
})
let resize = () => {
bikeContainer.x = window.innerWidth - bikeContainer.width
bikeContainer.y = window.innerHeight - bikeContainer.height
}
window.addEventListener('resize', resize)
resize()
// 实现骑行效果
let particlesContainer = new PIXI.Container();
this.stage.addChild(particlesContainer);
particlesContainer.pivot.x = window.innerWidth / 2
particlesContainer.pivot.y = window.innerHeight / 2
particlesContainer.x = window.innerWidth / 2
particlesContainer.y = window.innerHeight / 2
particlesContainer.rotation = 38 * Math.PI / 180
// 创建粒子
let particles = []
let colors = [0xf1cf54, 0xb5cea8] // 多颜色
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 parItem = {
sx: Math.random() * window.innerWidth,
sy: Math.random() * window.innerHeight,
gr: gr
}
gr.x = parItem.sx;
gr.y = parItem.sy;
particlesContainer.addChild(gr);
particles.push(parItem)
}
let speed = 10
function loop() {
speed += .5
speed = Math.min(speed, 30)
for (let i = 0; particles.length; i++) {
let pItem = particles[i]
pItem.gr.y += speed
pItem.gr.scale.y = 40
pItem.gr.scale.x = .02
if (pItem.gr.y > window.innerHeight) {
pItem.gr.y = 0
}
}
}
function start() {
speed = 0
gsap.ticker.add(loop)
}
start()
// 一直移动,超出边界回到顶部,按住鼠标停止回弹效果,松开继续
}
createActionButton() {
// 创建按钮图层
const actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
return actionButton
}
}
添加刹车效果
最后将效果实现刹车效果
class BrakeBanner {
constructor(selector) {
// 初始化画布
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0000ff,
resizeTo: window
})
document.querySelector(selector).appendChild(this.app.view);
// 创建资源加载器
this.loader = new PIXI.Loader();
this.loader.add('btn.png', 'images/btn.png');
this.loader.add('btn_circle.png', 'images/btn_circle.png');
this.loader.add('brake_bike.png', 'images/brake_bike.png');
this.loader.add('brake_handlerbar.png', 'images/brake_handlerbar.png');
this.loader.add('brake_lever.png', 'images/brake_lever.png');
this.loader.load();
this.stage = this.app.stage
this.loader.onComplete.add(() => {
this.show()
});
}
show() {
// 创建自行车图层
const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);
bikeContainer.scale.x = bikeContainer.scale.y = 0.2
const brakeBikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture);
const brakeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture);
const brakeHandlerbarImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture);
bikeContainer.addChild(brakeBikeImage);
bikeContainer.addChild(brakeLeverImage);
bikeContainer.addChild(brakeHandlerbarImage);
// 调整车闸的位置
brakeLeverImage.pivot.x = 455
brakeLeverImage.pivot.y = 455
brakeLeverImage.x = 722
brakeLeverImage.y = 900
// 按钮图层
let actionButton = this.createActionButton()
actionButton.x = actionButton.y = 400;
// 按钮与车闸的交互
actionButton.interactive = true
actionButton.buttonMode = true
actionButton.on("mousedown", () => {
// brakeLeverImage.rotation = Math.PI / 180 * -30
gsap.to(brakeLeverImage, { duration: .8, rotation: Math.PI / 180 * -30 })
pause()
})
actionButton.on("mouseup", () => {
gsap.to(brakeLeverImage, { duration: .6, rotation: 0 })
start()
})
let resize = () => {
bikeContainer.x = window.innerWidth - bikeContainer.width
bikeContainer.y = window.innerHeight - bikeContainer.height
}
window.addEventListener('resize', resize)
resize()
// 实现骑行效果
let particlesContainer = new PIXI.Container();
this.stage.addChild(particlesContainer);
particlesContainer.pivot.x = window.innerWidth / 2
particlesContainer.pivot.y = window.innerHeight / 2
particlesContainer.x = window.innerWidth / 2
particlesContainer.y = window.innerHeight / 2
particlesContainer.rotation = 35 * Math.PI / 180
// 创建粒子
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 parItem = {
sx: Math.random() * window.innerWidth,
sy: Math.random() * window.innerHeight,
gr: gr
}
gr.x = parItem.sx;
gr.y = parItem.sy;
particlesContainer.addChild(gr);
particles.push(parItem)
}
let speed = 0
function loop() {
speed += .5
speed = Math.min(speed, 20)
for (let i = 0; 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; particles.length; i++) {
let pItem = particles[i]
pItem.gr.scale.y = 1
pItem.gr.scale.x = 1
gsap.to(pItem.gr, { duration: .6, x: pItem.sx, y: pItem.sy, ease: "elastic.out" })
}
}
start()
// 一直移动,超出边界回到顶部,按住鼠标停止回弹效果,松开继续
}
createActionButton() {
// 创建按钮图层
const actionButton = new PIXI.Container();
this.stage.addChild(actionButton);
let btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
let btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
let btnCircleImage2 = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
actionButton.addChild(btnImage);
actionButton.addChild(btnCircleImage);
actionButton.addChild(btnCircleImage2);
// 更改按钮位置
const btnImageWidth = btnImage.width
btnImage.pivot.x = btnImageWidth / 2
btnImage.pivot.y = btnImageWidth / 2
const btnCircleImageWidth = btnCircleImage.width
btnCircleImage.pivot.x = btnCircleImageWidth / 2
btnCircleImage.pivot.y = btnCircleImageWidth / 2
const btnCircleImageWidth2 = btnCircleImage2.width
btnCircleImage2.pivot.x = btnCircleImageWidth2 / 2
btnCircleImage2.pivot.y = btnCircleImageWidth2 / 2
// 使用gsap实现动画效果
btnCircleImage.scale.x = btnCircleImage.scale.y = 0.8
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 })
gsap.to(btnCircleImage.scale, { duration: 1, alpha: 0, repeat: -1 })
return actionButton
}
}
最后
在公众号里搜 大帅老猿
,在他这里可以学到很多东西!
转载自:https://juejin.cn/post/7125858071456251911