Javascript 使用 canvas 和 blob 时内存泄漏
Javascript memory leaks when using canvas and blobs
我正在编写一个 SVG 编辑器。我在页面上放置了一种 'Magic Eye',用户可以在其中看到整个 SVG 绘图和鼠标光标周围的缩放区域。当然问题是内存使用和快速渲染。因此,修改或缩放软件的步骤会创建 svg 绘制的缩小图像,并将其用于 Magic Eye 渲染。结果非常好,但是......我遇到了一个问题,我发现垃圾收集器不会释放创建的图像和 blob。所以过了几次,我的记忆里充满了图像。这是我为这个工作写的套路:
var RenderPosition = function(obj) {
try{
var clearCanvas = function(context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
var w = canvas.width;
canvas.width = 1;
canvas.width = w;
};
var PrepareBlob = function(blob){
glb._ThumbUrl = glb._DOMURL.createObjectURL(blob);
glb._MagicImg = new Image();
glb._MagicImg.src = glb._ThumbUrl;
};
var PosizViewFilling = function(e){
obj.pDC.drawImage(this,
obj.srt.x,
obj.srt.y,
obj.dms.width,
obj.dms.height);
obj.canvas.toBlob(PrepareBlob);
this.removeEventListener('load',PosizViewFilling,true);
this.src='';
delete this;
};
clearCanvas(obj.pDC,obj.canvas);
if (glb._MagicImg!==null) delete(glb._MagicImg);
glb._DOMURL.revokeObjectURL(glb._ThumbUrl);
var Big_img = new Image();
Big_img.addEventListener('load',PosizViewFilling, true);
Big_img.src = 'data:image/svg+xml;base64,'+btoa(obj.dw); //data from a svg draw
}
catch(err){
console.log(err.message);
}
};
如您所见,例程首先使用 SVG 绘制在 Big_image 中创建。在内存中创建调整大小的图像之后。我尝试了一种不同的方法,但也只有 Big_image 和 obj.dw 足以解决内存泄漏问题。怎么了?可能是我看不到我的错误。希望能从不同的角度得到建议
你需要有一个明确的
删除 glb._MagicImg;
当您不再需要此对象时。
另见:
Deleting Objects in JavaScript
了解更多信息。
为了充分利用 javascript,如果可以的话,重用资源总是个好主意。
你的代码太浪费了。
据我所知,您想要创建大型(复杂?)SVG 图像的较小版本。看起来您在创建新副本时转储了所有以前的副本。
一个不会影响记忆的可能的解决方案。
你需要两张图片。一个用于 SVG
,一个用于 magicEye
(拇指)。缩略图可以是 canvas,创建一次并在需要时将 SVG 绘制到它上面。 SVG 的另一个图像也只需要创建一次,只需添加加载侦听器一次设置它的 URL = "" 当你不再需要它时。保留它以备下次需要时使用。
以下代码将加载 3 个 SVG 图像之一并将其转换为图像 (canvas),然后在 100 毫秒内再次执行此操作。
它不会消耗比两个图像所需更多的资源(忽略未决的 GC 转储)。
var thumbImage = {
width:100,
height:100,
}
var SVG_images = [
'<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" id="testSVG" ><defs></defs><path d="M169.5,9Q184.7,10.5,186,23Q187.9,7.2,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q154.2,20.1,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q183.8,107.2,167.5,110Q147.1,112.3,142,101.5Q137.6,87,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q178.2,73.6,140,48.5Q138.7,29.4,144,20.5Q155.2,7.7,169.5,9Z M37.5,13Q54.2,14.8,75,15.5Q76.1,30.5,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q40.5,60.1,30,33Q6.7,36.9,4,27.5Q.8,8.8,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q138,38.5,104,31L104,49Q126.7,40.1,127,58.5Q97.6,72.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M121.5,130Q141,128.2,157,153.5Q178.3,206,162.5,232Q155.3,234.7,150,227.5Q147.7,219.5,149,192Q124.5,197.1,106.5,191Q102.8,198.8,96,225.5Q86.5,237.5,78,224.5Q90.4,147.8,121.5,130Z M124,149.5Q120.7,154.4,112,172.5Q120,176.9,143,174Q140.8,156.3,124,149.5Z" fill="#000000" fill-rule="evenodd" ></path></svg>',
'<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" id="testSVG" ><defs></defs><path d="M162.5,9Q171.4,8.5,179.5,12Q186.2,17.8,186,23Q186.8,14,191.5,13Q192,13.6,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q162.8,20.1,159,25.5Q155.2,30.4,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q185.4,104,172.5,109Q151.2,113.2,144,104.5Q135.6,89.6,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q173.8,80.1,169,70.5Q157.6,60.5,146.5,63Q141.6,57.7,140,48.5Q138.7,29.4,144,20.5Q153.2,11.1,162.5,9Z M37.5,13Q54.2,14.8,75,15.5Q74.6,21.7,71.5,25Q58.8,29.6,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q23,97.5,35,60.5Q36.2,59.8,30,33Q6.7,36.9,4,27.5Q2.9,17.9,9.5,15Q37.6,13.9,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q136.6,28.8,129.5,32Q129.5,32.2,104,31L104,49Q113.1,46.7,120.5,47Q127.7,50.6,127,58.5Q125,67.9,103.5,69Q101.4,70.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M78.5,117Q97.7,121.6,159.5,129Q164.2,130.1,174,144.5Q176.2,144.4,176,212.5Q170,236.9,163.5,244Q113.5,247.9,65,241.5Q58.3,227.7,76,214.5Q72.8,199.7,72,144Q57,146.3,59,131.5Q58.8,128.3,78.5,117Z M106,146.5Q95.5,150.6,98,170Q132.5,179.7,150,168.5L149.5,153Q128.8,145.8,106,146.5Z M98,193.5Q102.4,222.6,106.5,225Q122.3,231.2,133.5,233Q148.3,229.2,153,222.5Q157.6,207.3,152.5,196Q130.7,198.5,98,193.5Z" fill="#000000" fill-rule="evenodd" ></path></svg>',
'<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" id="testSVG" ><defs></defs><path d="M162.5,9Q183.7,7.2,186,23Q187.9,7.2,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q154.2,20.1,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q185.4,104,172.5,109Q151.2,113.2,144,104.5Q135.6,89.6,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q178.2,73.6,140,48.5Q135.3,15.1,162.5,9Z M37.5,13Q54.2,14.8,75,15.5Q76.1,30.5,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q40.5,60.1,30,33Q6.7,36.9,4,27.5Q.8,8.8,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q138,38.5,104,31L104,49Q126.7,40.1,127,58.5Q97.6,72.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M121.5,129Q151.9,129.3,159.5,135Q167.3,139.1,165,163.5Q164,172.2,143.5,177Q131.8,176.1,134.5,151Q124.6,153.1,107.5,155Q89.6,186.1,98,210.5Q113.7,214.2,136.5,210Q141,195.7,146.5,194Q168.3,192.4,168,210.5Q165.1,227.3,147.5,237Q125.6,242.4,103.5,242Q85.1,237.3,73,212.5Q67,196.5,70,173.5Q75,154.4,88.5,138Q98.1,130.5,121.5,129Z" fill="#000000" fill-rule="evenodd" ></path></svg>',
]
var createThumb = function(svgData) {
var loadImg = function(){
if(thumbImage.image === undefined){ // check if the image exists?
thumbImage.image = document.createElement("canvas"); // create it if not
}
// resize it. Could test if this is neede but keeping it simple.
thumbImage.width = thumbImage.width;
thumbImage.height = thumbImage.height;
// is there a 2d context
if(thumbImage.image.ctx === undefined){
// no context so create it.
thumbImage.image.ctx = thumbImage.image.getContext("2d");
}
// the resize may be the same and thus no free clear so clear
thumbImage.image.ctx.clearRect(0,0,thumbImage.width,thumbImage.height);
// draw the SVG image onto the magicImg.
thumbImage.image.ctx.drawImage(this, 0,0,thumbImage.width,thumbImage.height);
this.src = "";
};
// does the thumb image exist.
if( thumbImage.tempImage === undefined){
thumbImage.tempImage = new Image(); // create it
// add listener that can be reused for all other loads.
thumbImage.tempImage.addEventListener('load',loadImg);
}
thumbImage.tempImage.src = 'data:image/svg+xml;base64,'+btoa(svgData);
};
var currentSvg = 0;
function justDoIt(){
createThumb(SVG_images[currentSvg % SVG_images.length]);
currentSvg += 1;
setTimeout(justDoIt,100);
}
justDoIt();
它只在第一次需要时创建 canvas 和图像一次,然后在它们存在时重新使用它们。当第一个原始 SVG 被光栅化为 canvas 时,不需要额外的内存,因为已经分配的 canvas 有它的内存(除非拇指大小改变)。
我已经 运行 它一个小时了(每秒 10 张图像),并给了它全套开发工具检查。它分配的所有内容最终都会回到 GC 中,因此不会发生内存泄漏。
您应该能够根据自己的需要进行调整。记住重用而不是删除和重新分配。此外,Canvas 是一个图像,因此无需将任何内容转换为数据URL,除非您需要将其传输到 Javascript 直接上下文之外。
您可能还需要考虑让 SVG 自行缩放。
#main {
width: 400px;
height: 400px;
}
svg {
width: 100%;
height: 100%;
}
#thumb, #zoom {
width: 40px;
height: 40px;
border: solid 1px black;
overflow: hidden;
}
#zoom svg {
width: 400px;
height: 400px;
position: relative;
top: -140px;
left: -210px
}
<div id="main">
<svg id="mainsvg" viewBox="0 0 1000 1000">
<rect x="100" y="100" width="500" height="500" fill="green"
transform="rotate(10,350,350)"/>
<rect x="400" y="400" width="500" height="500" fill="orange"
transform="rotate(-10,650,650)"/>
</svg>
</div>
<div id="thumb">
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#mainsvg" />
</svg>
</div>
<div id="zoom">
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#mainsvg" />
</svg>
</div>
我正在编写一个 SVG 编辑器。我在页面上放置了一种 'Magic Eye',用户可以在其中看到整个 SVG 绘图和鼠标光标周围的缩放区域。当然问题是内存使用和快速渲染。因此,修改或缩放软件的步骤会创建 svg 绘制的缩小图像,并将其用于 Magic Eye 渲染。结果非常好,但是......我遇到了一个问题,我发现垃圾收集器不会释放创建的图像和 blob。所以过了几次,我的记忆里充满了图像。这是我为这个工作写的套路:
var RenderPosition = function(obj) {
try{
var clearCanvas = function(context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
var w = canvas.width;
canvas.width = 1;
canvas.width = w;
};
var PrepareBlob = function(blob){
glb._ThumbUrl = glb._DOMURL.createObjectURL(blob);
glb._MagicImg = new Image();
glb._MagicImg.src = glb._ThumbUrl;
};
var PosizViewFilling = function(e){
obj.pDC.drawImage(this,
obj.srt.x,
obj.srt.y,
obj.dms.width,
obj.dms.height);
obj.canvas.toBlob(PrepareBlob);
this.removeEventListener('load',PosizViewFilling,true);
this.src='';
delete this;
};
clearCanvas(obj.pDC,obj.canvas);
if (glb._MagicImg!==null) delete(glb._MagicImg);
glb._DOMURL.revokeObjectURL(glb._ThumbUrl);
var Big_img = new Image();
Big_img.addEventListener('load',PosizViewFilling, true);
Big_img.src = 'data:image/svg+xml;base64,'+btoa(obj.dw); //data from a svg draw
}
catch(err){
console.log(err.message);
}
};
如您所见,例程首先使用 SVG 绘制在 Big_image 中创建。在内存中创建调整大小的图像之后。我尝试了一种不同的方法,但也只有 Big_image 和 obj.dw 足以解决内存泄漏问题。怎么了?可能是我看不到我的错误。希望能从不同的角度得到建议
你需要有一个明确的 删除 glb._MagicImg; 当您不再需要此对象时。
另见: Deleting Objects in JavaScript 了解更多信息。
为了充分利用 javascript,如果可以的话,重用资源总是个好主意。
你的代码太浪费了。
据我所知,您想要创建大型(复杂?)SVG 图像的较小版本。看起来您在创建新副本时转储了所有以前的副本。
一个不会影响记忆的可能的解决方案。
你需要两张图片。一个用于 SVG
,一个用于 magicEye
(拇指)。缩略图可以是 canvas,创建一次并在需要时将 SVG 绘制到它上面。 SVG 的另一个图像也只需要创建一次,只需添加加载侦听器一次设置它的 URL = "" 当你不再需要它时。保留它以备下次需要时使用。
以下代码将加载 3 个 SVG 图像之一并将其转换为图像 (canvas),然后在 100 毫秒内再次执行此操作。
它不会消耗比两个图像所需更多的资源(忽略未决的 GC 转储)。
var thumbImage = {
width:100,
height:100,
}
var SVG_images = [
'<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" id="testSVG" ><defs></defs><path d="M169.5,9Q184.7,10.5,186,23Q187.9,7.2,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q154.2,20.1,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q183.8,107.2,167.5,110Q147.1,112.3,142,101.5Q137.6,87,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q178.2,73.6,140,48.5Q138.7,29.4,144,20.5Q155.2,7.7,169.5,9Z M37.5,13Q54.2,14.8,75,15.5Q76.1,30.5,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q40.5,60.1,30,33Q6.7,36.9,4,27.5Q.8,8.8,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q138,38.5,104,31L104,49Q126.7,40.1,127,58.5Q97.6,72.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M121.5,130Q141,128.2,157,153.5Q178.3,206,162.5,232Q155.3,234.7,150,227.5Q147.7,219.5,149,192Q124.5,197.1,106.5,191Q102.8,198.8,96,225.5Q86.5,237.5,78,224.5Q90.4,147.8,121.5,130Z M124,149.5Q120.7,154.4,112,172.5Q120,176.9,143,174Q140.8,156.3,124,149.5Z" fill="#000000" fill-rule="evenodd" ></path></svg>',
'<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" id="testSVG" ><defs></defs><path d="M162.5,9Q171.4,8.5,179.5,12Q186.2,17.8,186,23Q186.8,14,191.5,13Q192,13.6,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q162.8,20.1,159,25.5Q155.2,30.4,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q185.4,104,172.5,109Q151.2,113.2,144,104.5Q135.6,89.6,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q173.8,80.1,169,70.5Q157.6,60.5,146.5,63Q141.6,57.7,140,48.5Q138.7,29.4,144,20.5Q153.2,11.1,162.5,9Z M37.5,13Q54.2,14.8,75,15.5Q74.6,21.7,71.5,25Q58.8,29.6,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q23,97.5,35,60.5Q36.2,59.8,30,33Q6.7,36.9,4,27.5Q2.9,17.9,9.5,15Q37.6,13.9,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q136.6,28.8,129.5,32Q129.5,32.2,104,31L104,49Q113.1,46.7,120.5,47Q127.7,50.6,127,58.5Q125,67.9,103.5,69Q101.4,70.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M78.5,117Q97.7,121.6,159.5,129Q164.2,130.1,174,144.5Q176.2,144.4,176,212.5Q170,236.9,163.5,244Q113.5,247.9,65,241.5Q58.3,227.7,76,214.5Q72.8,199.7,72,144Q57,146.3,59,131.5Q58.8,128.3,78.5,117Z M106,146.5Q95.5,150.6,98,170Q132.5,179.7,150,168.5L149.5,153Q128.8,145.8,106,146.5Z M98,193.5Q102.4,222.6,106.5,225Q122.3,231.2,133.5,233Q148.3,229.2,153,222.5Q157.6,207.3,152.5,196Q130.7,198.5,98,193.5Z" fill="#000000" fill-rule="evenodd" ></path></svg>',
'<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256" id="testSVG" ><defs></defs><path d="M162.5,9Q183.7,7.2,186,23Q187.9,7.2,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q154.2,20.1,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q185.4,104,172.5,109Q151.2,113.2,144,104.5Q135.6,89.6,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q178.2,73.6,140,48.5Q135.3,15.1,162.5,9Z M37.5,13Q54.2,14.8,75,15.5Q76.1,30.5,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q40.5,60.1,30,33Q6.7,36.9,4,27.5Q.8,8.8,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q138,38.5,104,31L104,49Q126.7,40.1,127,58.5Q97.6,72.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M121.5,129Q151.9,129.3,159.5,135Q167.3,139.1,165,163.5Q164,172.2,143.5,177Q131.8,176.1,134.5,151Q124.6,153.1,107.5,155Q89.6,186.1,98,210.5Q113.7,214.2,136.5,210Q141,195.7,146.5,194Q168.3,192.4,168,210.5Q165.1,227.3,147.5,237Q125.6,242.4,103.5,242Q85.1,237.3,73,212.5Q67,196.5,70,173.5Q75,154.4,88.5,138Q98.1,130.5,121.5,129Z" fill="#000000" fill-rule="evenodd" ></path></svg>',
]
var createThumb = function(svgData) {
var loadImg = function(){
if(thumbImage.image === undefined){ // check if the image exists?
thumbImage.image = document.createElement("canvas"); // create it if not
}
// resize it. Could test if this is neede but keeping it simple.
thumbImage.width = thumbImage.width;
thumbImage.height = thumbImage.height;
// is there a 2d context
if(thumbImage.image.ctx === undefined){
// no context so create it.
thumbImage.image.ctx = thumbImage.image.getContext("2d");
}
// the resize may be the same and thus no free clear so clear
thumbImage.image.ctx.clearRect(0,0,thumbImage.width,thumbImage.height);
// draw the SVG image onto the magicImg.
thumbImage.image.ctx.drawImage(this, 0,0,thumbImage.width,thumbImage.height);
this.src = "";
};
// does the thumb image exist.
if( thumbImage.tempImage === undefined){
thumbImage.tempImage = new Image(); // create it
// add listener that can be reused for all other loads.
thumbImage.tempImage.addEventListener('load',loadImg);
}
thumbImage.tempImage.src = 'data:image/svg+xml;base64,'+btoa(svgData);
};
var currentSvg = 0;
function justDoIt(){
createThumb(SVG_images[currentSvg % SVG_images.length]);
currentSvg += 1;
setTimeout(justDoIt,100);
}
justDoIt();
它只在第一次需要时创建 canvas 和图像一次,然后在它们存在时重新使用它们。当第一个原始 SVG 被光栅化为 canvas 时,不需要额外的内存,因为已经分配的 canvas 有它的内存(除非拇指大小改变)。
我已经 运行 它一个小时了(每秒 10 张图像),并给了它全套开发工具检查。它分配的所有内容最终都会回到 GC 中,因此不会发生内存泄漏。
您应该能够根据自己的需要进行调整。记住重用而不是删除和重新分配。此外,Canvas 是一个图像,因此无需将任何内容转换为数据URL,除非您需要将其传输到 Javascript 直接上下文之外。
您可能还需要考虑让 SVG 自行缩放。
#main {
width: 400px;
height: 400px;
}
svg {
width: 100%;
height: 100%;
}
#thumb, #zoom {
width: 40px;
height: 40px;
border: solid 1px black;
overflow: hidden;
}
#zoom svg {
width: 400px;
height: 400px;
position: relative;
top: -140px;
left: -210px
}
<div id="main">
<svg id="mainsvg" viewBox="0 0 1000 1000">
<rect x="100" y="100" width="500" height="500" fill="green"
transform="rotate(10,350,350)"/>
<rect x="400" y="400" width="500" height="500" fill="orange"
transform="rotate(-10,650,650)"/>
</svg>
</div>
<div id="thumb">
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#mainsvg" />
</svg>
</div>
<div id="zoom">
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#mainsvg" />
</svg>
</div>