libpag 动效图封装

296 阅读3分钟

vue

getCurrentInstance

getCurrentInstance 是 Vue 3 中的一个函数,用于获取当前组件实例。它可以在组件内部的任何地方使用。 在 Vue 3 中,由于 Composition API 的引入,我们可以在组件中使用函数式的方式编写代码。 getCurrentInstance 函数可以获取到当前组件实例的引用,从而可以访问组件实例的属性、方法和生命周期钩子

import { getCurrentInstance } from 'vue';

export default {
  mounted() {
    const instance = getCurrentInstance();
    console.log(instance.props); // 访问组件的 props
    console.log(instance.emit); // 访问组件的自定义事件方法
  },
};

通过vite实现按需加载 依赖包

// "agora-rtc-sdk-ng": "^4.18.2", 这个依赖包

// 1 安装plugin-legacy插件,兼容低版浏览器
 npm install --save-dev @vite/plugin-legacy
// 2 在vite.config.js中配置 
import { defineConfig } from 'vite';
import legacy from '@vite/plugin-legacy';

export default defineConfig({
  plugins: [
    legacy({
      targets: ['ie >= 11'],
      additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
    }),
  ],
});
// 3 代码中使用动态导入(dynamic import)来按需加载 Agora RTC SDK NG:
async function loadAgoraSDK() {
  const AgoraRTC = await import('agora-rtc-sdk-ng');
  // 使用 AgoraRTC
}

loadAgoraSDK();
// 这样,当你的代码执行到  loadAgoraSDK  函数时,Agora RTC SDK NG 将会被动态加载

libpag封装

import { useLibpag } from '../stores';
import { toRaw } from 'vue';
export class LibPag {
  buffer;
  pagFile;
  canvas;
  pagView;
  audioEl;
  id = Symbol(); // 消息队列的唯一标识

  constructor(pagUrl, dom, repeat = 1) {
    this.pagUrl = pagUrl;
    this.dom = dom;
    this.repeat = repeat;
  }

  async init(PAG) {
    this.buffer = await fetch(this.pagUrl).then((response) => response.arrayBuffer());
    this.pagFile = await PAG.PAGFile.load(this.buffer);
    const { height, width } = this.dom?.getBoundingClientRect();
    this.dom.innerHTML ='';
  //  const childNodes = this.dom.childNodes;
  //  let i = childNodes.length; 
  //  while (i--) { 
  //    this.dom.removeChild(childNodes[i]); 
  //  } 
    this.canvas = document.createElement('canvas');
    this.canvas.width = width;
    this.canvas.height = height;
    // this.canvas.width = this.pagFile.width();
    this.dom.appendChild(this.canvas);
  }

  async mount(PAG, firstFrame = true, useScale = true) {
    // Create PAGView.
    this.pagView = await PAG.PAGView.init(this.pagFile, this.canvas, { firstFrame, useScale });
    this.pagView.setRepeatCount(this.repeat);
    this.pagView.addListener('onAnimationEnd', (view) => {
      this.destroy();
      clearSingle(this.dom, this.id);
    });
    return this.dom;
  }

  

  audioPlay(){  // 如果涉及audio
    const audioBytes = this.pagFile.audioBytes();
    if (audioBytes?.byteLength > 0) {
      this.audioEl = document.createElement('audio');
       this.audioEl.preload = 'auto';
      const blob = new Blob([audioBytes], { type: 'audio/mp3' });
      this.audioEl.src = URL.createObjectURL(blob);
      this.pagView.addListener('onAnimationStart', (event) => {
        this.audioEl.play();
      });
      this.pagView.addListener('onAnimationEnd', (event) => {
        this.audioEl.pause();
      });
      this.pagView.addListener('onAnimationCancel', (event) => {
        this.audioEl.pause();
      });
      this.pagView.addListener('onAnimationRepeat', (event) => {
        this.audioEl.currentTime = 0;
      });
    }
  }

  // 播放
  async play() {
    this.audioPlay();
    try {
      await this.pagView?.play();
    } catch (error) {
      this.destroy()
    }
   
  }

  // 暂停
  pause() {
    this.pagView?.pause();
  }
  // 停止
  stop() {
    this.pagView?.stop();
  }
  //  回收无用的 PAGView 实例和移除 Canvas 的引用
  destroy() {
     // 释放音频URL
    this.audioEl?.pause();
    URL.revokeObjectURL(this.audioEl?.src);
    this.pagView?.destroy();
    this.dom.removeChild(this.canvas);
  }

  // 设置动画次数
  setRepeat(n) {
    this.repeat = n;
  }

  // 设置进度
  setPropress(propress) {
    // 值是0-1
    this.pagView.setProgress(propress);
  }

  // 获取时长
  getDuration() {
    return this.pagFile.duration();
  }

  // 获取可编辑图片数量
  getEditNums() {
    return this.pagFile.numImages();
  }

  // 替换image
  async replaceImage(PAG, img, index = 0) {
    const image = await this.loadImage(img); // ./cat.png
    if (!image) return;
    const pagImage = PAG.PAGImage.fromSource(image);
    this.pagFile.replaceImage(index, pagImage); // 替换索引为index的图片 默认为0
  }

  // 替换文字
  repalceText(text, index = 0) {
    const textDoc = this.pagFile.getTextData(index);
    textDoc.text = text;
    this.pagFile.replaceText(index, textDoc);
  }

  // 替换文字以及样式 ,传入一个方法
  replaceStyle(fn, ...args) {
    fn?.apply(that, args);
  }

  // 加载图片
  loadImage(src) {
    return new Promise((resolve) => {
      const image = new Image();
      image.onload = () => {
        resolve(image);
      };
      image.onerror = () => {
        resolve(false);
      };
      image.crossOrigin = 'anonymous';
      image.src = src;
    });
  }
}

 // 清除当前dom元素上的一个动画
 export const clearSingle = (dom,id)=>{
    // 注销后,将对应的pag在消息队列中删除
    const pagStore = useLibpag();
    pagStore.clearSingle(dom, id);
  }


 // 清除当前dom元素上的所有动画
 export const clearDomNode =(dom) =>{
    const pagStore = useLibpag();
    const pags = pagStore.getPag(dom);
    if (pags?.length) {
      pagStore.clearDomNode(dom);
        for(let ele of pags){
          console.log(ele, 11)
            toRaw(ele).destroy();
        }
        
    }
  }

  // 清除当前所有动效
export const clearAll = () =>{
    const pagStore = useLibpag();
    pagStore.clearAll();
    toRaw(pagStore.pagMap).forEach((item) => {
      if (item?.length) {
        for(let ele of item){
            toRaw(ele).destroy();
        }
      }
    });
  }

import { defineStore } from 'pinia'

export default defineStore('useLibpag', {
    state: () => ({ pagMap: new Map([]), }),
    getters: {
       
    },
    actions: {
        getPag(key) {
            return this.pagMap.get(key);
        },
        incrementPag(key, value){
            if(this.pagMap.has(key)){
                this.pagMap.set(key, [value,...this.pagMap.get(key)])

            }else{
                this.pagMap.set(key,[value])
            }
        },
        clearSingle(key, id){
            if(this.pagMap.has(key)){
                const idMaps = this.pagMap.get(key);
                const index = idMaps.findIndex(item=>item.id===id)
                if(index!=-1){
                    idMaps.splice(index, 1);
                }
                idMaps == []?this.pagMap.delete(key) : this.pagMap.set(key, idMaps);
            }  
        },
        clearDomNode(key){
            if(this.pagMap.has(key)){
                this.pagMap.delete(key);
            }
        },
        clearAll(){
            this.pagMap.clear();
        }   


    },
})
<script setup>
import { ref, onMounted, toRaw  } from 'vue'
import { PAGInit, types } from 'libpag';
import { useLibpag } from '../stores'
import { LibPag, clearDomNode, clearAll, clearSingle } from '../utils/pag'

let PAG;
const count = ref(0);
const myRef = ref(null);
const myRef2 = ref(null);
const arr = [ './sd.pag','./music.pag' ,'./snowman.pag', './like.pag', './text.pag', './city.pag', './jp.pag', './1.pag', './2.pag', './3.pag']
const pagStore = useLibpag();
let timer =null;
onMounted(async ()=>{

      PAG = await PAGInit();
      const pagUrl2 = './1.pag';
      const imgSrc = './cat.png';
       //add()

       timerEvent()

    
    // const pagObj1 = new LibPag(pagUrl2, myRef2.value, 0);
    // await pagObj1.init(PAG);
    // const dom = await pagObj1.mount(PAG);
    // pagStore.incrementPag(dom, pagObj1);
    // await pagObj1.play();
    // console.log(pagStore.pagMap)



    // const pagObj2 = new LibPag(pagUrl2, myRef2.value, 0);
    // await pagObj2.init(PAG)
    // await pagObj2.replaceImage(PAG, imgSrc, 1)
    // await pagObj2.mount(PAG);
    // pagStore.incrementPag(myRef2.value, pagObj2); // 保存图片实例
    // await pagObj2.play();


    //  for(let i=0;i<arr.length;i++){
    //     const pagUrl = arr[i];
    //     const pagObj1 = new LibPag(pagUrl, myRef2.value, 1);
    //     await pagObj1.init(PAG);
    //     await pagObj1.mount(PAG);
    //     await pagObj1.play();
    // }

})


function timerEvent(){
  console.log(123)
  timer = setInterval(() => {
        add()
      }, 5000);
}

document.addEventListener("visibilitychange", () => {
            if (document.hidden) {
                console.log('页面被挂起')
                clearInterval(timer);
            }else{
              console.log('页面呼出')
              timerEvent();
            }
          })

async function add(){
     // 清除之前的动效
    //  const pags = pagStore.getPag(myRef.value);
    //  console.log(1, pags)
    //  if(pags?.length){
    //   console.log(pags)
    //   toRaw(pags).forEach(element => {
    //     element.destroy();
    //   });
    //  }

     clearDomNode(myRef.value)
    // 添加新的动效
    console.log(arr.length, count.value)
    const pag = new LibPag(arr[count.value], myRef.value,0);
    await pag.init(PAG);
    await pag.mount(PAG);
    pagStore.incrementPag(myRef.value, pag);
    await pag.play();
   console.log(pagStore.pagMap)

    count.value< arr.length - 1? count.value++ : (count.value=0);

}


function deletec(){
  clearDomNode(myRef.value)
}

function deleteAll(){
  clearAll();
}



</script>

<template>
     <div class="mydom2" ref="myRef"></div>
     <div class="mydom" ref="myRef2"></div>
     <!-- <div class="mydom2" ref="myRef2"></div> -->

     <button @click="add"> 添加新动效</button>
     <button @click="deletec"> 删除动效</button>
     <button @click="deleteAll"> 删除所有动效</button>
</template>

<style>
  .mydom{
    width: 100%;
    height: 300px;
  }
  .mydom2{
    position: fixed;
    z-index: 22;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;

  }


</style>