UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

146 lines 6.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VideoRenderer = void 0; const tslib_1 = require("tslib"); const g_1 = require("@antv/g"); const lodash_1 = require("lodash"); const renderer_1 = require("../common/constant/renderer"); const icons_1 = require("../common/icons"); const interface_1 = require("../common/interface"); const customRenderer_1 = require("../utils/cell/customRenderer"); const BaseRenderer_1 = require("./BaseRenderer"); // 部分浏览器 autoplay=false 时不解码首帧,seek 到此时间点强制解码以展示预览画面 const VIDEO_PREVIEW_FRAME_TIME = 0.001; const defaultVideoConfig = { loop: true, autoplay: false, preload: 'auto', crossOrigin: true, controls: false, muted: true, }; class VideoRenderer extends BaseRenderer_1.BaseRenderer { constructor() { super(...arguments); this.fallback = ''; } prepare(renderer, cell) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const text = yield this.prepareText(renderer, cell); return new Promise((resolve) => { const { height, width } = this.getCellInfo(cell); const { timeout = 10000, fallback = '' } = renderer; this.fallback = fallback; const cacheKey = BaseRenderer_1.BaseRenderer.getCacheKey('video', text); if (BaseRenderer_1.BaseRenderer.mediaCache.has(cacheKey)) { const cached = BaseRenderer_1.BaseRenderer.mediaCache.get(cacheKey); if (cached instanceof HTMLVideoElement || cached instanceof HTMLImageElement) { resolve(cached); return; } if (cached === null) { resolve(fallback); return; } } const video = document.createElement('video'); const handleFallback = () => tslib_1.__awaiter(this, void 0, void 0, function* () { if (fallback) { const img = yield (0, customRenderer_1.asyncDrawImage)({ src: fallback, fallback: '', timeout: 5000, mediaCache: BaseRenderer_1.BaseRenderer.mediaCache, }).catch(() => null); if (img) { BaseRenderer_1.BaseRenderer.mediaCache.set(cacheKey, img); resolve(img); return; } } BaseRenderer_1.BaseRenderer.mediaCache.set(cacheKey, null); resolve(fallback); }); const fallbackTimer = setTimeout(handleFallback, timeout); const config = Object.assign(Object.assign({ height, width, src: text }, defaultVideoConfig), renderer.videoConfig); Object.assign(video, config); video.onloadeddata = () => { clearTimeout(fallbackTimer); // 只在未显式配置 autoplay: true 时才 pause/seek,避免覆盖用户配置 if (!config.autoplay) { video.pause(); video.currentTime = VIDEO_PREVIEW_FRAME_TIME; } BaseRenderer_1.BaseRenderer.mediaCache.set(cacheKey, video); resolve(video); }; const onError = () => { clearTimeout(fallbackTimer); handleFallback(); }; // 错误处理 ['error', 'abort', 'stalled'].forEach((eventName) => { video.addEventListener(eventName, onError); }); }); }); } generateConfig(renderer, cell, element) { const { y, height } = cell.getBBoxByType(interface_1.CellClipBox.CONTENT_BOX); const availableWidth = Math.max(cell.getMaxTextWidth(), 0); let videoWidth = availableWidth; let videoHeight = height; let fill = 'transparent'; const getMediaSize = (el) => el instanceof HTMLVideoElement ? { width: el.videoWidth, height: el.videoHeight } : { width: el.naturalWidth, height: el.naturalHeight }; if (element instanceof HTMLVideoElement || element instanceof HTMLImageElement) { const { width: srcWidth, height: srcHeight } = getMediaSize(element); const { width: finalWidth, height: finalHeight } = (0, customRenderer_1.calculateImageSize)(availableWidth, height, srcWidth, srcHeight); videoWidth = finalWidth; videoHeight = finalHeight; fill = { image: element, repetition: 'no-repeat', transform: `scale(${finalWidth / srcWidth}, ${finalHeight / srcHeight})`, }; } else { // 加载失败:展示空白 fill = 'transparent'; } const { x: videoX } = cell.getContentPosition({ contentWidth: videoWidth, }); const videoY = y + (height - videoHeight) / 2; // https://g.antv.antgroup.com/api/css/pattern return { style: Object.assign({ x: videoX, y: videoY, width: videoWidth, height: videoHeight, fill }, renderer.config), isFallback: element instanceof HTMLImageElement, }; } render(cell, config) { if (config.isFallback) { cell.appendChild(new g_1.Image((0, lodash_1.merge)({}, config, { style: { src: this.fallback } }))); return; } const rect = new g_1.Rect(Object.assign(Object.assign({}, config), { name: renderer_1.VIDEO_RECT_NAME })); const { x, y, width, height } = config.style; const calcSize = Math.min(width, height) * 0.25; rect.appendChild(new icons_1.GuiIcon({ name: 'Play', width: calcSize, height: calcSize, x: x + width / 2 - calcSize / 2, y: y + height / 2 - calcSize / 2, pointerEvents: 'none', cursor: 'pointer', })); cell.appendChild(rect); } } exports.VideoRenderer = VideoRenderer; //# sourceMappingURL=VideoRenderer.js.map