项目初始化

This commit is contained in:
jerry
2025-01-21 01:46:34 +08:00
parent 364021b042
commit 48153e7761
962 changed files with 172070 additions and 0 deletions

View 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>

View 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
}

View 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() {
}
}

View 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('');
}