Canvas 录制使用手册 (SDK 版本要求>= 3.3.0)¶
概述¶
Guance Browser RUM 现在支持在 Session Replay 中录制 canvas 画面。
先说明最重要的一点:
只配置 replayCanvasEnabled 还不够。
canvas 录制要真正生效,至少还需要同时满足下面这些前置条件:
- 已开启 Session Replay 采样
- 也就是
sessionReplaySampleRate > 0,或命中sessionReplayOnErrorSampleRate - 已启动 Session Replay 录制
- 也就是调用
startSessionReplayRecording() - 已开启 canvas 录制
- 也就是
replayCanvasEnabled: true - 页面里的目标元素确实是
2d canvas - 如果是
manual模式,业务代码还必须主动调用snapshotCanvas(canvas)
当前版本的能力边界是:
- 支持手动触发录制
- 支持自动录制
- 自动录制支持两条正式路径:
- snapshot sampling
- higher-fidelity auto recording
- 仅支持
2d canvas - 录制结果会进入 Session Replay 事件流
当前版本暂不覆盖的范围:
webgl/webgl2- 对所有复杂 2D 场景都做完全一致的还原
快速选择¶
最小可用配置¶
下面三套是当前最小可用配置。实际接入时,建议直接从这里复制,再按场景往上加。
1. 手动录制:最稳、最推荐¶
这套配置适合:
- 你知道什么时候画面已经稳定
- 你只需要关键节点补一帧
- 你希望成本尽量可控
datafluxRum.init({
applicationId: '<YOUR_APPLICATION_ID>',
datakitOrigin: '<YOUR_DATAKIT_ORIGIN>',
sessionReplaySampleRate: 100,
replayCanvasEnabled: true,
replayCanvasMode: 'manual',
replayCanvasQuality: 'medium'
})
datafluxRum.startSessionReplayRecording()
然后在业务代码里主动补帧:
这套里真正必须显式配置的是:
sessionReplaySampleRatereplayCanvasEnabled
推荐同时显式配置的是:
replayCanvasMode: 'manual'replayCanvasQuality: 'medium'
2. 自动 snapshot:更保守的自动录制¶
这套配置适合:
- 业务不方便手动卡补帧时机
- 你更关心稳定和成本
- 可以接受自动补帧不是逐帧录制
datafluxRum.init({
applicationId: '<YOUR_APPLICATION_ID>',
datakitOrigin: '<YOUR_DATAKIT_ORIGIN>',
sessionReplaySampleRate: 100,
replayCanvasEnabled: true,
replayCanvasMode: 'auto',
replayCanvasSampling: 2,
replayCanvasQuality: 'medium'
})
datafluxRum.startSessionReplayRecording()
这套里建议显式配置的是:
replayCanvasEnabled: truereplayCanvasMode: 'auto'replayCanvasSampling: 2replayCanvasQuality: 'medium'
如果你不显式传 replayCanvasSampling,SDK 也会用数值模式,但不建议依赖默认值做接入说明,最好写清楚。
3. 自动高还原度录制¶
这套配置适合:
- 你希望自动模式更接近真实绘制过程
- 页面主要是 2D canvas
- 你可以接受复杂场景下自动回退为 snapshot
datafluxRum.init({
applicationId: '<YOUR_APPLICATION_ID>',
datakitOrigin: '<YOUR_DATAKIT_ORIGIN>',
sessionReplaySampleRate: 100,
replayCanvasEnabled: true,
replayCanvasMode: 'auto',
replayCanvasSampling: 'all',
replayCanvasQuality: 'medium'
})
datafluxRum.startSessionReplayRecording()
这套里必须显式配置的是:
replayCanvasEnabled: truereplayCanvasMode: 'auto'replayCanvasSampling: 'all'
必须配置什么¶
如果你的目标只是“让 canvas 录制先跑起来”,最少要关心下面这些参数:
必须满足的前置条件¶
sessionReplaySampleRate- 必须保证 replay 会被采样到,否则 canvas 录制不会生效
startSessionReplayRecording()- 只
init()不够,必须真正启动 replay 录制 replayCanvasEnabled: true- 不开这个,canvas 录制完全关闭
手动模式必须配置¶
replayCanvasEnabled: true- 建议显式写
replayCanvasMode: 'manual' - 业务代码必须调用
snapshotCanvas(canvas)
自动模式必须配置¶
replayCanvasEnabled: truereplayCanvasMode: 'auto'- 建议显式写
replayCanvasSampling - 数值:自动 snapshot
'all':更高还原度的自动录制
什么时候要配 replayCanvasWorkerUrl¶
这个参数不是“功能开关”,而是部署参数。
只有在这些场景才需要配:
- 站点 CSP 不允许
worker-src blob: - 你要把 canvas snapshot 编码 worker 单独托管
- 你明确希望 canvas 编码不走 inline blob worker
典型写法:
datafluxRum.init({
// ...
replayCanvasEnabled: true,
replayCanvasMode: 'auto',
replayCanvasSampling: 2,
replayCanvasWorkerUrl: '/canvas-worker.js'
})
注意:
replayCanvasWorkerUrl只影响 canvas snapshot 编码- 它不替代原来的
workerUrl - 如果当前这一帧没有走 snapshot 编码,就不会用到 canvas worker
模式差异¶
manual¶
特点:
- 由业务主动决定补帧时机
- 不做重复帧去重
- 最适合“关键节点补一帧”
适合:
- 图表渲染完成
- 游戏结算页
- 白板保存
- 动画结束后取最终画面
auto¶
特点:
- SDK 自动补帧
- 具体补帧方式由
replayCanvasSampling决定 - 页面进入后台时会暂停录制,回到前台后恢复
- 可通过
shouldRecordCanvas()返回数字优先级,让关键 canvas 先录
replayCanvasSampling 的正式语义:
2:推荐默认值。自动 snapshot,稳定性和成本更平衡,建议从这里开始。1:更频繁的自动 snapshot,适合画面变化更密集的 2D canvas,但编码和上报成本更高。- 更大的数字:自动 snapshot 更稀疏,成本更低,但更容易丢掉中间变化。
'all':更高还原度的自动录制,尽量保留绘制过程;复杂场景下仍可能自动回退为 snapshot。
简单选择建议:
- 不确定怎么配:先用
replayCanvasSampling: 2 - 更看重成本:把数值调大
- 更看重还原度:先尝试
1,再评估是否真的需要'all' - 只有在你明确接受更高复杂度和更高数据成本时,才使用
'all'
适合:
- 业务不方便手工卡补帧时机
- 页面里有少量到中等规模的 2D canvas
- 希望在成本和还原度之间做平衡
- 多图表页面里只需要优先录关键主图
适用场景¶
适合这些场景:
- 业务自己知道什么时候画面稳定
- 需要在关键操作后手动截取一次 canvas 画面
- 需要让 Session Replay 能还原一部分 2D canvas 视觉结果
不适合这些场景:
- 高频动画逐帧录制
- 复杂 WebGL 场景
- 对每一帧都要求严格还原的视频式回放
开启方式¶
NPM¶
import { datafluxRum } from '@cloudcare/browser-rum'
datafluxRum.init({
applicationId: '<YOUR_APPLICATION_ID>',
datakitOrigin: '<YOUR_DATAKIT_ORIGIN>',
service: 'browser',
env: 'production',
version: '1.0.0',
sessionSampleRate: 100,
sessionReplaySampleRate: 100,
trackUserInteractions: true,
replayCanvasEnabled: true,
replayCanvasMode: 'manual',
replayCanvasQuality: 'medium'
})
datafluxRum.startSessionReplayRecording()
CDN¶
<script
src="https://static.guance.com/browser-sdk/v3/dataflux-rum.js"
type="text/javascript"
></script>
<script>
window.DATAFLUX_RUM &&
window.DATAFLUX_RUM.init({
applicationId: '<YOUR_APPLICATION_ID>',
datakitOrigin: '<YOUR_DATAKIT_ORIGIN>',
service: 'browser',
env: 'production',
version: '1.0.0',
sessionSampleRate: 100,
sessionReplaySampleRate: 100,
trackUserInteractions: true,
replayCanvasEnabled: true,
replayCanvasMode: 'manual',
replayCanvasQuality: 'medium'
})
window.DATAFLUX_RUM && window.DATAFLUX_RUM.startSessionReplayRecording()
</script>
配置项说明¶
日常接入时,优先只关心下面 5 个配置:
replayCanvasEnabledreplayCanvasModereplayCanvasSamplingreplayCanvasQualityshouldRecordCanvas
其余参数都属于高级覆盖项。只有在你已经确认默认策略不适合当前页面时,再继续往下调。
replayCanvasEnabled¶
- 类型:
boolean - 默认值:
false
是否开启 canvas 录制能力。
建议始终显式配置。默认关闭时,不会影响现有普通 Session Replay 逻辑。
replayCanvasMode¶
- 类型:
string - 当前支持:
'manual' | 'auto' - 默认值:
'auto'
指定 canvas 录制模式。
manual: 由业务代码在合适时机主动调用snapshotCanvas()auto: SDK 自动补帧,具体策略由replayCanvasSampling决定
replayCanvasSampling¶
- 类型:
number | 'all' - 默认值:数值模式
指定 auto 模式下的 canvas 录制策略。
- 数值:走 snapshot sampling
'all':走更高还原度的自动录制
建议:
- 如果目标是更稳、更保守:优先用数值模式
- 如果目标是更接近真实绘制过程:用
'all'
注意:
'all'并不意味着所有场景都会完全按高还原度路径录制- 对某些复杂场景,SDK 会自动回退到 snapshot
replayCanvasQuality¶
- 类型:
'low' | 'medium' | 'high' | number - 默认值:
0.4
canvas 快照编码质量。
推荐优先使用预设值:
low: 编码质量0.25medium: 编码质量0.4high: 编码质量0.5
这些预设主要影响 snapshot 编码质量。在 replayCanvasSampling: 'all' 下,command 帧本身不会走图片编码,但 fallback snapshot 仍会用到这里的质量配置。
如果传数字,仍然按高级覆盖项处理。
数值或预设越高:
- 图像质量越高
- 体积通常越大
- 对 replay segment 的压力也越大
建议先从 medium 开始。
如何录制¶
API¶
当前对外 API:
或 NPM:
返回值是一个 Promise,resolve 后得到:
或:
当前可能出现的 reason 包括:
not_recordingreplay_disabledinvalid_modenot_canvasnot_serializeddetachedrejected_by_should_record_canvasencode_too_largeunchangedencode_failedobserver_stopped
最小示例¶
const canvas = document.getElementById('my-canvas')
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#2563eb'
ctx.fillRect(20, 20, 160, 80)
ctx.fillStyle = '#0f172a'
ctx.font = '20px sans-serif'
ctx.fillText('Canvas Replay', 210, 70)
window.DATAFLUX_RUM &&
window.DATAFLUX_RUM.snapshotCanvas(canvas).then((result) => {
if (!result.ok) {
console.warn('snapshotCanvas failed:', result.reason)
}
})
推荐调用时机¶
建议在以下时机调用:
- 一次绘制完成之后
- 一组动画结束之后
- 用户完成关键交互后
不建议:
- 每一帧都调用
- 高频定时器中调用
- 页面空闲之外的大量批量调用
录制生效条件¶
要让 canvas snapshot 真正进入 replay,必须同时满足:
- 已调用
init() - 已调用
startSessionReplayRecording() replayCanvasEnabled = truereplayCanvasMode = 'manual'或'auto'- 传入的是
HTMLCanvasElement - 节点已经进入当前 DOM snapshot
- 节点仍然在文档中
- 编码结果未超出大小限制
如果其中任意条件不满足,本次 snapshot 会失败或被跳过。
shouldRecordCanvas 用法¶
shouldRecordCanvas(canvas) 现在既可以控制“录不录”,也可以控制 auto 模式里的优先级。
返回值规则:
- 返回
false:不录制这个 canvas - 返回数字:作为 auto 模式优先级,数字越大越优先
- 返回
true、undefined或其他非数字真值:按默认优先级0
示例:
datafluxRum.init({
replayCanvasEnabled: true,
replayCanvasMode: 'auto',
replayCanvasQuality: 'medium',
shouldRecordCanvas(canvas) {
if (canvas.dataset.replay === 'off') {
return false
}
if (canvas.dataset.chartRole === 'primary') {
return 10
}
if (canvas.dataset.chartRole === 'secondary') {
return 5
}
return 0
}
})
这对 dashboard 很有用:
- 主图先录
- 次级图在有预算时继续录
- 无关小图或缩略图可以直接排除
高级覆盖项¶
如果默认策略不够用,再考虑这些底层参数:
replayCanvasMimeType编码格式,默认image/webpreplayCanvasMaxCanvasSize编码前允许的最大边长,默认1280replayCanvasMaxEncodedBytes单帧允许进入 replay 的最大字节数,默认40000replayCanvasMaxConcurrentEncodes并发编码上限,默认1replayCanvasFlushImmediately成功进入 replay 后是否优先 flush,默认true
只有在以下场景才建议继续调这些项:
- 页面里 canvas 数量明显多于默认预算
- 单帧体积过大,必须压缩尺寸或字节数
- 你已经通过 debug 结果确认当前节奏过慢或过快
Demo 调试面板¶
本仓库的本地 demo 已带一个 canvas 调试面板,便于观察当前录制链路:
mode: 当前 demo 使用的 canvas 录制模式auto policy: 当前 demo 初始化时传入的interval / cooldown / unchanged backoff / failure backoff / max per runauto draw: demo 自己的持续重绘开关,只用于制造画面变化last trigger: 最近一次手动snapshotCanvas()的触发来源last snapshot: 最近一次手动snapshotCanvas()的结果last reason: 最近一次失败原因auto result: 按当前配置推导出的策略结果展示last event: 最近一次 demo 侧记录的事件状态
当前 demo 还提供两个辅助能力:
toggle auto draw: 周期性重绘 canvas,方便观察 auto 模式下是否持续产生变化export last canvas event: 导出最近一次成功手动 snapshot 对应的调试事件
需要注意:
auto draw是 demo 行为,不是 SDK 内部自动采样器auto result目前是基于手动snapshotCanvas()结果做的策略映射,主要用于调试说明- 它还没有直接接入 SDK 内部 auto 定时采样结果
- 导出的 event 也是 demo 侧按当前 canvas 内容重建的调试样例,不是从 intake payload 直接回读
性能建议¶
canvas 录制是高成本能力,建议保守使用。
推荐做法:
- 只在关键时刻调用
snapshotCanvas() - 控制 canvas 尺寸
- 优先使用
replayCanvasQuality: 'low' | 'medium' | 'high' - 保持
replayCanvasMaxConcurrentEncodes = 1 - auto 模式下优先让 quality 预设决定默认预算
- dashboard 页面优先用
shouldRecordCanvas()做关键图筛选和优先级排序
不推荐:
- 对高频动画逐帧截图
- 对超大画布频繁调用
- 将 canvas 录制当作默认路径
隐私说明¶
必须注意:
- canvas 像素内容不受普通 DOM masking 自动保护
也就是说:
- 文本节点、表单节点、属性脱敏规则,不会自动作用于 canvas 像素
- 如果 canvas 中绘制了敏感信息,录制后可能会在 replay 中还原出来
因此建议:
- 仅对可公开回放的 canvas 开启录制
- 不要对包含账号、手机号、支付信息等敏感内容的 canvas 调用
snapshotCanvas()
常见问题¶
1. 为什么调用了 snapshotCanvas() 但 replay 里没看到画面?¶
优先检查:
- 是否已开启
replayCanvasEnabled - 是否已启动 session replay recording
- 是否传入的是
canvas元素 - 是否在 canvas 绘制完成后再调用
- 是否因为体积过大被丢弃
2. 为什么自动模式不是逐帧录制?¶
当前版本已经支持自动录制,但它不是“逐帧视频”。
原因是:
- snapshot sampling 本质上仍然是采样
- 更高还原度的自动录制虽然更接近真实绘制,但仍然会受到复杂场景边界和自动回退约束
- canvas 编码和上传成本仍然明显高于普通 DOM replay
- WebGL 和高频动画场景仍然不适合走这条路径
所以当前自动模式的设计目标是:
- 用较低成本补关键视觉状态
- 在还原度和成本之间做平衡
- 不把 Session Replay 变成视频录制
3. 为什么不支持 WebGL?¶
当前版本只支持 2D canvas。
WebGL / WebGL2 的上下文状态更复杂,自动录制和回放都需要额外处理,后续版本再考虑。
4. canvas 录制是单独上传的吗?¶
不是。
当前版本中,canvas 快照会被编码为 replay 事件的一部分,并与普通 replay 共用同一个上传链路。
5. 为什么仪表板页面里有些图表录到了,有些没有录到?¶
这类页面通常会同时存在很多个 canvas 图表。出现“部分图表没有录到”时,最常见的原因不是单个图表报错,而是:
- 自动模式每一轮不一定会录所有图表
- 同屏图表太多,自动录制预算不够
- 某些图表初始绘制太早,后面又没有重绘
- 某些复杂图表在自动高还原度模式下会回退到 snapshot,成本更高
优先建议这样处理:
- 如果页面核心诉求是“图表尽量别漏”,优先使用:
- 对首屏关键图表,在图表渲染完成后主动补一帧:
-
对非关键图表继续交给自动模式处理
-
使用
shouldRecordCanvas()提高关键图表优先级,或跳过不重要的小图:
shouldRecordCanvas(canvas) {
if (canvas.dataset.miniChart === 'true') {
return false
}
if (canvas.id === 'main-trend' || canvas.id === 'conversion-funnel') {
return 10
}
return 1
}
如果你更看重稳定和成本,而不是覆盖所有图表:
- 保持
replayCanvasSampling: 2 - 只对关键图表手动
snapshotCanvas(canvas)
如果你更看重覆盖率:
- 尝试
replayCanvasSampling: 'all' - 再配合关键图表手动补帧
简单理解就是:
- 自动模式负责“尽量录”
- 手动
snapshotCanvas(canvas)负责“保证关键图一定录到”
示例场景¶
适合直接接入的业务场景:
- 图表绘制完成后录一次
- 关卡结束时录一次
- 白板保存前录一次
- 签字确认后录一次
一个更完整的示例:
function drawInvoicePreview(canvas, data) {
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = '#111827'
ctx.font = '18px sans-serif'
ctx.fillText('Invoice Preview', 24, 36)
ctx.fillText('Order: ' + data.orderNo, 24, 72)
ctx.fillText('Amount: ' + data.amount, 24, 108)
}
function refreshPreview(canvas, data) {
drawInvoicePreview(canvas, data)
window.DATAFLUX_RUM &&
window.DATAFLUX_RUM.snapshotCanvas(canvas)
}
dashboard 场景示例:
datafluxRum.init({
replayCanvasEnabled: true,
replayCanvasMode: 'auto',
replayCanvasSampling: 2,
replayCanvasQuality: 'medium',
shouldRecordCanvas(canvas) {
if (canvas.dataset.miniChart === 'true') {
return false
}
if (canvas.id === 'main-trend' || canvas.id === 'conversion-funnel') {
return 10
}
return 1
}
})
这个配置的语义是:
- 跳过小型辅助图
- 优先录主趋势图和核心漏斗图
- 其他普通图在同优先级下轮转取样
建议¶
当前版本推荐的使用方式只有一句话:
- 把 canvas 录制当成“关键视觉状态补帧”,不要当成“持续视频录制”
如果业务时机明确,优先 manual;如果是 dashboard 一类多图场景,再谨慎开启 auto,并明确选择数值 sampling 或 'all'。