Preview Area Component
The preview area can be understood as the display and rendering module of elements in the canvas. The component in the preview area is essentially a Canvas element, which will be drawn into the core rendering canvas.
The preview area component is a React hooks component. The default Props parameters of the element are as follows:
/**
* Element component props parameters, in fact, we only need to care about the following parameters:
* element element data
* relativeTime relative time
* currentTime absolute time
* dirty element update flag
*/
export interface PixiElementProps {
currentTime: number; // Absolute time, the time of the element relative to the video, starting from 0 by default
relativeTime: number | null; // The time of the element relative to the element's delay time (startTime) = currentTime - startTime
visible: boolean; // Whether the element is displayed at the current time node
hide: boolean; // Hide the element, the priority is higher than visible - used in the editor
lock: boolean; // Lock the element, after locking the element will not be editable - used in the editor
scale: number; // Canvas scaling size
trackIndex: number; // Track number
element: BaseElement; // Element data
parent?: PIXI.Container; // Parent element's PIXI container node
dirty: string; // If the data changes, it means notifying the element to update
store: Store; // Collection of component global parameters and methods
env: Env; // Core usage environment
}
/**
* Core usage environment
* editor: Editing mode
* preview: Preview mode
* export: Export video mode
*/
export type Env = "editor" | "preview" | "export";It should be noted that the env parameter. Under normal circumstances, our rendering business in different environments is based on canvas, but some plugins may have different rendering modes in different environments, such as video. In editor and preview modes, you can directly draw <video /> tag elements in the canvas, but when exporting videos, you need to decode frame by frame.
Taking the QR code plugin as an example:
import { useMemo, useEffect, useState, useRef } from "react";
import * as PIXI from "pixi.js";
import QRCode from "qrcode";
import { plugin, options } from "@sdk/videoEditorSDK.react.es.min.js";
import { QrcodeElement } from "./types";
const { Animate, ControlElment, useSyncPixiElement } = plugin;
function QrcodeEl(props: PixiElementProps) {
const { visible, env, trackIndex, relativeTime } = props;
const element = props.element as QrcodeElement;
// const [, forceUpdate] = useReducer(x => x + 1, 0);
const store = props.store;
const animateRef = useRef();
const cav = useRef<HTMLCanvasElement>();
// Draw QR code
const drawQrcode = (ctx, elem) => {
// Handle asynchronous issues in components, when rendering, it will wait for renderAsyncMark[elementId] to become 'success' before continuing execution
store.renderAsyncMark[element.id] = "start";
// Generate QR code
QRCode.toDataURL(
elem.text,
{
errorCorrectionLevel: elem.correctLevel,
type: "image/png",
quality: 1,
width: elem.style.width,
height: elem.style.height,
color: {
dark: elem.colorDark,
light: elem.colorLight,
},
},
(err, base64 = "") => {
if (!base64) return;
// After the QR code is created successfully, use the image as a new texture
const img = new Image();
img.onload = () => {
const sprite = pixiElem.children.find(
(d) => d.name === "element"
) as PIXI.Sprite;
ctx.drawImage(img, 0, 0);
sprite.texture.update();
syncPixiStyle({ ...elem.style }, pixiElem);
store.renderAsyncMark[elem.id] = "success";
};
img.src = base64;
}
);
};
// Create pixi element
const pixiElem = useMemo<PIXI.Container>(() => {
const container = new PIXI.Container();
// By default, use a blank image as the texture to create an image element
cav.current = document.createElement("canvas");
cav.current.width = element.style.width;
cav.current.height = element.style.height;
// Draw QR code
drawQrcode(cav.current.getContext("2d"), element);
const texture = PIXI.Texture.from(cav.current);
const sprite = new PIXI.Sprite(texture);
// useSyncPixiElement will use the name
sprite.name = "element";
container.addChild(sprite);
return container;
}, [trackIndex]);
// Use a debounce function to update the view, because modifying the QR code content is a very performance-consuming operation
const updateDataDebounce = useCallback(
debounce((elem) => {
drawQrcode(cav.current.getContext("2d"), elem);
}, 300),
[]
);
// Set size
useEffect(
() => updateDataDebounce(element),
[
element.text,
element.style.width,
element.style.height,
element.colorDark,
element.colorLight,
element.correctLevel,
]
);
/**
* Bind data with pixi elements, this part of the code can be directly copied and used, and usually does not change
* syncPixiStyle is a method to synchronize element data and Pixi element styles, specific usage: syncPixiStyle({ ...element.style }, pixiElem);
*/
const [syncPixiStyle] = useSyncPixiElement(
pixiElem,
{
style: { ...element.style! },
parent: props.parent!,
animateRef,
visible,
hide: props.hide,
lock: props.lock,
relativeTime: props.relativeTime,
store: props.store,
},
["x", "y", "width", "height", "alpha", "rotation"],
element
);
return (
<>
{env === "editor" && (
<ControlElment
trackIndex={trackIndex}
hide={props.hide}
lock={props.lock}
scale={props.scale}
store={props.store}
element={element}
visible={visible}
/>
)}
<Animate
ref={animateRef}
store={props.store}
pixiElem={pixiElem}
elementData={element}
currentTime={props.currentTime}
relativeTime={props.relativeTime}
/>
</>
);
}
export default QrcodeEl;