Skip to content

预览区组件

预览区可以理解为画布中元素的显示渲染模块,预览区的组件本质就是一个 Canvas 元素,该 canvas 元素会绘制到内核渲染画布中。

预览区组件是一个 React hooks 组件,元素默认的 Props 参数如下:

ts
/**
 * 元素组件props参数,其实我们只需要关心以下参数:
 * element 元素数据
 * relativeTime 相对时间
 * currentTime 绝对时间
 * dirty 元素更新标识
 */
export interface PixiElementProps {
  currentTime: number; // 绝对时间,元素相对视频的时间,默认从0开始
  relativeTime: number | null; // 元素相对元素的延迟时间(startTime)的时间 = currentTime - startTime
  visible: boolean; // 元素在当前时间节点是否显示
  hide: boolean; // 隐藏元素,优先级会比 visible更高 - 编辑器中使用
  lock: boolean; // 锁定元素,锁定元素之后元素将无法被编辑 - 编辑器中使用
  scale: number; // 画布的缩放大小
  trackIndex: number; // 轨道序号
  element: BaseElement; // 元素数据
  parent?: PIXI.Container; // 父元素的PIXI容器节点
  dirty: string; // 如果数据发生变化,表示通知元素进行更新
  store: Store; // 组件全局参数和方法的集合
  env: Env; // 内核的使用环境
}

/**
 * 内核的使用环境
 * editor: 编辑模式
 * preview: 预览模式
 * export: 导出视频模式
 */
export type Env = "editor" | "preview" | "export";

需要注意的是env这个参数,正常情况下,在不同的环境中,我们的渲染业务都是基于 canvas 做的渲染,但是有些插件可能在不同环境中的渲染模式是不一样的,比如视频,在编辑器和预览模式中可以直接在画布中绘制<video />标签元素,但是在导出视频的时候,需要逐帧解码。

以二维码插件为例:

tsx
import { useMemo, useEffect, useState, useRef } from "react";
import * as PIXI from "pixi.js";
import QRCode from "qrcode";
import { plugin, options } from "@h5/videoPluginSDK";
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>();

  // 绘制二维码
  const drawQrcode = (ctx, elem) => {
    // 处理组件中异步问题,当渲染的时候会等renderAsyncMark[elementId] 变为 'success'才会继续执行
    store.renderAsyncMark[element.id] = "start";
    // 生成二维码
    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;
        // 二维码创建成功后,使用图片当新的纹理
        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;
      }
    );
  };

  // 创建pixi元素
  const pixiElem = useMemo<PIXI.Container>(() => {
    const container = new PIXI.Container();

    // 默认使用一个空白的图片当纹理创建一个图片元素
    cav.current = document.createElement("canvas");
    cav.current.width = element.style.width;
    cav.current.height = element.style.height;

    // 绘制二维码
    drawQrcode(cav.current.getContext("2d"), element);
    const texture = PIXI.Texture.from(cav.current);
    const sprite = new PIXI.Sprite(texture);
    
    // useSyncPixiElement 会使用到 name
    sprite.name = "element";
    container.addChild(sprite);
    return container;
  }, [trackIndex]);

  // 使用防抖函数更新视图,因为修改二维码内容是一个非常消耗性能的操作
  const updateDataDebounce = useCallback(
    debounce((elem) => {
      drawQrcode(cav.current.getContext("2d"), elem);
    }, 300),
    []
  );

  // 设置大小
  useEffect(
    () => updateDataDebounce(element),
    [
      element.text,
      element.style.width,
      element.style.height,
      element.colorDark,
      element.colorLight,
      element.correctLevel,
    ]
  );

  /**
   * 数据和pixi元素进行绑定,这部分代码可以直接复制使用,通常情况下不会变化
   * syncPixiStyle是一个同步元素数据和Pixi元素样式的方法,具体使用方法: 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;

Powered by 四川爱趣五科技有限公司.蜀ICP备18034069号.