動畫組件(多圖預(yù)警):一套Animation API,實(shí)現(xiàn)React和Vue兩種動畫組件

多圖預(yù)警 多圖預(yù)警 多圖預(yù)警

社會人

在網(wǎng)頁中經(jīng)常會見到效果酷炫的動態(tài)效果,如之前文章分享比較簡單的動態(tài)可交互“粒子-線”效果,如下圖:


網(wǎng)上也有比較多的教程,但是學(xué)習(xí)的最終成果,往往是各個(gè)獨(dú)立的html文件,很難復(fù)用在其他項(xiàng)目中。


而如今,前端開發(fā)講究模塊化以及組件化,所以便想通過一定方式將其封裝為模塊或者組件,方便在其他項(xiàng)目中調(diào)用??紤]到如今比較流行React與Vue組件開發(fā), 所以本文主要分享如何將之前的動效以模塊和React及Vue組件的形式實(shí)現(xiàn)。

效果放前面,具體請戳源碼:

‘粒子-線’組件化.gif

熱力圖.gif
鼠標(biāo)移動--氣泡.gif

使用很是簡單,具體如下:

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=1920, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>React BGAnimation</title>
    <link rel='stylesheet' href='./index.css'></link>
</head>
<body>
    <div id="container" class="txt-center">
        <div class="left">
            <header>
                <h3>React組件</h3>
            </header>
            <main id="root" class="content"> </main>
        </div>
        <div class="right">
            <header>
                <h3>Vue組件</h3>
            </header>
            <main id="app" class="content">
                <app></app>
            </main>
        </div>
    </div>
    <script src='./dist/react.build.js'></script>
    <script src='./dist/vue.build.js'></script>
</body>
</html>
  • Vue使用方法
<template>
    <MouseMovingBubble :width='width' :height='height' :number='number' :style="style"></MouseMovingBubble>
</template>
<script>
export default {
  data() {
    return {
      width: 600,
      height: 500,
      number: 10,
      style: {
        border: "1px solid lightgray"
      }
    };
  }
};
</script>
  • React使用方法
import React from "react";
import ReactDOM from "react-dom";
import * as Coms from "./components/index";
const mountDom = document.getElementById("root");
ReactDOM.render(
  <Coms.MouseMovingBubble width="600" height="500" number={10} />,
  mountDom
);

動畫

我們都知道,動畫只不過是一個(gè)短時(shí)間內(nèi)連續(xù)繪制不同圖形(幀)產(chǎn)生的效果.通過requestAnimationFrame可以實(shí)現(xiàn)流暢的動畫,因?yàn)闉g覽器內(nèi)部對動畫執(zhí)行做了優(yōu)化(與瀏覽器渲染同步);不建議使用setTimeInterval,因?yàn)檫@是個(gè)不靠譜的家伙!

那么下邊介紹一下我自己理解的一個(gè)動畫的基本框架:

  • 初始化(Init):完成一些數(shù)據(jù)的預(yù)處理和畫布畫筆的創(chuàng)建等
  • 步進(jìn)(Step):完成每一幀動畫圖形變化需要做的操作(如清空畫布/計(jì)算新的位置等)
  • 循環(huán)(Loop):完成動畫的循環(huán),即循環(huán)執(zhí)行步進(jìn)(Step)方法

是不是很簡單呢?哈哈 下邊動手實(shí)現(xiàn)以下:

所有動畫父類:IAniamtion

通過分析完成一個(gè)動畫效果的過程,我們可以抽象出一個(gè)基類(IAnimation),定義所有動畫必須實(shí)現(xiàn)的方法以及對一下公共操作進(jìn)行封裝.

export default class IAnimation {
  constructor(props) {
    // 接收配置參數(shù)
    this.canvas = props.canvas;
    this.width = parseInt(props.width);
    this.height = parseInt(props.height);
    // 為方法綁定this
    this.init = this.init.bind(this);
    this.step = this.step.bind(this);
    this.loop = this.loop.bind(this);
  }
  init() {
    // 初始化畫布和畫筆
    this.ctx = this.canvas.getContext("2d");
    if (!this.ctx) throw "瀏覽器不支持Canvas,請使用其他瀏覽器試試!";
    this.canvas.width = this.width;
    this.canvas.height = this.height;
  }
  loop() {
    // 每次loop前清空之前畫布的內(nèi)容
    this.ctx.clearRect(0, 0, this.width, this.height);
  }
  step() {
    // 步進(jìn)
  }
}

"鼠標(biāo)移動--氣泡"動畫實(shí)現(xiàn)

以"鼠標(biāo)氣泡"為例,簡單說明實(shí)現(xiàn)動畫的過程.因?yàn)檫壿嫴粡?fù)雜,所以實(shí)現(xiàn)也比較簡單.

import IAnimation from "./IAnimation";
import { Extent, Particle } from "../graphic";
import { random } from "../utils";

export default class MouseMovingBubbleAnimation extends IAnimation {
  constructor(props) {
    super(props);
    // 每次鼠標(biāo)移動產(chǎn)生的粒子數(shù)
    this.number = props.number || 0;
    this.oArray = [];
  }
  init() {
    // 調(diào)用父類(IAnimation)的init方法
    super.init();
    var self = this;
    // 監(jiān)視鼠標(biāo)移動事件
    this.canvas.onmousemove = function(e) {
      const mpos = { x: e.offsetX, y: e.offsetY };
      for (let i = 0; i < self.number; i++) {
        // 創(chuàng)建粒子
        let oPr = new Particle();
        oPr.range = new Extent([0, 0], [self.width, self.height]);
        oPr.x = mpos.x;
        oPr.y = mpos.y;
        oPr.radius = random(8, 15);
        oPr.speed = 20 / oPr.radius;
        oPr.direction = random(0, 2 * Math.PI);
        self.oArray.push(oPr);
      }
    };
    // 執(zhí)行動畫
    self.loop();
  }
  loop() {
    // 判斷粒子數(shù)
    // 當(dāng)例子說大于0的時(shí)候執(zhí)行步進(jìn)操作
    if (this.oArray.length > 0) {
      super.loop();
      this.step();
    }
    requestAnimationFrame(this.loop);
  }
  step() {
    // 調(diào)用父類步進(jìn)(step)方法
    super.step();
    let deletAry = [];
    this.oArray.forEach(p => {
      // 粒子步進(jìn)
      p.step();
      // 減小粒子半徑
      p.radius -= p.oldRadius / 50;
      // 直到小于0 消失
      if (p.radius <= 0) deletAry.push(p);
      else p.draw(this.ctx);
    });

    // 移除消失的粒子
    deletAry.forEach(p => {
      let index = this.oArray.indexOf(p);
      this.oArray.splice(index, 1);
    });
  }
}

React與Vue組件封裝

其實(shí)在動畫邏輯實(shí)現(xiàn)好了的情況下,封裝組件是一件異常簡單的事情(暫時(shí)不考慮復(fù)雜的邏輯或者交互),只需要在組件特定的生命周期執(zhí)行動畫實(shí)例的相應(yīng)發(fā)方法即可.這里以為需要傳入canvas DOM作為動畫參數(shù),所以在組件掛載之后創(chuàng)建了動畫實(shí)例,并執(zhí)行了初始化方法.

具體以"鼠標(biāo)移動--氣泡"動畫為例說明React和Vue封裝:

  • MouseMovingBubble.jsx
import React from "react";
import { MouseMovingBubbleAnimation } from "../../../common/index";

export default class Particle extends React.Component {
  constructor(props) {
    super(props);
  }

  // 掛載后創(chuàng)建動畫實(shí)例
  componentDidMount() {
    this.animation = new MouseMovingBubbleAnimation ({
      canvas: this.oCanvas,
      width: this.props.width,
      height: this.props.height,
      number: this.props.number
    });
    // 執(zhí)行初始化方法
    this.animation.init();
  }

  render() {
    return (
      <canvas style={this.props.style} ref={canvas => (this.oCanvas = canvas)}>
        您的瀏覽器不支持Canvas,請使用其他瀏覽器試試看!
      </canvas>
    );
  }
}

// 默認(rèn)配置參數(shù)
Particle.defaultProps = {
  width: 1080,
  height: 512,
  number: 10,
  style: {
    border: "1px solid lightgray"
  }
};
  • MouseMovingBubble.vue
<template>
    <canvas :width="width" :height="height">您的瀏覽器不支持Canvas,請使用其他瀏覽器試試看!</canvas>
</template>

<script>
import { MouseMovingBubbleAnimation } from "../../../common/animation/index";
export default {
  // 默認(rèn)參數(shù)配置
  props: {
    width: {
      type: Number,
      default: 300
    },
    height: {
      type: Number,
      default: 300
    },
    number: {
      type: Number,
      default: 30
    }
  },
  mounted() {
    // 組件掛載后創(chuàng)建動畫實(shí)例
    const animation = new MouseMovingBubbleAnimation ({
      width: this.width,
      height: this.height,
      number: this.number,
      canvas: this.$el
    });
    // 初始化動畫
    animation.init();
  }
};
</script>

<style>
</style>

其他的效果實(shí)現(xiàn),暫時(shí)不在這里拋代碼了,感興趣的可以去看源碼,后面也會針對個(gè)別效果做詳細(xì)介紹...


如果您感覺有所幫助,或者有問題需要交流,歡迎留言評論,非常感謝!
前端菜鳥,還請多多關(guān)照!


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容