从简单动效开始,开启副业创收之路 | 猿创营

739 阅读2分钟

这是一个真实的私活儿项目,大帅老师整理好后拿来给我们做全员实训。基于PIXI + GSAP来模仿vanmoof电商页面的刹车视觉动效,案例虽小,但里面的知识点很密集,基础的API基本都用到了,是个很好上手的小项目。

效果

pg7np-px7zd.gif

分析

  • 首先准备一些图片素材
  • 其次用一块画布把素材装起来,排好布局
  • 最后让素材动起来

方案

  • 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 来创建图片资源的容器。它添加了对高级渲染功能的内置支持,例如遮罩和过滤。它是所有显示对象的基类,充当其他对象(包括 GraphicsSprite)的容器。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…

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