5G时代都来了,你需要认识一下它-PIXI.JS|猿创营

772 阅读4分钟

背景

没动手之前总觉得做这类炫酷的效果很难,不知道从哪里下手。

这次终于在老帅的鞭策(其实是诱惑)下动手了。

看完===学会 如果觉得还错,点个赞给予鼓励吧

技术背景

PIXI.JS中文网

  • 据说是在2D渲染方面,PixiJS是最快的。
  • 运行web服务,否则Pixi将无法工作

动画引擎库-GSAP

pixi的基本概念

先基本了解一下PIXI的基础内容,做到心中有数。

stage 舞台

即一个特殊的Pixi对象叫做stage(舞台)。 通过 app.view获取舞台


//Create a Pixi Application
let app = new PIXI.Application({width: 256, height: 256});
document.body.appendChild(app.view);

sprites 精灵

sprite(精灵)是一种的特殊图像对象。您可以控制它们的位置、大小和其他属性。类似于舞台上的演员,他们有可以有自己的位置和特征。

1. 定位 :根据 x、y轴 控制定位

    x=10
    y=20
    // 缩写
    position.set(x, y)

2. 大小 :根据width 和 height控制大小

3. 缩放:sacale ,scale.xscale.y

值表示纹理尺寸的百分比,从0到1(0%到100%)。

         scale.x =0.5
        scale.y  = 0.5
        // 缩写
        scale.set(0.5, 0.5);

4. 旋转:rotation

rotation = 0.5
// 正常角度转换 π/180*deg

5. 锚点:anchoranchor.xanchor.y

值表示纹理尺寸的百分比,从0到1(0%到100%)。

    anchor.x =0.5
    anchor.y  = 0.5
    // 缩写
    anchor.set(0.5, 0.5);

6.轴心点:pivot设置精灵的原点。

pivot.xpivot.y 按像素计算。 类似于锚点

   pivot.x =30
   pivot.y  = 30
   // 缩写
   pivot.set(30, 30);
demo.png ### texture 纹理 因为Pixi使用WebGL在GPU上渲染图像,图像需要转换为GPU可以处理的东西

可以利用纹理texture(纹理),使用Pixi的Sprite类创建精灵

loader 加载器

PIXI.loader
  .add("images/anyImage.png")
  .load(setup);

function setup() {
  let sprite = new PIXI.Sprite(
    PIXI.loader.resources["images/anyImage.png"].texture
  );
}

精灵分组 Container

new PIXI.Container() 创建容器,再 使用addChild 加入精灵

    let container = new PIXI.Container();
    container.addChild(sprite1);
    container.addChild(sprite2);

粒子容器 ParticleContaine

任何在ParticleContainer中的精灵渲染速度都比在常规容器中快2到5倍

    let superFastSprites = new PIXI.particles.ParticleContainer();

如果想在ParticleContainer中更改子画面的rotationscaletintuvs,则必须将这些属性设置为true,如下所示:

let superFastSprites = new ParticleContainer(
  size, 
  {
    rotation: true,
    alphaAndtint: true,
    scale: true,
    uvs: true
  }
);

图形绘制

  • 需创建Pixi的Graphics类(Pixi.Graphics)的实例
  • 填充颜色: beginFill
  • 外边框: lineStyle
  • 绘制图形:drawRect drawCircledrawEllipsedrawRoundedRectdrawPolygon
  • 结束绘制:endFill drawRect
let rectangle = new Graphics();
rectangle.lineStyle(4, 0xFF3300, 1);
rectangle.beginFill(0x66CCFF);
rectangle.drawRect(0, 0, 64, 64);
rectangle.endFill();
rectangle.x = 170;
rectangle.y = 170;
app.stage.addChild(rectangle);

动手实战

需求分析

  1. 刹车动效制作
  2. 刹车按钮动画制作
  3. 下雨动画制作
  4. 刹车效果和下雨动画同步

上代码

导入所有元素

  1. 创建容器
  2. 加载所有图片
  3. 绑定加载后事件 (展示动画)
class BrakeBanner {
  constructor(selector) {
    this.app = new PIXI.Application({
      width: window.innerWidth,
      height: window.innerHeight,
      backgroundColor: 0xffffff,
      resizeTo: window,
    });
    document.querySelector(selector).appendChild(this.app.view);
    this.stage = this.app.stage;
    this.loader = new PIXI.Loader();

    this.loader
      .add("btn_circle.png", "images/btn_circle.png")
      .add("brake_bike.png", "images/brake_bike.png")
      .add("brake_handlerbar.png", "images/brake_handlerbar.png")
      .add("brake_lever.png", "images/brake_lever.png")
      .add("btn.png", "images/btn.png");
    this.loader.load();
    this.loader.onComplete.add(() => {
      this.show(); // 动画操作
    });
  }

展示动画。 细分模块 : 自行车模块、按钮模块、下雨动画模块

show() {
    this.showBrake();
    this.showPoint();
    this.showBtn();
  }

1、 自行车模块

  1. 加入自行车所有元素
  2. 调整所有图片的位置
  3. 绑定窗口变化事件,自适应
  4. 将需要用于动画的元素挂载到根实例,用于与按钮联动效果制作
  showBrake() {
    const bikeContainer = new PIXI.Container();
    bikeContainer.scale.set(0.3, 0.3);
    this.stage.addChild(bikeContainer);
    let brake_bike = new PIXI.Sprite(
      this.loader.resources["brake_bike.png"].texture
    );
    let brake_handlerbar = new PIXI.Sprite(
      this.loader.resources["brake_handlerbar.png"].texture
    );
    let brake_lever = new PIXI.Sprite(
      this.loader.resources["brake_lever.png"].texture
    );
    brake_lever.pivot.set(455, 455);
    brake_lever.position.set(722, 900);
    this.brake_lever = brake_lever;
    this.bikeContainer = bikeContainer;
    bikeContainer.addChild(brake_bike, brake_lever, brake_handlerbar);
    let resize = () => {
      bikeContainer.position.set(
        window.innerWidth - bikeContainer.width,
        window.innerHeight - bikeContainer.height
      );
    };
    window.addEventListener("resize", resize);
    resize();
  }
}

2、下雨模块

  1. 创建例子容及粒子
  2. 将粒子容器倾斜
  3. 制作粒子动画(加速粒子变形、速度由慢变快)
  4. 将动画开始和暂定事件 绑定在跟容器
showPoint() {
    
    let pointContainer = new PIXI.Container();
    pointContainer.rotation = (35 * Math.PI) / 180;
    pointContainer.pivot.set(window.innerWidth / 2, window.innerHeight / 2);
    pointContainer.position.set(window.innerWidth / 2, window.innerHeight / 2);
    this.stage.addChild(pointContainer);
    let particles = [];
    let colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0x818181, 0x000000];
    for (let i = 0; i <= 20; i++) {
      let rectangle = new PIXI.Graphics();
      rectangle.beginFill(colors[Math.floor(Math.random() * colors.length)]);
      rectangle.drawCircle(0, 0, 3);
      rectangle.endFill();
      pointContainer.addChild(rectangle);
      let x = Math.random() * window.innerWidth;
      let y = Math.random() * window.innerHeight;
      rectangle.position.set(x, y);
      particles.push({ gr: rectangle, sx: x, sy: y });
    }
    let speed = 10;
    function loop() {
      speed += 0.5;
      speed = Math.min(20, speed);
      particles.forEach((pItem) => {
        pItem.gr.y += speed;
        if (speed >= 20) {
          pItem.gr.scale.set(0.03, 40);
        }
        if (pItem.gr.y >= window.innerHeight) {
          pItem.gr.y = pItem.sy;
        }
      });
    }
    function start() {
      gsap.ticker.add(loop);
    }
    function pause() {
      gsap.ticker.remove(loop);
      particles.forEach((pItem) => {
        pItem.gr.scale.set(1, 1);
        gsap.to(pItem.gr, {
          duration: 0.6,
          x: pItem.sx,
          y: pItem.sy,
          ease: "elastic.out",
        });
      });
    }
    start();
    this.pause = pause;
    this.start = start;
  }

3、按钮模块

  1. 加载按钮元素
  2. 设置按钮位置
  3. 绑定按钮点击事件(下雨效果及刹车动效)
  4. 将按钮容器挂载到根实例,用于后期动画同步

  showBtn() {
    let actionButton = new PIXI.Container();
    actionButton.position.set(900, 800);
    actionButton.scale.set(2, 2);
    this.bikeContainer.addChild(actionButton);
    let btn = new PIXI.Sprite(this.loader.resources["btn.png"].texture);
    let btn_circle = new PIXI.Sprite(
      this.loader.resources["btn_circle.png"].texture
    );
    let btn_circle2 = new PIXI.Sprite(
      this.loader.resources["btn_circle.png"].texture
    );

    actionButton.addChild(btn, btn_circle, btn_circle2);
    btn.pivot.set(btn.width / 2, btn.width / 2);
    btn_circle.anchor.set(0.5, 0.5);
    btn_circle2.anchor.set(0.5, 0.5);
    gsap.to(btn_circle2.scale, { duration: 1, x: 1.2, y: 1.2, repeat: -1 });
    gsap.to(btn_circle2, { duration: 1, alpha: 0, repeat: -1 });
    actionButton.interactive = true;
    actionButton.buttonMode = true;
    actionButton.on("mousedown", () => {
      gsap.to(this.brake_lever, {
        duration: 0.6,
        rotation: (Math.PI / 180) * -30,
      });
      this.pause();
      // this.brake_lever.rotation = (Math.PI / 180) * -30;
    });
    actionButton.on("mouseup", () => {
      gsap.to(this.brake_lever, {
        duration: 0.6,
        rotation: 0,
      });
      this.start();
      // this.brake_lever.rotation = 0;
    });
    this.actionButton = actionButton;
  }

最后

公众号里搜 大帅老猿,在他这里可以学到很多东西!