UNPKG

press-plus

Version:
450 lines (393 loc) 13 kB
import { loaderUnity as loader } from 't-comm/es/loader'; import { createProxyPAGComposition } from './pag-composition'; import { createProxyPAGFile } from './pag-file'; import { createProxyPAGVIew } from './pag-view'; import type { TipPAGWebOptions, TipPAGWebCore, TipPAGWebLoadResult, TipPAGWebScaleInfo, TipPAGWebBaseLayerInfo, TipPAGWebLayerInfo, PAG, PAGTypes, PAGFile, PAGView, PAGComposition, PAGLayer, Vector, } from './types'; const SDK_URL = 'https://image-1251917893.file.myqcloud.com/commjs/libpag.min.js'; /** * @description 二次封装,方便业务调用 * @class PAGWeb */ class PAGWeb { private pag: PAG | null | undefined; private pagTypes: PAGTypes | null | undefined; private pagFile: PAGFile | null | undefined; private pagView: PAGView | null | undefined; private pagComposition: PAGComposition | null | undefined; constructor(options: TipPAGWebOptions) { this.pag = null; this.pagTypes = null; this.pagFile = null; this.pagView = null; this.pagComposition = null; if (options?.autoLoad) { this.load(); } } /** * @description 加载依赖库 * @returns Promise<TipPAGWebLoadResult> * @memberof PAGWeb */ load(): Promise<TipPAGWebLoadResult> { return new Promise((resolve, reject) => { if (this.pag && this.pagTypes) { resolve({ pag: this.pag, pagTypes: this.pagTypes }); return; } loader(SDK_URL) ?.then(async () => { console.log('[info]pag-web-api: initPAG'); const pag = await window?.libpag?.PAGInit(); const pagTypes = window?.libpag?.types; this.pag = pag; this.pagTypes = pagTypes; resolve({ pag, pagTypes }); }) .catch((err: Error) => reject(err)); }); } /** * @description 完成初始化并渲染 * @param {HTMLCanvasElement} $canvas 挂载的 <canvas /> * @param {File} file .pag格式的素材文件对象 * @param {object} [options={}] 选项,同 PAGWebOptions * @returns Promise<TipPAGWebCore|undefined> * @memberof PAGWeb */ async create( $canvas: HTMLCanvasElement, file: File, options: TipPAGWebOptions = {}, ): Promise<TipPAGWebCore|undefined> { if (this.pagFile) this.pagFile.destroy(); if (this.pagView) this.pagView.destroy(); if (this.pagComposition) this.pagComposition.destroy(); const pagCanvas = $canvas; const defaultOptions = { useScale: true, useCanvas2D: false, firstFrame: true, }; const initOptions = { ...defaultOptions, ...options, }; // 加载素材 this.pagFile = await this.pag?.PAGFile?.load(file); if (!this.pagFile) return; // 初始化舞台 this.pagView = (await this.pag?.PAGView?.init(this.pagFile, pagCanvas, initOptions)); if (!this.pagView) return; this.pagView.setRepeatCount(0); // 设置初始画面 // 挂载合成器 this.pagComposition = this.pagView.getComposition(); if (!this.pagComposition) return; // 二次封装 const pagTypes = this.getPAGTypes(); const pagView = this.getPAGView(); const pagFile = this.getPAGFile(); const pagComposition = this.getPAGComposition(); // 返回 return { pagTypes, pagView, pagFile, pagComposition, }; } /** * @description 封装 load 和 create,方便直接加载后渲染 * @param {HTMLCanvasElement} $canvas 挂载的 <canvas /> * @param {File} file .pag格式的素材文件对象 * @param {object} [options={}] 选项,同 * @returns Promise<TipPAGWebCore|undefined> * @memberof PAGWeb */ async init($canvas: HTMLCanvasElement, file: File, options: TipPAGWebOptions = {}): Promise<TipPAGWebCore|undefined> { await this.load(); const pag = await this.create($canvas, file, options); return pag; } /** * 清空舞台渲染的图层 * @description * https://bbs.pag.art/thread/883 * PAGView 的 destroy 只是清除实例,并没有清除 Canvas 的内容或者从 DOM 树上移除 Canvas, 因为 Canvas 是由业务上创建的,控制权在业务上。如果你需要在动画结束后将画面清空,你有几个方法: * 1. 当你不需要 CanvasElement 时,你可以将 CanvasElement 从 DOM 树上移除。 * 2. 当你还需要 CanvasElement 但希望它呈现透明状态时,你可以用 WebGL 命令手动将 Canvas 的内容清空; * 或者使用 pagView.pagSurface.clearAll() 将内容清空,不过需要注意 PAGView 实例上的 pagSurface 是一个私有对象。 * @memberof PAGWeb */ clearAll() { if (!this.pagView) { return; } // @ts-ignore this.pagView?.pagSurface?.clearAll(); } /** * 获取内置类型 * @returns PAGTypes|null * @memberof PAGWeb */ getPAGTypes() { if (!this.pagTypes) return null; return this.pagTypes; } /** * 获取二次封装的 pagFile 对象 * @returns PAGFile|null * @memberof PAGWeb */ getPAGFile() { if (!this.pagFile) return null; return createProxyPAGVIew(this.pagFile); } /** * 获取二次封装的 pagView 对象 * @returns PAGView|null * @memberof PAGWeb */ getPAGView() { if (!this.pagView) return null; return createProxyPAGFile(this.pagView); } /** * 获取二次封装的 pagComposition 对象 * @returns PAGComposition|null * @memberof PAGWeb */ getPAGComposition() { if (!this.pagComposition) return null; return createProxyPAGComposition(this.pagComposition); } /** * 获取缩放信息 * @returns {(TipPAGWebScaleInfo | undefined)} * @memberof PAGWeb */ getScaleInfo(): TipPAGWebScaleInfo | undefined { const pagView = this.getPAGView(); const pagTypes = this.getPAGTypes(); if (!pagView || !pagTypes) return; const { useScale } = pagView.pagViewOptions; const dpr = useScale ? window.devicePixelRatio : 1; // 有开启 useScale const m = pagView.matrix(); const { MatrixIndex } = pagTypes; const scaleX = m.get(MatrixIndex.a); const scaleY = m.get(MatrixIndex.d); const tx = m.get(MatrixIndex.tx); const ty = m.get(MatrixIndex.ty); const info = { dpr, scaleX, scaleY, tx, ty, }; console.log('[info]pag-web: getScaleInfo', info); return info; } /** * 获取自构造的图层信息,方便业务调用 * @param {PAGLayer} layer 图层对象 * @returns {TipPAGWebBaseLayerInfo} * @memberof PAGWeb */ getLayerInfo(layer: PAGLayer): TipPAGWebBaseLayerInfo { const { right, bottom } = layer.getBounds(); // 算得是内部四个点的位置 const width = right; const height = bottom; const uniqueID = layer.uniqueID(); const layerType = layer.layerType(); const layerName = layer.layerName(); const alpha = layer.alpha(); const visible = layer.visible(); const editableIndex = layer.editableIndex(); const duration = layer.duration(); const frameRate = layer.frameRate(); const localStartTime = layer.startTime(); const startTime = layer.localTimeToGlobal(localStartTime); return { uniqueID, layerType, layerName, width, height, alpha, visible, editableIndex, frameRate, startTime, duration, }; } /** * 获取可编辑图层集合 * @param {types.LayerType} layerTypes 过滤指定图层类型,若传入 LayerType.Image 代表只返回图像类型的图层 * @returns {TipPAGWebLayerInfo[]} 图层信息集合 * @memberof PAGWeb */ getEditableLayers(layerTypes: number[]): TipPAGWebLayerInfo[] { const editableLayers: TipPAGWebLayerInfo[] = []; const pagTypes = this.getPAGTypes(); const pagFile = this.getPAGFile(); if (!pagTypes || !pagFile) return []; // 为空时,默认取图片类型 let layerTypeList = layerTypes; if (!layerTypes || layerTypes.length === 0) { layerTypeList = [pagTypes.LayerType.Image]; } // 根据图层类型过滤获取 layerTypeList.forEach((layerType) => { const indices = pagFile.getEditableIndices(layerType); indices.forEach((index: number) => { const imageLayers: Vector<PAGLayer> = pagFile.getLayersByEditableIndex(index, layerType); for (let j = 0; j < imageLayers.size(); j++) { const layer: PAGLayer = imageLayers.get(j); const layerInfo = this.getLayerInfo(layer); editableLayers.push({ layer, ...layerInfo, }); } }); }); return editableLayers; } /** * 通过图层名字获取图层,并返回匹配集合 * @param {string} layerName 图层名称 * @returns {TipPAGWebLayerInfo[]} * @memberof PAGWeb */ getLayersByName(layerName: string): TipPAGWebLayerInfo[] { const pagComposition = this.getPAGComposition(); if (!pagComposition) return []; const pagLayerList = pagComposition.getLayersByName(layerName); const layerList = pagLayerList.map((layer: PAGLayer) => { console.log(`test getLayersByName: layerName: ${layer.layerName()}`); const layerInfo = this.getLayerInfo(layer); return { layer, ...layerInfo, }; }); return layerList; } /** * 获取事件响应时坐标位置所在的图层集合 * @param {Event} { event } 事件对象 * @returns {TipPAGWebLayerInfo[]} * @memberof PAGWeb */ getLayersUnderPoint({ event }: { event: MouseEvent }): TipPAGWebLayerInfo[] { const pagComposition = this.getPAGComposition(); if (!pagComposition) return []; const scaleInfo = this.getScaleInfo(); if (!scaleInfo) return []; const { dpr, scaleX, scaleY, tx, ty } = scaleInfo; const localX = (event.offsetX * dpr) / scaleX + (tx * -1) / scaleX; const localY = (event.offsetY * dpr) / scaleY + (ty * -1) / scaleY; const pagLayers = pagComposition.getLayersUnderPoint(localX, localY); // console.log('[info]点击位置', event.offsetX, event.offsetY); console.log('[info]计算后的画布点击位置', localX, localY); // console.log('[info]scaleInfo', scaleInfo); // 添加常用的图层信息 const paglayerList = pagLayers.map((layer: PAGLayer) => { const layerInfo = this.getLayerInfo(layer); return { layer, ...layerInfo, }; }); // 打印日志 paglayerList.forEach((layer: PAGLayer) => { console.log(` 舞台的点击响应位置${localX}, ${localY} 点击中了图层: ${layer.layerName} 图层的唯一ID: ${layer.uniqueID} 图层的可编辑ID(同类型公用一个ID): ${layer.editableIndex} `, layer); }); return paglayerList; } /** * 替换图像 * @param {number} layerIndex 图层的可编辑下标 * @param {File} file 待替换的图像文件对象 * @memberof PAGWeb */ async replaceImage(layerIndex: number, file: File) { const pagView = this.getPAGView(); const pagComposition = this.getPAGComposition(); if (!pagView || !pagComposition) return; const pagImage = await this.pag?.PAGImage?.fromFile(file); if (!pagImage) return; pagComposition.replaceImage(layerIndex, pagImage); await pagView.flush(); pagImage.destroy(); } /** * 替换文本 * @param {number} editableTextIndex 图层的可编辑下标 * @param {object} texture 文本属性对象,支持属性:https://pag.art/apis/web/classes/types.TextDocument.html * @memberof PAGWeb */ async replaceText(editableTextIndex: number, texture: Record<string, any>) { const pagFile = this.getPAGFile(); const pagView = this.getPAGView(); if (!pagView || !pagFile) return; const textDocument = pagFile.getTextData(editableTextIndex); console.log('[info]pag-web-api: 当前文本', textDocument); // 设置文本属性:https://pag.art/apis/web/classes/types.TextDocument.html Object.keys(texture).forEach((key) => { const value = texture[key]; textDocument[key] = value; }); // 更新文本 pagFile.replaceText(editableTextIndex, textDocument); await pagView.flush(); } /** * 对当前舞台进行截图 * @returns {dataUrl|undefined} base64格式的图像数据 * @memberof PAGWeb */ async makeSnapshot() { const pagView = this.getPAGView(); if (!pagView) return; const bitmap = await pagView.makeSnapshot(); if (bitmap) { const snapshotCanvas = document.createElement('canvas'); snapshotCanvas.width = bitmap.width; snapshotCanvas.height = bitmap.height; const ctx = snapshotCanvas.getContext('2d'); if (ctx) { ctx.drawImage(bitmap, 0, 0); const dataUrl = snapshotCanvas.toDataURL(); return dataUrl; } return; } return; } } export { PAGWeb, };