写作初衷
- 参与猿创营的活动
- 踏入前端行业之前,一直被前端的酷炫效果,动画所吸引,奈何工作中无接触类似的工作,不知道如何学,加上懒惰,想法便只停留在脑海中
- 尝试开始输出内容,证明存在过的痕迹
资料
- PIXI:pixijs.com/
- GSAP:greensock.com/docs/v3/GSA…
PIXI:是一个非常快的2D sprite渲染引擎。这是什么意思?这意味着它可以帮助你显示、动画和管理交互式图形,这样你就可以轻松地使用JavaScript和其他HTML5技术制作游戏和应用程序。它有一个合理的,整洁的API,并包括许多有用的功能,如支持纹理地图集和提供一个精简的系统,为动画精灵(交互式图像)。它还为您提供了一个完整的场景图,这样您就可以创建嵌套精灵(精灵中的精灵)的层次结构,并允许您将鼠标和触摸事件直接附加到精灵上。而且,最重要的是,Pixi可以让您自由的使用,使其适应您的个人编码风格,并与其它框架无缝集成。
GSAP:(GreenSock Animation Platform)是一个从flash时代一直发展到今天的web专业动画库。
实现步骤
1、html结构及brakeBanner类实现
// 存放的容器
<div id="brakebanner"></div>
<script>
window.onload = init;
function init() {
let banner = new BrakeBanner("#brakebanner");
}
class BrakeBanner{}
</script>
2、创建PIXI应用PIXI.Application【画布】& 导入资源 PIXI.Loader
想要在画布显示的东西,都必须被加进app.stage中,app.stage: 舞台,是PIXI的容器对象,用来包裹所有精灵的主要容器
class BrakeBanner{
constructor(selector){
// 1、创建PIXI应用【画布】
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x3b3d3c, // 灰色
resizeTo: window // 根据window窗口进行缩放
})
document.querySelector(selector).appendChild(this.app.view)
// 想在画布显示东西,必须被加进stage中, stage也叫舞台,是PIXI的容器对象,也是所有可见对象的根容器
this.stage = this.app.stage
// 2、加载资源
this.loader = new PIXI.Loader()
// 自定义的key: btn.png, 资源路径:images/btn.png
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.loader.onComplete.add(() => {
// 资源加载完成, 开始进行图片拼接以及动效制作
this.show()
})
}
图中有公路,自行车,按钮,线条,由于从上往下显示顺序,所以最先加载的图片,在最底下,故创建顺序为:公路 > 自行车 > 按钮
3、创建公路:创建容器和精灵,并添加进app.stage, 并对容器进行35°旋转
// 公路
createHighway () {
// 创建公路容器
const highwayContainer = new PIXI.Container()
// 轴中心【原点】设置为屏幕的中心,默认为左上角0,0
highwayContainer.pivot.x = window.innerWidth/2
highwayContainer.pivot.y = window.innerHeight/2
// 位置
highwayContainer.x = window.innerWidth/2
highwayContainer.y = window.innerHeight/2
// 想在画布显示,必须添加进stage
this.stage.addChild(highwayContainer)
// 35°旋转
highwayContainer.rotation = 35 * Math.PI / 180
// 使用loader.resources,从已加载的资源中创建精灵
const highway = new PIXI.Sprite(this.loader.resources['highway.png'].texture)
// 图片添加进公路容器
highwayContainer.addChild(highway)
}
同理创建自行车,并给自行车着色
// 自行车
createBike () {
const bikeContainer = new PIXI.Container()
this.stage.addChild(bikeContainer)
// 调整bikeLeverImg 和 bikeHandlerbarImg精灵创建的顺序, 从上往下执行
// 从已有资源创建精灵,'brake_bike.png'是key值
const bikeImg = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture)
const bikeLeverImg = new PIXI.Sprite(this.loader.resources['bikeLeverImg.png'].texture)
const bikeHandlerbarImg = new PIXI.Sprite(this.loader.resources['bikeHandlerbarImg.png'].texture)
bikeContainer.addChild(bikeImg)
bikeContainer.addChild(bikeLeverImg)
bikeContainer.addChild(bikeHandlerbarImg)
// 把手中心点
bikeLeverImg.pivot.x = 455
bikeLeverImg.pivot.y = 455
// 把手位置
bikeLeverImg.x = 722
bikeLeverImg.y = 900
// 把手按下的效果:逆时针30°
// bikeLeverImg.rotation = Math.PI/180*-30
//调色器, 自行车填颜色
bikeLeverImg.tint = 0x9AC0CD
bikeImg.tint = 0x9AC0CD
bikeHandlerbarImg.tint = 0x9AC0CD
return {
bikeContainer,
bikeLeverImg
}
}
同理创建按钮,使用gsap进行动画渲染
// 按钮
creatActionButton () {
// 创建容器,用于按钮和圈的偏移
let actionButton = new PIXI.Container()
this.stage.addChild(actionButton)
// initSprite是封装的创建精灵
let btnImg = this.initSprite('btn.png', actionButton)
let btnCircle = this.initSprite('btn_circle.png', actionButton)
let btnCircle2 = this.initSprite('btn_circle.png', actionButton)
// 3、改变圆心, 原点为中心,而不是左上角[正方形]
btnImg.pivot.x = btnImg.pivot.y = btnImg.width/2
btnCircle.pivot.x = btnCircle.pivot.y = btnCircle.width/2
btnCircle2.pivot.x = btnCircle2.pivot.y = btnCircle2.width/2
// actionButton.x = actionButton.y = 400 // 父容器移动到400的位置
// 4、添加动画 gsap.to从当前位置变换为其他位置, gasp.from从其他位置变换为当前位置
// 缩放:从小到大 duration:1为1s, repeat: -1循环播放 alpha: 透明度
btnCircle.scale.x = btnCircle.scale.y = 0.8
// 设置按钮圆圈缩放
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
}
// 初始化精灵图
initSprite(key, stage = this.stage) {
let source = new PIXI.Sprite(this.loader.resources[key].texture)
stage.addChild(source)
return source
}
创建线条,让线条,马路,自行车动起来
show() {
let { highwayContainer, highway } = this.createHighway() // 公路
let { bikeContainer, bikeLeverImg } = this.createBike() // 自行车
bikeContainer.scale.x = bikeContainer.scale.y = 0.3 // 自行车缩小,图片太大了
let actionButton = this.creatActionButton()
actionButton.scale.x = actionButton.scale.y = 0.45
actionButton.x = 430
actionButton.y = 400
actionButton.buttonMode = true // 移动上去箭头变为手
actionButton.interactive = true // 可以交互
// 公路位置
highway.x = window.innerWidth - bikeContainer.width - 1130
highway.y = window.innerHeight - bikeContainer.height - 1130
// 按下效果
actionButton.on('mousedown', () => {
// 把手被按下的效果:逆时针30°
// bikeLeverImg.rotation = Math.PI/180*-30
gsap.to(bikeLeverImg, {duration: 0.6, rotation: Math.PI/180*-30})
// 粒子暂停
pause()
})
actionButton.on('mouseup', () => { // 松开效果 pointerdown
// bikeLeverImg.rotation = 0
gsap.to(bikeLeverImg, {duration: 0.6, rotation: 0})
// 粒子启动
start()
})
let resize = () => {
// 自行车位置
bikeContainer.x = window.innerWidth - bikeContainer.width
bikeContainer.y = window.innerHeight - bikeContainer.height
// 公路位置
highway.x = window.innerWidth - bikeContainer.width - 600
highway.y = window.innerHeight - bikeContainer.height - 600
// 按钮位置
actionButton.x = window.innerWidth - bikeContainer.x + 420
actionButton.y = window.innerHeight - bikeContainer.y - 160
}
window.addEventListener('resize', resize)
resize()
// 5、创建粒子
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
// 粒子向35°旋转
particleContainer.rotation = 35*Math.PI/180
// 粒子有多个颜色
let particles = []
const colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0xFF8C00]
for(let i = 0; i<10; i++) { // 给10个粒子,随机给颜色和位置随机分布
let gr = new PIXI.Graphics()
gr.beginFill(colors[Math.floor(Math.random()*colors.length)])
// 画圆,半径为6
gr.drawCircle(0,0,4)
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)
}
// 向某一个角度持续移动: 让容器一直往y轴向下移动, 容器旋转
// 超出边界后回到顶部继续移动
let speed = 0
function loop() {
speed += .3
speed = Math.min(speed, 20) // 最大速度为20
// 计算粒子
for(let i = 0; i<particles.length; i++) {
let pItem = particles[i]
pItem.gr.y += speed
// pItem.gr.x = 30
if (speed >= 20) { // 圆点变成线, 由慢到快
pItem.gr.scale.y = 30
pItem.gr.scale.x = 0.03
}
if (pItem.gr.y > window.innerHeight) pItem.gr.y = 0 // 超出时回到原点
}
// 计算公路的x, y值
highwayContainer.y += Math.cos(35* Math.PI/180) * speed
highwayContainer.x -= Math.sin(35* Math.PI/180) * speed
// 公路超出重置
if (highwayContainer.y > 400) {
highwayContainer.y = -100
highwayContainer.x = 800
}
}
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: .6, x:pItem.sx, y:pItem.sy, ease: 'elastic.out'})
}
}
start()
// 按住鼠标停止
// 停止的时候还有一点回弹的效果
// 松开鼠标继续
}
最后
我的github: github.com/dpgirl/YCY-… ,以后会尽量输出内容,写的一般请见谅!
在公众号里搜 大帅老猿,在他这里可以学到很多东西。
马路的资源和想法,是参考:juejin.cn/post/711902… 自行车轮子动起来还没实现,但可以参考:juejin.cn/post/712527…