Skip to content

Commit

Permalink
feat: 时间操作序列
Browse files Browse the repository at this point in the history
  • Loading branch information
winily committed Nov 25, 2023
1 parent a916dce commit fa06521
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ tmp
*.webm
*.jpg
*.bmp
test.js
test.js
.DS_Store
yarn.lock
17 changes: 15 additions & 2 deletions api/ChunkVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export default class ChunkVideo extends VideoChunk {
pageViewport;
/** @type {Function} - 页面预处理函数 */
pagePrepareFn;
/** @type {{[key: number]: Function}} - 动作序列 */
timeActions;
/** @type {Function} - 页面获取函数 */
#pageAcquireFn = null;
/** @type {AsyncLock} - 异步锁 */
Expand Down Expand Up @@ -76,11 +78,12 @@ export default class ChunkVideo extends VideoChunk {
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
*/
constructor(options = {}) {
super(options);
assert(_.isObject(options), "options must be Object");
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn } = options;
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn, timeActions } = options;
assert(_.isUndefined(url) || util.isURL(url), `url ${url} is not valid URL`);
assert(_.isUndefined(content) || _.isString(content), "page content must be string");
assert(!_.isUndefined(url) || !_.isUndefined(content), "page url or content must be provide");
Expand All @@ -90,6 +93,12 @@ export default class ChunkVideo extends VideoChunk {
assert(_.isUndefined(pageWaitForOptions) || _.isObject(pageWaitForOptions), "pageWaitForOptions must be Object");
assert(_.isUndefined(pageViewport) || _.isObject(pageViewport), "pageViewport must be Object");
assert(_.isUndefined(pagePrepareFn) || _.isFunction(pagePrepareFn), "pagePrepareFn must be Function");
assert(_.isUndefined(timeActions) || _.isObject(timeActions), "timeActions must be Object");
timeActions && Object.keys(timeActions).forEach(key => {
key = Number(key)
assert(_.isFinite(key), `timeActions key ${key} must be Number`);
assert(_.isFunction(timeActions[key]), `timeActions[${key}] must be Function`);
})
this.url = url;
this.content = content;
this.startTime = startTime;
Expand All @@ -99,6 +108,7 @@ export default class ChunkVideo extends VideoChunk {
this.pageWaitForOptions = pageWaitForOptions;
this.pageViewport = pageViewport;
this.pagePrepareFn = pagePrepareFn;
this.timeActions = timeActions;
}

/**
Expand Down Expand Up @@ -172,7 +182,7 @@ export default class ChunkVideo extends VideoChunk {
...pageViewport
});
// 跳转到您希望渲染的页面,您可以考虑创建一个本地的Web服务器提供页面以提升加载速度和安全性
if(url)
if (url)
await page.goto(url, pageWaitForOptions);
// 或者设置页面内容
else
Expand All @@ -186,6 +196,9 @@ export default class ChunkVideo extends VideoChunk {
page.registerFonts(this.fonts);
// 等待字体加载完成
await page.waitForFontsLoaded();
// 注册事件序列
if (this.timeActions && Object.keys(this.timeActions).length > 0)
page.registerTimeActions(this.timeActions);
// 启动合成
super.start();
// 合成完成promise
Expand Down
2 changes: 2 additions & 0 deletions api/MultiVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default class MultiVideo extends ChunkSynthesizer {
fonts = [];
/** @type {Function} - 页面预处理函数 */
pagePrepareFn;
/** @type {{[key: number]: Function}} - 动作序列 */
timeActions;
/** @type {Function} - 页面获取函数 */
#pageAcquireFn = null;
/** @type {AsyncLock} - 异步锁 */
Expand Down
20 changes: 17 additions & 3 deletions api/SingleVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export default class SingleVideo extends Synthesizer {
pageViewport;
/** @type {Function} - 页面预处理函数 */
pagePrepareFn;
/** @type {{[key: number]: Function}} - 动作序列 */
timeActions;
/** @type {Function} - 页面获取函数 */
#pageAcquireFn = null;
/** @type {AsyncLock} - 异步锁 */
Expand Down Expand Up @@ -68,6 +70,7 @@ export default class SingleVideo extends Synthesizer {
* @param {number} [options.parallelWriteFrames=10] - 并行写入帧数
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
* @param {{[key: number]: Function}} [options.timeActions] - 动作序列
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {boolean} [options.showProgress=false] - 是否在命令行展示进度
* @param {boolean} [options.backgroundOpacity=1] - 背景不透明度(0-1),仅webm格式支持
Expand All @@ -77,7 +80,7 @@ export default class SingleVideo extends Synthesizer {
*/
constructor(options = {}) {
super(options);
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn } = options;
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn, timeActions } = options;
assert(_.isUndefined(url) || util.isURL(url), `url ${url} is not valid URL`);
assert(_.isUndefined(content) || _.isString(content), "page content must be string");
assert(!_.isUndefined(url) || !_.isUndefined(content), "page url or content must be provide");
Expand All @@ -87,6 +90,12 @@ export default class SingleVideo extends Synthesizer {
assert(_.isUndefined(pageWaitForOptions) || _.isObject(pageWaitForOptions), "pageWaitForOptions must be Object");
assert(_.isUndefined(pageViewport) || _.isObject(pageViewport), "pageViewport must be Object");
assert(_.isUndefined(pagePrepareFn) || _.isFunction(pagePrepareFn), "pagePrepareFn must be Function");
assert(_.isUndefined(timeActions) || _.isObject(timeActions), "timeActions must be Object");
timeActions && Object.keys(timeActions).forEach(key => {
key = Number(key)
assert(_.isFinite(key), `timeActions key ${key} must be Number`);
assert(_.isFunction(timeActions[key]), `timeActions[${key}] must be Function`);
})
this.url = url;
this.content = content;
this.startTime = startTime;
Expand All @@ -96,6 +105,7 @@ export default class SingleVideo extends Synthesizer {
this.pageViewport = pageViewport;
this.pageWaitForOptions = pageWaitForOptions;
this.pagePrepareFn = pagePrepareFn;
this.timeActions = timeActions;
}

/**
Expand Down Expand Up @@ -163,7 +173,7 @@ export default class SingleVideo extends Synthesizer {
height
});
// 跳转到您希望渲染的页面,您可以考虑创建一个本地的Web服务器提供页面以提升加载速度和安全性
if(url)
if (url)
await page.goto(url, pageWaitForOptions);
// 或者设置页面内容
else
Expand All @@ -173,10 +183,14 @@ export default class SingleVideo extends Synthesizer {
// 存在预处理函数时先执行预处理
this.pagePrepareFn && await this.pagePrepareFn(page);
// 注册字体
if(this.fonts.length > 0)
if (this.fonts.length > 0)
page.registerFonts(this.fonts);
// 等待字体加载完成
await page.waitForFontsLoaded();
// 注册事件序列
if (this.timeActions && Object.keys(this.timeActions).length > 0)
page.registerTimeActions(this.timeActions);

// 启动合成
super.start();
// 合成完成promise
Expand Down
2 changes: 2 additions & 0 deletions api/WebVideoCreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default class WebVideoCreator {
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
* @param {{[key: number]: Function}} [options.timeActions] - 动作序列
*/
createSingleVideo(options) {
assert(this.#configured, "WebVideoCreator has not been configured yet, please execute config() first");
Expand Down Expand Up @@ -160,6 +161,7 @@ export default class WebVideoCreator {
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
* @param {{[key: number]: Function}} [options.timeActions] - 动作序列
*/
createChunkVideo(options) {
assert(this.#configured, "WebVideoCreator has not been configured yet, please execute config() first");
Expand Down
2 changes: 2 additions & 0 deletions core/CaptureContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ export default class CaptureContext {

// CSS动画调度
await ____seekCSSAnimations(this.currentTime);
// 动作序列调度
await ____seekTimeActions(this.currentTime);

// 根据帧间隔推进当前时间
this.currentTime += this.frameInterval;
Expand Down
23 changes: 22 additions & 1 deletion core/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export default class Page extends EventEmitter {
rejectResources = [];
/** @type {Object[]} - CSS动画列表 */
cssAnimations = [];
/** @type {{[key: number]: Function}} - CSS动画列表 */
timeActions = {}
/** @type {Set} - 资源排重Set */
#resourceSet = new Set();
/** @type {CDPSession} - CDP会话 */
Expand Down Expand Up @@ -263,6 +265,14 @@ export default class Page extends EventEmitter {
fonts.forEach(font => this.registerFont(font));
}

/**
* 注册动作序列
* @param {Object} timeActions
*/
registerTimeActions(timeActions) {
this.timeActions = timeActions;
}

/**
* 等待字体加载完成
*
Expand Down Expand Up @@ -414,7 +424,7 @@ export default class Page extends EventEmitter {
* @param {Error} err - 错误对象
*/
#emitError(err) {
if(err.message.indexOf("Another frame is pending") != -1)
if (err.message.indexOf("Another frame is pending") != -1)
err = new Error("Page rendering has been interrupted");
if (this.eventNames().indexOf("error") != -1)
this.emit("error", err);
Expand Down Expand Up @@ -487,6 +497,8 @@ export default class Page extends EventEmitter {
await this.target.exposeFunction("____screencastCompleted", this.#emitScreencastCompleted.bind(this));
// 暴露CSS动画控制函数
await this.target.exposeFunction("____seekCSSAnimations", this.#seekCSSAnimations.bind(this));
// 暴露动作序列
await this.target.exposeFunction("____seekTimeActions", this.#seekTimeActions.bind(this));
// 暴露跳帧函数
await this.target.exposeFunction("____skipFrame", this.#skipFrame.bind(this));
// 暴露下一帧函数
Expand Down Expand Up @@ -543,6 +555,14 @@ export default class Page extends EventEmitter {
await Promise.all(seekPromises);
}

/**
* seek所有时间轴动作
*/
async #seekTimeActions(currentTime) {
const timeAction = this.timeActions[parseInt(currentTime)]
timeAction && await timeAction(this)
}

/**
* 跳过帧
*/
Expand Down Expand Up @@ -867,6 +887,7 @@ export default class Page extends EventEmitter {
this.acceptResources = [];
this.rejectResources = [];
this.cssAnimations = [];
this.timeActions = {};
}

/**
Expand Down

0 comments on commit fa06521

Please sign in to comment.