Core requirements
- Single continuous recording - Do not record in segments
- Record the timeline - Precise timestamp for each operation
- Real data - Use real or meaningful test data
- Appropriate pacing - Leave enough time for the visuals to be presented
Screen recording script structure
const { chromium } = require("playwright");
const path = require("path");
const fs = require("fs/promises");
const BASE_URL = "http://localhost:5173";
const OUTPUT_DIR = path.join(__dirname, "..", "public", "recordings");
const VIEWPORT = { width: 1920, height: 1080 };
// Test data - use real and meaningful data const TEST_DATA = { // Define test data };
// Timeline recorder - key component class TimelineRecorder { constructor() { this.startTime = Date.now(); this.events = []; }
mark(event) {
const elapsed = (Date.now() - this.startTime) / 1000;
this.events.push({ time: elapsed, event });
console.log( [${elapsed.toFixed(1)}s] ${event});
return elapsed;
}
getTimeline() { return this.events; } }
async function recordDemo() { await fs.mkdir(OUTPUT_DIR, { recursive: true });
const browser = await chromium.launch({ headless: false, slowMo: 50, // Slow down operations slightly for a more natural feel });
const context = await browser.newContext({ viewport: VIEWPORT, recordVideo: { dir: OUTPUT_DIR, size: VIEWPORT, }, locale: "zh-CN", });
const page = await context.newPage(); const timeline = new TimelineRecorder();
try { // Scenario recording...
} finally { // Save the timeline const timelineData = { totalDuration: (Date.now() - timeline.startTime) / 1000, events: timeline.getTimeline(), };
await fs.writeFile(
path.join(OUTPUT_DIR, "timeline.json"),
JSON.stringify(timelineData, null, 2)
);
await page.close();
await context.close();
await browser.close();
} }
## 录屏最佳实践
### 1. 等待时间设置
```javascript
// 页面加载后等待
await page.goto(url, { waitUntil: "networkidle" });
await page.waitForTimeout(1500);
// 点击后等待响应
await element.click();
await page.waitForTimeout(2000);
// 表单输入后等待
await input.fill(value);
await page.waitForTimeout(500);
// 弹窗打开后等待
await dialog.waitFor({ state: "visible" });
await page.waitForTimeout(1500);
2. Operation pacing
| Operation type | Recommended wait time |
|---|---|
| Page load | 1.5-3 seconds |
| Button click | 1-2 seconds |
| Form input | 0.5-1 second |
| Pop-up appears | 1.5-2 seconds |
| Data loading | 2-3 seconds |
| Scene transition | 2-3 seconds |
3. Hover display
// 悬停显示 tooltip
await element.hover();
timeline.mark("悬停显示提示");
await page.waitForTimeout(3000); // 留足时间让观众看清
4. Character-by-character input effect
// 更真实的输入效果
for (const char of text) {
await input.type(char, { delay: 80 });
}
Timeline recording specification
Events that must be recorded
- Page load complete
- Main area ready
- Every click operation
- Each data load complete
- Pop-up opened/closed
- Recording ended
Event naming specification
timeline.mark("页面加载完成");
timeline.mark("点击[按钮名]按钮");
timeline.mark("输入用户名: xxx");
timeline.mark("[弹窗名]弹窗打开");
timeline.mark("显示查询结果");
timeline.mark("录制结束");
Video format conversion
Convert to MP4 after recording:
// 使用 FFmpeg 转换
const { execSync } = require("child_process");
execSync(`ffmpeg -y -i ${webmPath} -c:v libx264 -preset fast -crf 23 ${mp4Path}`);
Troubleshooting
Chromium not installed
npx playwright install chromium
Recording is black
Ensure headless: false
Operations are too fast
Increase the slowMo value or waitForTimeout
Element not found
Use more stable selectors:
// 推荐
page.locator('button:has-text("查询")')
page.locator('.el-button--primary')
// 避免
page.locator('button:nth-child(2)')