Commit 8ed5e9af authored by haiyoucuv's avatar haiyoucuv

init & svga

parent c32b7f75
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from "react"; import React, { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
import { Player } from 'svga'; import { Player } from 'svga';
import { loadSvga } from "../utils/loader.ts"; import { loadImage, loadSvga } from "../utils/loader.ts";
import { VideoSprite } from "svga/dist/types";
export interface BaseSvgaPlayerProps { export interface BaseSvgaPlayerProps {
className?: string;
styles?: { icon: React.CSSProperties };
src: string; src: string;
autoplay?: boolean; autoplay?: boolean;
loop?: boolean; loop?: number;
startFrame?: number, startFrame?: number,
endFrame?: number, endFrame?: number,
onProcess?: (current: number, total: number) => void;
onStart?: () => void; onStart?: () => void;
onPause?: () => void;
onResume?: () => void;
onStop?: () => void;
onEnd?: () => void; onEnd?: () => void;
} }
...@@ -22,17 +25,21 @@ export interface BaseSvgaPlayerProps { ...@@ -22,17 +25,21 @@ export interface BaseSvgaPlayerProps {
type MergedHTMLAttributes = Omit< type MergedHTMLAttributes = Omit<
React.HTMLAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement> &
React.CanvasHTMLAttributes<HTMLElement>, React.CanvasHTMLAttributes<HTMLElement>,
'type' | 'color' 'onPause'
>; >;
export type HTMLDisplayElement = HTMLImageElement | HTMLCanvasElement | ImageBitmap;
export interface SvgaPlayerRef { export interface SvgaPlayerRef {
nativeElement: HTMLCanvasElement; nativeElement: HTMLCanvasElement;
player: Player; getPlayer: () => Player;
start: () => Player; start: () => Player;
pause: () => Player; pause: () => Player;
resume: () => Player; resume: () => Player;
stop: () => Player; stop: () => Player;
replaceImage: (name: string, img: string | HTMLDisplayElement) => Promise<HTMLDisplayElement>;
addImage: (parentName: string, targetName: string, img: string | HTMLDisplayElement, width?: number, height?: number) => void;
removeImage: (targetName: string) => void;
} }
...@@ -49,7 +56,8 @@ export const SvgaPlayer = forwardRef< ...@@ -49,7 +56,8 @@ export const SvgaPlayer = forwardRef<
src, autoplay = true, loop = true, src, autoplay = true, loop = true,
startFrame = 0, startFrame = 0,
endFrame = 0, endFrame = 0,
onStart, onEnd, onStart, onPause, onResume, onStop, onEnd,
onProcess,
} = props; } = props;
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
...@@ -63,7 +71,7 @@ export const SvgaPlayer = forwardRef< ...@@ -63,7 +71,7 @@ export const SvgaPlayer = forwardRef<
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
nativeElement: canvasRef.current, nativeElement: canvasRef.current,
player: playInfo.current.player, getPlayer: () => playInfo.current.player,
start: () => { start: () => {
playInfo.current.player.start(); playInfo.current.player.start();
return playInfo.current.player; return playInfo.current.player;
...@@ -79,30 +87,88 @@ export const SvgaPlayer = forwardRef< ...@@ -79,30 +87,88 @@ export const SvgaPlayer = forwardRef<
stop: () => { stop: () => {
playInfo.current.player.stop(); playInfo.current.player.stop();
return playInfo.current.player; return playInfo.current.player;
} },
async replaceImage(name: string, img: string | HTMLDisplayElement): Promise<HTMLDisplayElement> {
const player = playInfo.current.player;
if (playInfo.current.player && playInfo.current.player.videoEntity) {
if (typeof img === 'string') {
img = await loadImage(img)
}
// @ts-ignore
player.bitmapsCache[name] = img
return img;
}
},
async addImage(parentName: string, targetName: string, img: string | HTMLDisplayElement, width?: number, height?: number) {
const player = playInfo.current.player;
if (player && player.videoEntity) {
let videoItem = player.videoEntity
let parent, target, parentIndex
videoItem.sprites.forEach((sprite, index) => {
if (sprite.imageKey === parentName) {
parent = sprite
parentIndex = index
}
if (sprite.imageKey === targetName) {
target = sprite
}
})
if (!parent) {
console.warn(`父节点[${parentName}]不存在`)
return
}
if (target) {
console.warn(`节点[${targetName}]已存在`)
} else {
const imgEl = await this.replaceImage(targetName, img)
let targetFrames = JSON.parse(JSON.stringify(parent.frames))
for (let { layout, transform } of targetFrames) {
layout.width = width || imgEl.width;
layout.height = height || imgEl.height;
transform._tx = transform.tx;
transform._ty = transform.ty;
}
let frame = {
imageKey: targetName,
frames: targetFrames,
}
videoItem.sprites.splice(parentIndex + 1, 0, frame)
}
}
},
removeImage(targetName: string) {
const player = playInfo.current.player;
if (player && player.videoEntity) {
let videoItem = player.videoEntity
let target: VideoSprite
videoItem.sprites.forEach((sprite, index) => {
if (sprite.imageKey === targetName) {
target = sprite
videoItem.sprites.splice(index, 1)
}
})
if (!target) {
console.warn(`节点[${targetName}]不存在`)
}
}
},
})); }));
useEffect(() => { useEffect(() => {
const player = new Player({ container: canvasRef.current }); const player = new Player({ container: canvasRef.current });
playInfo.current.player = player; playInfo.current.player = player;
player.onStart = () => { player.onStart = () => onStart && onStart();
onStart && onStart(); player.onPause = () => onPause && onPause();
} player.onResume = () => onResume && onResume();
player.onResume = () => { player.onStop = () => onStop && onStop();
console.log('onResume') player.onProcess = () => onProcess && onProcess(player.currentFrame, player.totalFrames);
} player.onEnd = () => onEnd && onEnd();
player.onPause = () => {
console.log('onPause')
}
player.onStop = () => {
console.log('onStop')
}
// player.onProcess = () => console.log('onProcess', player);
player.onEnd = () => {
console.log('onEnd')
onEnd && onEnd();
}
return () => { return () => {
player.destroy(); player.destroy();
......
...@@ -55,3 +55,17 @@ export const loadSvgaList = async (srcList: string[]) => { ...@@ -55,3 +55,17 @@ export const loadSvgaList = async (srcList: string[]) => {
return await Promise.all(ps); return await Promise.all(ps);
} }
export function loadImage(src: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function () {
resolve(img);
};
img.onerror = function (e) {
reject(e);
};
img.src = src;
});
}
\ No newline at end of file
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
height: 100px; height: 100px;
position: absolute; position: absolute;
} }
.svgaTest2 { .svgaTest2 {
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
width: 300px; width: 300px;
...@@ -23,8 +24,8 @@ ...@@ -23,8 +24,8 @@
} }
.button { .button {
width: 100px; margin: 5px;
height: 40px; padding: 10px;
border: 1px solid black; border: 1px solid black;
border-radius: 15px; border-radius: 15px;
display: flex; display: flex;
...@@ -32,14 +33,17 @@ ...@@ -32,14 +33,17 @@
justify-content: center; justify-content: center;
} }
.container{ .container {
position: absolute; position: absolute;
left: 0; left: 0;
top: 800px; top: 800px;
width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: start;
flex-wrap: wrap;
} }
.play { .play {
} }
\ No newline at end of file
...@@ -4,6 +4,8 @@ import styles from "./App.module.less"; ...@@ -4,6 +4,8 @@ import styles from "./App.module.less";
import svga from "./assets/svga/1红色标题.svga"; import svga from "./assets/svga/1红色标题.svga";
import svga2 from "./assets/svga/2输出加载页氛围.svga"; import svga2 from "./assets/svga/2输出加载页氛围.svga";
import icon from "./assets/react.svg";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
function App() { function App() {
...@@ -12,7 +14,7 @@ function App() { ...@@ -12,7 +14,7 @@ function App() {
const svgaRef = useRef<SvgaPlayerRef>(null); const svgaRef = useRef<SvgaPlayerRef>(null);
useEffect(() => { useEffect(() => {
console.log(svgaRef.current); console.log(svgaRef.current.getPlayer());
}); });
const clickPlay = () => { const clickPlay = () => {
...@@ -22,6 +24,14 @@ function App() { ...@@ -22,6 +24,14 @@ function App() {
svgaRef.current?.stop(); svgaRef.current?.stop();
} }
const clickReplaceImage = () => {
svgaRef.current?.addImage("img_3929", "5555", icon, 200, 200);
}
const clickRemoveImage = () => {
svgaRef.current?.removeImage("5555");
}
return <> return <>
<SvgaPlayer <SvgaPlayer
...@@ -37,11 +47,11 @@ function App() { ...@@ -37,11 +47,11 @@ function App() {
className={styles.svgaTest3} className={styles.svgaTest3}
src={svga2} src={svga2}
/> />
<div <div className={styles.container}>
className={styles.container}
>
<div className={styles.button} onClick={clickPlay}>play</div> <div className={styles.button} onClick={clickPlay}>play</div>
<div className={styles.button} onClick={clickStop}>stop</div> <div className={styles.button} onClick={clickStop}>stop</div>
<div className={styles.button} onClick={clickReplaceImage}>replaceImage</div>
<div className={styles.button} onClick={clickRemoveImage}>removeImage</div>
</div> </div>
</> </>
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
"jsx": "react-jsx", "jsx": "react-jsx",
/* Linting */ /* Linting */
"strict": true, "strict": false,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment