Dom节点变动检测并录制的简单实现

2,781 阅读5分钟

关键技术

MutationObserver

MutationObserver使用来监测某个范围内DOM的变动,如节点的增减、属性的变动,文本节点的变化等。相当于DOM的改变就会触发MutationObserver这个事件,但是这个事件是异步触发的,会把DOM的变动封装成一个数组进行统一更新的,这点和react中的setState非常相像。

构造函数

var observer = new MutationObserver(function (mutations, observer) {
  mutations.forEach(function(mutation) {
    console.log(mutation);
  });
})

在调用时,观察者对象会传给该函数两个参数:

  • MutationObserver接受一个callback参数,用来处理节点变化的回调函数,返回两个参数,mutations和observer。

  • mutations:节点变化记录列表

  • observer:MutationObserver的实例对象。

方法

MutationObserver对象有三个方法,分别如下:

  • observe:设置观察目标,接受两个参数,target:观察目标,options:通过对象成员来设置观察选项

  • disconnect:阻止观察者观察任何改变

  • takeRecords:清空记录队列并返回里面的内容

observe方法中常用的options参数有已下几个选项:

  • childList:设置true,表示观察目标子节点的变化,比如添加或者删除目标子节点,不包括修改子节点以及子节点后代的变化

  • attributes:设置true,表示观察目标属性的改变

  • characterData:设置true,表示观察目标数据的改变

  • subtree:设置为true,目标以及目标的后代改变都会观察

使用示例

现在有一个id为target的节点

<div id='target' class='block'>

</div>

对该dom进行监听并测试

var target=document.getElementById('target');
var i=0
var observe=new MutationObserver(function (mutations,observe) {
    i++  
});
observe.observe(target,{ childList: true});
target.appendChild(docuemnt. createElement ('span')); 
target.appendChild(docuemnt. createElement ('div'));
console.log(i)  //1

录制回放操作的简单实现

初始思路:使用MutationObserver监听整个页面,每当有页面变动,则将页面的html转换成图片进行队列存储,回放用户操作即不停从队列中取出元素展示

Html转Canvas

这里我们直接使用html2canvas这个第三方库官方地址,基于html2canvas.js可将一个元素渲染为canvas,只需要简单的调用html2canvas(element[, options]),下列html2canvas方法会返回一个包含有canvas元素的promise:

html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});

Canvas转Img

上一步生成的canvas即为包含目标元素的canvas元素对象。实现保存图片的目标只需要将canvas转image即可。通过canvas的toDataURL方法将canvas输出为data: URI类型的图片base64地址,再将该图片地址赋值给元素的src属性即可。

示例

<canvas id="canvas" width="5" height="5">
</canvas>

获取该图片,直接可以用toDataURL转成图片,默认为 PNG 格式

var canvas = document.getElementById("canvas");
var dataURL = canvas.toDataURL();
console.log(dataURL);
// "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby...

最后,通过定时器将img不停的插入到img标签的src中,实现回放的效果。

Canvas在高分屏中一些缺陷的解决方法

Canvas在高分屏中绘制图片会有模糊的效果,其实不只是绘制图片时会出现模糊的问题,高清屏的设备中任何绘制在 canvas 中的图形(包括文字)都会出现模糊的问题。

这是因为HIDPI的屏幕上的像素实际上是逻辑像素,我们如果当成正常的像素(css中设置的像素)使用它,如我们在css中设置100px时,在例如iphone4S(devicePixelRatio为2)上,实际渲染的是200px的物理像素。所以当我们向这种高分辨率的屏幕添加img的时候,我们的图像及其他一些图形文字都会受到devicePixelRatio的影响会变得模糊。
解决方法很简单,就是将 canvas 的高和宽分别乘以 devicePixelRatio将其放大,然后又用 CSS 将高和宽限制成初始的大小。

Demo效果

这里为了简化操作,做捉了两个按钮做添加和删除元素,实际情况中,不仅需要对dom节点的操作进行监听,例如用户的点击、停留、轨迹等行为数据都需要进行采集分析。

方案优化

在Web中, 图形图像的操作以及存储会消耗大量的性能, 通过使用html快照来替代图像快照可以大幅的提高性能以达到生成可用状态,但是需要做很多的处理,当我们存储的是html的快照链时,想要在客户端进行用户操作回放,我们需要构建沙盒环境对html重新进行渲染,有一些工具如parse5可以进行html的序列化和解析操作,“视频回放”其实就是 HTML DOM 的变化增量及快照。
在html的解析过程中需要要对dom快照进行一点的处理,如:

  • 禁止表单提交

  • 禁止跳转、window.open等操作

  • js脚本的执行(因为我们只是需要dom结构的变化)等

  • 浏览器端存一定量的增量快照后再一起发送到服务端,减少网络开销;也包括多次增量之后再进行一次全量,对齐真实状态。

  • 数据脱敏

  • 通过 DocumentFragment 提高平台回放效率。

可以参考开源方案RRWeb:github.com/rrweb-io/rr…

基于上述技术还可以实现哪些小功能(思路)

网页中长按保存截图(针对移动端)

捕捉长按的操作,然后通过我们上面的,实现转canvas转img的操作

网页中实现撤销操作

记录用户的每次的dom操作放到一个栈中,每次用户触发撤销操的时候从栈中取出一个元素,可以还原上次的dom结构,从而实现了撤销的操作

有关动态显示的部分

例如根据文本内容是否超出来动态控制是否显示Tooltip,文本末尾显示其他内容等