使用mutationObserver检测DOM变化
正在加载今日诗词....2022-07-25
最近在写项目时,有一个需求是保存的同时截取当前页面内容并生成缩略图,下面是我的实现过程。
生成快照
将DOM转成图片的三种方式 dom-to-image、html2canvas、 html-to-image 对比之后选择html2canvas
使用
async function eleToImage(content) { const container = document.createElement('div'); container.style.position = 'absolute'; container.style.left = '-9999px'; container.innerHTML = typeof content === 'string' ? content : content?.outerHTML; document.body.appendChild(container); /* 删除transform布局,改为position */ var transformLay = container.querySelectorAll('.react-grid-item'); let list = Array.prototype.slice.call(transformLay || []) list.forEach((item) => { let positon = item.style.transform.slice(10, -1).split(','); item.style.left = positon[0] item.style.top = positon[1] item.style.transform = '' }) var textDom = container.querySelectorAll('.text'); let textList = Array.prototype.slice.call(textDom || []) textList.forEach((item) => { // 截图时文字会突出 item.style.fontSize = parseInt(item.style.fontSize) * 0.92 + 'px'; }) console.time('canvas') const blob = await new Promise<any>((resolve) => { html2canvas(container, { useCORS: true }).then((canvas) => canvas.toBlob(resolve, 'png', 0.1) ); }); document.body.removeChild(container); console.timeEnd('canvas') return blob; }
使用html2canvas时遇到的坑
- transform兼容性问题,html2canvas不支持transform,需要修改成position
- 字体限制宽度时,可能会遮挡,需要缩小一定比例
- 图片跨域问题,需要配置useCORS
通过html2canvas将DOM转成图片并保存,原则上已经解决了问题,但每次保存时不管页面有没有发生变化,都会生成快照,尝试通过MutationObserver解决一下。
优化
为了通用,写了一个自定义hooks,通过传入func函数来监听DOM变化时的回调
import { useEffect } from 'react';
/**
* MutationObserver主要用于监视对DOM树所做的修改
* https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
*/
const useMutationObserver = (dom, depends, func) => {
useEffect(() => {
let mutation: MutationObserver;
if(dom) {
// 当观察到变动时执行的回调函数
const callback = (mutations: MutationRecord[], observer: MutationObserver) => {
func(mutations, observer);
}
const config = {
attributes: true, // 观察属性变动
// attributeFilter: ['style', 'childList'],
attributeOldValue: true,
characterData: true, // 监听元素内的数据变动
characterDataOldValue: true,
childList: true, // 监听子元素数量变动(子元素的属性变化不会触发事件)(只针对一级子元素,孙子元素的数量变化不会触发事件)
subtree: true, // 将observer事件下发到目标元素的所有子元素,相当于对子元素也进行了observer
}
// 创建一个观察器实例并传入回调函数
mutation = new MutationObserver(callback);
// config 观察器的配置(需要观察什么变动)
// 在DOM更改时 开始接收通知
mutation.observe(dom, config);
}
// 组件销毁时 阻止MutationObaserver继续接收通知
return () => {
if(mutation) {
mutation.disconnect();
}
}
}, [depends])
}
export default useMutationObserver;
// 使用: 监测删除事件
// const isDelBtn = useRef<boolean>(false);
// const func = (mutations) => {
// // 如果删除最后一个空格 则将当前的优惠券删除
// if (
// mutations[0]?.removedNodes[0]?.className === "delete" &&
// !isDelBtn.current
// ) {
// const key = mutations[0]?.removedNodes[0].getAttribute("data-key");
// removeNode(key);
// }
// };
// useMutationObserver(refInput, func);
京ICP备2022027737号
Copyright © 2022 - present @wangxiang