码上摸金,用PIXI+GSAP仿写vanmoof刹车动效 | 猿创营
前言
事情是这样的,上个月接了一个活儿。客户要我们模仿一个国外的网站 http://www.vanmoof.com/en-NL/s3?color=dark (需要科学上网) 为他们做几个展示动效。
确实很酷!这个项目一共6个动效,客户给的工期是15天,我们实际花在真正写代码上的工期大概一个礼拜左右。报酬2个W
。
可能有同学觉得赚得也不多...的确不算多,但接活儿赚钱只是一方面,更重要的是在这个项目里我带着很多同学们一起实战获得了宝贵的项目实战经验。
并且这种动效开发,很多只会用vue
,react
写页面的前端同学很少能接触到。
想一起接外包私活儿的朋友加v吧:
dashuailaoyuan
最终效果
先看最终效果
还原得还是很接近的吧,下面我就来一步步教大家如何写
准备好基础环境
http://github.com/ezshine/YCY-TrainingCamp-S1
我也给大家准备好了一个空白项目,大家可以clone
这个仓库一步步来实现。
空白项目很简单,引入了两个库,一个叫PIXI
,是性能非常优秀的webGL2d
渲染引擎。另一个GSAP
,是非常优秀的前端动画引擎,这个库非常老牌,从我还在做FLASH
的时代就一直用它。
```html
```
然后大家安装一下 VSCode
里的 live preview
插件,一定要注意是微软官方出品的,不要装了山寨版。
本教程视频版:http://www.bilibili.com/video/BV1q34y1n7dA/?vd_source=adcbe5bd729e9521863eb85d80c0fe62
创建PIXI应用
```javascript class BrakeBanner{ constructor(selector){ //初始化PIXI应用,将舞台设置为1920x1080 this.app = new PIXI.Application({ width:window.innerWidth, height:window.innerHeight, backgroundColor: 0xffffff, resizeTo:window }); document.querySelector(selector).appendChild(this.app.view);
this.stage = new PIXI.Container();
this.app.stage.addChild(this.stage);
} } ```
加载图片资源
```javascript //创建资源加载器 this.loader = new PIXI.Loader();
//向资源加载器添加资源 key,path 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(); });
this.loader.resources[key]; ```
通过图片创建精灵元素
```javascript const btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture); btnContainer.addChild(btnImage);
const btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture); btnContainer.addChild(btnCircleImage); ```
转换中心点设置
```javascript btnImage.pivot.x = btnImage.pivot.y = btnImage.width/2;
btnCircleImage.pivot.x = btnCircleImage.pivot.y = btnCircleImage.width/2;
btnContainer.x = 200; btnContainer.y = 200; ```
初尝动效GSAP
```javascript btnCircleImage.scale.x = btnCircleImage.scale.y = .8;
gsap.to(btnCircleImage,{duration:1,alpha:0,repeat:-1}) gsap.to(btnCircleImage.scale,{duration:1,x:1.1,y:1.1,repeat:-1}) ```
把按钮相关的代码整合进 createActionButton 函数中
车架和刹车把手
```javascript const bikeContainer = new PIXI.Container(); this.stage.addChild(bikeContainer);
//整体进行一个缩放 bikeContainer.scale.x = bikeContainer.scale.y = .3;
const bikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture); bikeContainer.addChild(bikeImage);
const bikeHandlerImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture); bikeContainer.addChild(bikeHandlerImage); ```
车把手 ```javascript const bikeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture); bikeContainer.addChild(bikeLeverImage);
bikeLeverImage.x = 255+454; bikeLeverImage.y = 450+462;
bikeLeverImage.pivot.x = 454; bikeLeverImage.pivot.y = 462;
bikeLeverImage.rotation = Math.PI/180*-35; ```
现在我们这个刹车的旋转角度已经OK了。
添加按钮交互
javascript
actionBtn.on("mousedown",()=>{
bikeLeverImage.rotation = Math.PI/180*-35;
})
actionBtn.on("mouseup",()=>{
bikeLeverImage.rotation = 0;
})
加入GSAP
javascript
actionBtn.on("mousedown",()=>{
gsap.to(bikeLeverImage,{duration:.6,rotation : Math.PI/180*-35});
})
actionBtn.on("mouseup",()=>{
gsap.to(bikeLeverImage,{duration:.6,rotation : 0});
})
是不是更有感觉了?OK,到这里大家对PIXI和GSAP已经算是入了门了,接下来我们实现这个速度线粒子效果。
将车体永远置于画布右下角
javascript
let resize = () => {
bikeContainer.x = window.innerWidth-bikeContainer.width;
bikeContainer.y = window.innerHeight-bikeContainer.height;
}
window.addEventListener('resize', resize);
resize();
速度粒子效果
怎么做?我们再仔细观察一下参考案例
- 首先得要有粒子,也就是小圆点
- 小圆点的颜色有好几种
- 向某一个角度一直移动
- 超出底部边界后回到顶部继续移动
- 按住鼠标停止
- 停止的时候还有一点回弹的效果
- 松开鼠标继续
OK,一条一条来实现
创建粒子容器和对象数组
```javascript let particleZoneSize = window.innerWidth; let particlesContainer = new PIXI.Container(); this.stage.addChild(particlesContainer); let particles = [];
for(let i=0;i<10;i++){ let gr = new PIXI.Graphics(); gr.beginFill(0xff0000); gr.drawCircle(0,0,6); gr.endFill();
let parItem = { sx:Math.random()particleZoneSize, sy:Math.random()particleZoneSize, }
gr.x = parItem.sx; gr.y = parItem.sy;
particlesContainer.addChild(gr); } ```
粒子随机颜色
javascript
const colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0x818181, 0x000000];
for(let i=0;i<10;i++){
//...
gr.beginFill(colors[Math.floor(Math.random() * colors.length)]);
//...
粒子的持续移动
向一个角度一直移动大家知道算法吗?
javascript
gr.x -= cos(angle) * 1;
gr.y -= sin(angle) * 1;
上述方法不过多的说明,我教大家一个更简单的方法。旋转容器,其实我们只要控制粒子向下移动即可,然后将这个容器旋转一个角度,那么所有粒子的移动方向也就都改变了。
javascript
particlesContainer.pivot.set(particleZoneSize / 2, particleZoneSize / 2);
particlesContainer.rotation = (35 * Math.PI) / 180;
particlesContainer.x = particleZoneSize/2;
particlesContainer.y = particleZoneSize/2;
旋转的时候,我们仍然需要注意一下将旋转的圆心设置到粒子容器的中心点上。
OK,现在让我们让粒子移动起来
通过gsap.ticker.add();
添加一个屏幕渲染时机的钩子,就是requestAnimationFrame
事件。
```javascript function loop(){ for (let i = 0; i < particles.length; i++) { //拿到粒子item和小圆点实例 let pItem = particles[i]; let gr = pItem.gr;
//让粒子走起来
gr.y += 1;
//当粒子移动超出范围时回到顶部
if(gr.y>innerWidth)gr.y=0;
} } gsap.ticker.add(loop); ```
这个速度显然不够快,我们可以多调整一下每次移动的距离看看效果。略微有点感觉了,对吧
然后我们还希望速度是由慢到快的,所以我们定义一个speed的变量,让它从0 开始不断变大,直到一个固定值。
``` let speed = 0; function loop(){ speed+=.5; speed = Math.min(speed,20); for (let i = 0; i < particles.length; i++) { //拿到粒子item和小圆点实例 let pItem = particles[i]; let gr = pItem.gr;
gr.y += speed;
if(gr.y>innerWidth)gr.y=0;
}
} ```
感觉更加舒服了一些,但还是不到位,现在我来教大家一些魔法
人的眼睛观察一个球体直线运动时,球体会发生什么变化?
它会慢慢被拉长,变细对不对?
所以我们用代码来模拟这个拉长变细的效果。
if(speed>=20){
gr.scale.y = 40;
gr.scale.x = 0.03
}
OK ,接下来我们写停止,停止的时候,有一个回弹的效果。
有停止就有启动,把之前的代码定义成一个启动函数,loop函数不变
``` function start(){ speed = 0;
gsap.ticker.add(loop); } ```
停止粒子运动
``` function pause(){ //先移除掉requestAnimationFrame的侦听 gsap.ticker.remove(loop);
for (let i = 0; i < particles.length; i++) { let pItem = particles[i]; let gr = pItem.gr;
//恢复小圆点的拉伸比例
gr.scale.x = gr.scale.y = 1;
//恢复小圆点透明度
gr.alpha = 1;
//让所有的小圆点使用弹性补间动画回到初始坐标
gsap.to(gr, {
duration: 0.6,
x: pItem.sx,
y: pItem.sy,
ease: "elastic.out",
});
}
} ```
OK,把这个两个函数和咱们按钮的按下和松开事件关联上。这个动效就做好了。
最终效果
咱们还原得还是相当可以的吧,动效基本都是这样的原理,希望大家都可以自己去练习一下。
在公众号里搜 大帅老猿
,在他这里可以学到很多东西
- 2022,38岁,裸辞,自由职业一年实况分享
- 太强了!外国小哥花16个月用Three.JS打造了一个无缝切地图的3D开车游戏
- 细节狂魔,用 JavaScript 复原何同学B站头图的创意
- 码上摸金,用PIXI GSAP仿写vanmoof刹车动效 | 猿创营
- 在uni-app中使用微软的文字转语音服务
- 万马奔腾队的phaser3 战疫小游戏开发历程回顾
- 用uni-app开发一个名为汉兜的游戏
- 浅谈对贪食蛇游戏的一点微创新
- 手把手教你做iOS逆向分析,突破微信的群发多选数量限制
- 产品经理:你能不能用div给我画条龙?
- 这个榜单我不服!终究还是错付了这个绿茶掘金
- 前端摸鱼神器,设计稿一键导出 「小程序/Vue/Uni-app」代码
- 请收下这份源码,用Vue开发的一个“蚂蚁森林浇水偷菜”游戏
- 微信小程序统一分享,全局接管页面分享消息的一些技巧
- 这45道面试可能被问到的JS判断题!你能答对几道?
- CSS边玩边学,这五个游戏让你对CSS的掌握更进一步!
- Web网站扫【小程序码】登录的技术实现!记得收藏,要用时别找不到!
- 献给所有技术内容创作者~猿创聚合助手小程序开发难点解析
- 花60秒给Vue3提的PR,竟然被尤大亲自Merge了~
- 使用Vue开发“蚂蚁森林浇水偷菜”游戏的心得体会