项目初始化
This commit is contained in:
267
uni_modules/c-svga/components/c-svga/c-svga.vue
Normal file
267
uni_modules/c-svga/components/c-svga/c-svga.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<!-- #ifdef H5||APP-PLUS -->
|
||||
<view class="c-svga" :style="{width,height}" :svgaData="svgaData" :change:svgaData="svga.render" :fun='fun' :change:fun='svga.callPlayer'>
|
||||
<div :id='myCanvasId'></div>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP -->
|
||||
<view class="c-svga" :style="{width,height}">
|
||||
<canvas class="canvas" style="height: 100vh;" :id="myCanvasId" type="2d"></canvas>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
<script>
|
||||
/**
|
||||
* c-svga svga组件
|
||||
* @property {String} canvasId 画布id
|
||||
* @property {String} width 图像宽度 默认750rpx 单位rpx/px
|
||||
* @property {String} height 图像高度 默认750rpx 单位rpx/px
|
||||
* @property {String} src svga文件地址
|
||||
* @property {Boolean} autoPlay 是否自动播放 默认true
|
||||
* @property {Number} loops 动画循环次数,默认值为 0,表示无限循环
|
||||
* @property {Boolean} clearsAfterStop 默认值为 true,表示当动画结束时,清空画布
|
||||
* @property {String} fillMode 默认值为 Forward,可选值 Forward / Backward,当 clearsAfterStop 为 false 时,Forward 表示动画会在结束后停留在最后一帧,Backward 则会在动画结束后停留在第一帧。
|
||||
* @property {Boolean} isOnChange 是否开启播放进度监听 默认false false时不触发Frame Percentage监听
|
||||
* @event {Function()} loaded 监听svga文件加载完成
|
||||
* @event {Function()} finished 监听动画停止播放 loop!=0时生效
|
||||
* @event {Function()} frame 监听动画播放至某帧
|
||||
* @event {Function()} percentage 监听动画播放至某进度
|
||||
* 组件内方法统一使用 call(funName, args) 调用player实例方法 详见文档
|
||||
* */
|
||||
import uuid from './js/uuid.js'
|
||||
// #ifdef MP&VUE2
|
||||
import {Parser,Player} from '../../node_modules/svgaplayer-weapp'
|
||||
// #endif
|
||||
// #ifdef MP&VUE3
|
||||
import {Parser,Player} from 'svgaplayer-weapp'
|
||||
// #endif
|
||||
export default {
|
||||
name:"c-svga",
|
||||
props: {
|
||||
canvasId: {
|
||||
type: String
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '750rpx'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '750rpx'
|
||||
},
|
||||
src: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
autoPlay: { //是否自动播放
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loops: { //动画循环次数,默认值为 0,表示无限循环。
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
clearsAfterStop: { //默认值为 true,表示当动画结束时,清空画布。
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
fillMode: { //默认值为 Forward,可选值 Forward / Backward,当 clearsAfterStop 为 false 时,Forward 表示动画会在结束后停留在最后一帧,Backward 则会在动画结束后停留在第一帧。
|
||||
type: String,
|
||||
default: 'Forward'
|
||||
},
|
||||
isOnChange: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['loaded', 'finished', 'frame', 'percentage'],
|
||||
data() {
|
||||
return {
|
||||
fun:{}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
myCanvasId() {
|
||||
if (!this.canvasId) {
|
||||
return 'c' + uuid(18)
|
||||
} else {
|
||||
return this.canvasId
|
||||
}
|
||||
},
|
||||
svgaData(){
|
||||
return {
|
||||
myCanvasId: this.myCanvasId,
|
||||
width: this.width,
|
||||
height:this.height,
|
||||
src: this.src,
|
||||
autoPlay:this.autoPlay,
|
||||
loops: this.loops,
|
||||
clearsAfterStop:this.clearsAfterStop,
|
||||
fillMode:this.fillMode,
|
||||
isOnChange:this.isOnChange
|
||||
}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
svgaData(){
|
||||
// #ifdef MP
|
||||
this.render()
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
call(name, args) {
|
||||
this.fun = {name,args}
|
||||
// #ifdef MP
|
||||
this.callPlayer(this.fun)
|
||||
// #endif
|
||||
},
|
||||
// #ifdef MP
|
||||
getContext(){
|
||||
return new Promise((resolve) => {
|
||||
const {
|
||||
pixelRatio
|
||||
} = uni.getSystemInfoSync()
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(`#${this.myCanvasId}`)
|
||||
.fields({
|
||||
node: true,
|
||||
size: true
|
||||
})
|
||||
.exec(res => {
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = res[0]
|
||||
const canvas = res[0].node
|
||||
resolve({
|
||||
canvas,
|
||||
width,
|
||||
height,
|
||||
pixelRatio
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
async render(){
|
||||
if (!this.player) {
|
||||
this.parser = new Parser;
|
||||
this.player = new Player;
|
||||
await this.player.setCanvas('#' +this.myCanvasId,this)
|
||||
}
|
||||
this.player.stopAnimation()
|
||||
this.player.loops = this.loops
|
||||
this.player.clearsAfterStop = this.clearsAfterStop
|
||||
this.player.fillMode = this.fillMode
|
||||
// console.time("test");
|
||||
const videoItem = await this.parser.load(this.src);
|
||||
await this.player.setVideoItem(videoItem);
|
||||
// console.timeEnd("test");
|
||||
this.$emit('loaded')
|
||||
if (this.autoPlay) {
|
||||
this.player.startAnimation();
|
||||
}
|
||||
this.player.onFinished(() => { //只有在loop不为0时候触发
|
||||
// console.log('动画停止播放时回调');
|
||||
this.$emit('finished')
|
||||
})
|
||||
if (this.isOnChange) {
|
||||
this.player.onFrame(frame => { //动画播放至某帧后回调
|
||||
// console.log(frame);
|
||||
try {
|
||||
this.$emit('frame', frame)
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
this.player.onPercentage(percentage => { //动画播放至某进度后回调
|
||||
// console.log(percentage);
|
||||
try {
|
||||
this.$emit('percentage', percentage)
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
callPlayer(val){
|
||||
if (!val.name) return;
|
||||
let {
|
||||
name,
|
||||
args
|
||||
} = val
|
||||
// console.log(name, args);
|
||||
if (Array.isArray(args)) {
|
||||
this.player[name](...args)
|
||||
} else {
|
||||
this.player[name](args)
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
// #ifndef MP
|
||||
receiveRenderData(val) {
|
||||
// console.log(val);
|
||||
this.$emit(val.name, val.val)
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
mounted() {
|
||||
// #ifdef MP
|
||||
this.render()
|
||||
// #endif
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- #ifndef MP -->
|
||||
|
||||
<!-- #ifdef VUE3 -->
|
||||
<script lang="renderjs" src='./js/render.js' module='svga'></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef VUE2 -->
|
||||
<script lang="renderjs" module='svga'>
|
||||
import svgaRender from "./js/render.js"
|
||||
export default {
|
||||
mixins:[svgaRender]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
|
||||
<!-- #endif -->
|
||||
<style lang="scss" scoped>
|
||||
.c-svga {
|
||||
// width: v-bind(width);
|
||||
// height: v-bind(height);
|
||||
|
||||
/* #ifndef MP */
|
||||
div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
left: 0;
|
||||
bottom: calc(0px + constant(safe-area-inset-bottom));
|
||||
bottom: calc(0px + env(safe-area-inset-bottom));
|
||||
pointer-events: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
67
uni_modules/c-svga/components/c-svga/js/getfile.js
Normal file
67
uni_modules/c-svga/components/c-svga/js/getfile.js
Normal file
@@ -0,0 +1,67 @@
|
||||
export default function getfile(e,isIosDown=false) {
|
||||
// #ifdef APP-PLUS
|
||||
let isIOS = plus.os.name==='iOS'
|
||||
let url = plus.io.convertLocalFileSystemURL(e)
|
||||
return new Promise((resolve, reject) => {
|
||||
if (/(http|https):\/\/([\w.]+\/?)\S*/.test(url)) {
|
||||
if(!isIosDown||!isIOS){
|
||||
resolve(e)
|
||||
return
|
||||
}
|
||||
if(isIOS){
|
||||
let dtask = plus.downloader.createDownload(url, {}, function(d, status) {
|
||||
// 下载完成
|
||||
if (status == 200) {
|
||||
let newurl = plus.io.convertLocalFileSystemURL(d.filename);
|
||||
// console.log("Download success: " + newurl);
|
||||
plus.io.resolveLocalFileSystemURL(newurl, entry => {
|
||||
let reader = null;
|
||||
entry.file(file => {
|
||||
reader = new plus.io.FileReader();
|
||||
reader.onloadend = (read) => {
|
||||
resolve(read.target.result)
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}, function(error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}, err => {
|
||||
resolve(e)
|
||||
})
|
||||
|
||||
} else {
|
||||
console.log("Download failed: " + status);
|
||||
reject(status)
|
||||
}
|
||||
})
|
||||
dtask.start();
|
||||
}
|
||||
} else {
|
||||
|
||||
plus.io.resolveLocalFileSystemURL(url, entry => {
|
||||
let reader = null;
|
||||
entry.file(file => {
|
||||
reader = new plus.io.FileReader();
|
||||
reader.onloadend = (read) => {
|
||||
resolve(read.target.result)
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}, function(error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}, err => {
|
||||
resolve(e)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(e)
|
||||
})
|
||||
|
||||
// #endif
|
||||
}
|
120
uni_modules/c-svga/components/c-svga/js/render.js
Normal file
120
uni_modules/c-svga/components/c-svga/js/render.js
Normal file
@@ -0,0 +1,120 @@
|
||||
// #ifdef VUE3
|
||||
import { Howl } from 'howler';
|
||||
import SVGA from 'svgaplayerweb'
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE2
|
||||
import SVGA from '../../../node_modules/svgaplayerweb'
|
||||
import { Howl } from '../../../node_modules/howler';
|
||||
// #endif
|
||||
import getfile from './getfile.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
player:null,
|
||||
parser:null,
|
||||
pdata:{}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dataURLtoBlob(dataURL) {
|
||||
// 获取 Data URL 的数据部分,去掉头信息
|
||||
let parts = dataURL.split(',');
|
||||
let contentType = parts[0].split(':')[1];
|
||||
let raw = parts[1];
|
||||
|
||||
// 将 Data URL 转换为 Blob 对象
|
||||
let byteString = atob(raw);
|
||||
let arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
let uint8Array = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
uint8Array[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
return new Blob([arrayBuffer], {type: contentType});
|
||||
},
|
||||
async render(val,oldValue,vm) {
|
||||
this.$nextTick(async()=>{
|
||||
let data,player,parser;
|
||||
if(val){
|
||||
data =val
|
||||
this.pdata=data
|
||||
}else{
|
||||
data=this.pdata
|
||||
}
|
||||
if(!data.src){
|
||||
//console.error('缺少src');
|
||||
return
|
||||
}
|
||||
if(!this.player){
|
||||
player = new SVGA.Player('#'+data.myCanvasId);
|
||||
parser = new SVGA.Parser();
|
||||
}else{
|
||||
player=this.player
|
||||
parser=this.parser
|
||||
player.stopAnimation()
|
||||
}
|
||||
player.loops=data.loops
|
||||
player.clearsAfterStop=data.clearsAfterStop
|
||||
player.fillMode=data.fillMode
|
||||
// console.time("test");
|
||||
// console.log(await getfile(data.src));
|
||||
// return
|
||||
parser.load(await getfile(data.src),(videoItem)=>{
|
||||
player.setVideoItem(videoItem);
|
||||
// console.log(player,videoItem);
|
||||
// let {audios,images} = videoItem
|
||||
// let audioFile="data:audio/x-mpeg;base64," + images[audios[0].audioKey]
|
||||
// console.log(URL.createObjectURL(this.dataURLtoBlob(audioFile)));
|
||||
// console.timeEnd("test");
|
||||
|
||||
this.$ownerInstance.callMethod('receiveRenderData',{name:'loaded'})
|
||||
if(data.autoPlay){
|
||||
player.startAnimation();
|
||||
}
|
||||
},err=>{
|
||||
console.error(err);
|
||||
})
|
||||
|
||||
player.onFinished(()=>{ //只有在loop不为0时候触发
|
||||
// console.log('动画停止播放时回调');
|
||||
vm.callMethod('receiveRenderData',{name:'finished'})
|
||||
})
|
||||
if(data.isOnChange){
|
||||
player.onFrame(frame=>{ //动画播放至某帧后回调
|
||||
// console.log(frame);
|
||||
vm.callMethod('receiveRenderData',{name:'frame',val:frame})
|
||||
})
|
||||
player.onPercentage(percentage=>{ //动画播放至某进度后回调
|
||||
// console.log(percentage);
|
||||
vm.callMethod('receiveRenderData',{name:'percentage',val:percentage})
|
||||
})
|
||||
}
|
||||
this.player=player
|
||||
this.parser=parser
|
||||
});
|
||||
},
|
||||
async callPlayer(val){
|
||||
if(!val.name)return;
|
||||
let {name, args} = val
|
||||
// console.log(name, args);
|
||||
if(name=='setImage'){
|
||||
args[0]=await getfile(args[0])
|
||||
}
|
||||
if(name=='clearDynamicObjects'){
|
||||
const parser = new SVGA.Parser();
|
||||
parser.load(await getfile(this.pdata.src),(videoItem)=>{
|
||||
this.player.setVideoItem(videoItem);
|
||||
this.player.startAnimation();
|
||||
})
|
||||
}
|
||||
if(Array.isArray(args)){
|
||||
this.player[name](...args)
|
||||
}else{
|
||||
this.player[name](args)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
}
|
23
uni_modules/c-svga/components/c-svga/js/uuid.js
Normal file
23
uni_modules/c-svga/components/c-svga/js/uuid.js
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
export default (len = 32, radix = null) => {
|
||||
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
|
||||
let uuid = [];
|
||||
radix = radix || chars.length;
|
||||
if (len) {
|
||||
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
|
||||
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
|
||||
} else {
|
||||
let r;
|
||||
// rfc4122标准要求返回的uuid中,某些位为固定的字符
|
||||
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
|
||||
uuid[14] = '4';
|
||||
|
||||
for (let i = 0; i < 36; i++) {
|
||||
if (!uuid[i]) {
|
||||
r = 0 | Math.random() * 16;
|
||||
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
|
||||
}
|
||||
}
|
||||
}
|
||||
return uuid.join('');
|
||||
}
|
Reference in New Issue
Block a user