@antv/s2
Version:
effective spreadsheet render core lib
146 lines • 6.53 kB
JavaScript
;
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