svg 导出成图片会有很多限制, 外链的图片, 样式均会丢失,推荐使用 saveSvgAsPng.js 进行导出
以下是借鉴库写的导出函数:

function reEncode(data) { return decodeURIComponent( encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, (match, p1) => { const c = String.fromCharCode(`0x${p1}`); return c === '%' ? '%25' : c; }) ) } function export2Base64Img(svgDom, MIMEType, option) { var serializer = new XMLSerializer(); var source = serializer.serializeToString(svgDom); // 方式一: unescape(encodeURIComponent(txt)) // var path = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(source))); // 方式二: decodeURIComponent(encodeURIComponent(txt)) var path = "data:image/svg+xml;base64," + window.btoa(reEncode(source)); var canvas = document.createElement("canvas"), context = canvas.getContext("2d"), img = new Image(), pixelRatio = window.devicePixelRatio || 1, _exportPath, handler option = option || {}; canvas.width = parseFloat(svgDom.getAttribute('width')); // * pixelRatio canvas.height = parseFloat(svgDom.getAttribute('height')); // * pixelRatio img.src = path; img.onload = function () { // 增加底色 if (option.background) { context.beginPath(); context.rect(0, 0, canvas.width, canvas.height); context.fillStyle = option.background; context.fill(); context.closePath(); } // context.drawImage(img, 0, 0); var marker = option.watermark || ""; if (marker) { context.font = "18px 微软雅黑"; context.fillStyle = "rgba(12, 0, 70, 0.5)"; var textWidth = context.measureText(marker).width, textHegith = 50, pk = 1.2, rotate = (option.rotation || -45) * Math.PI / 180, sinReg = Math.sin(rotate), cosReg = Math.cos(rotate), width = Math.abs(canvas.width * cosReg) + Math.abs(canvas.height * sinReg), height = Math.abs(canvas.height * cosReg) + Math.abs(canvas.width * sinReg); var xf = Math.ceil(width / textWidth * pk); var yf = Math.ceil(height / textHegith); context.rotate(rotate); for (var i = 0; i < yf; i++) { for (var k = 0; k < xf; k++) { context.fillText(marker, textWidth * k * pk - canvas.height * cosReg, textHegith * i) } } } document.body.appendChild(canvas); _exportPath = canvas.toDataURL(MIMEType || 'image/png', 1) typeof handler === 'function' && handler(_exportPath) document.body.removeChild(canvas) } return new Promise(function (resolve, reject) { handler = resolve }) }
一个小例子;

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> * { /* margin: 0; */ padding: 0; } body { background: #f3f3f3; } svg .pinkCircle { fill: rgba(145, 92, 101, 0.671); stroke: #f60; stroke-width: 5; } svg .blackRect { fill: black; stroke: #ccc; stroke-width: 2; } </style> </head> <body> <p>Svg Start</p> <svg width="800" height="600" id="svg"> <!-- 圆形: cx, cy, r --> <circle cx="80" cy="40" r="15" class="pinkCircle"></circle> <circle cx="180" cy="40" r="15" class="pinkCircle"></circle> <circle cx="100" cy="100" r="25" class="pinkCircle"></circle> <!-- fill="pink" stroke="#f60" stroke-width="5" 也可以直接设置属性的方式 --> <!-- 矩形: x, y, rx, ry --> <rect x="80" y="50" class="blackRect" width="60" height="42" rx="8" ry="20"></rect> <!-- 通过 class 定义的样式只能定义填充和边框, 无法定义 svg内几何图形的大小或位置 --> <!-- rect 标签的 坐标位置用 x, y来表示, 而且额外可以定义 rx, ry; 表示圆角在 水平/垂直方向上的曲率半径 --> <!-- 椭圆 --> <ellipse cx="120" cy="120" rx="60" ry="20"></ellipse> <!-- cx: 椭圆中心点的水平位置 cy: 椭圆中心点的垂直位置 rx: 椭圆水平方向上的半径 ry: 椭圆垂直方向上的半径 ???? 椭圆什么时候会有中心点? 而且椭圆是有两个中心点 椭圆的绘制方法: https://jingyan.baidu.com/article/4ae03de30f5bfc3efe9e6b7f.html --> <!-- 多边形 --> <polygon points="300,300 350,350 420,300"></polygon> <!-- points 一系列的 x 和 y 坐标点, 描述了多边形各个角的位置 多边形是一个封闭的形状, 如果制定了几个点, 那么这几个点自动会与第一个点相连 --> <!-- 线 --> <line x1="0" y1="0" x2="700" y2="700" stroke="black" stoke-width="2"></line> <!-- x1: 起点的水平位置 y1: 起点的垂直位置 x2: 终点的水平位置 y2: 终点的垂直位置 --> <!-- 路径 --> <!-- d: 告知浏览器如何绘图 --> <!-- M: moveto 的缩写, 目标是一个假想的点 q 告诉光标要绘制一条贝塞尔曲线, 什么是贝塞尔曲线:? 可以从B站中去搜索查看, 一段几十分钟的讲解, 很复杂, 到处都在用的贝塞尔曲线 --> <path d="M 100 500 L 300 500 L 200 300 z" fill="orange" stroke="black" stroke-width="3" /> <!-- 文本 --> <text x="120" y="520" font-size="30" fill="blue" stroke="#ccc" stroke-width="2" font-family="Comic Sans MS" text-anchor="start" opacity="0.5">hello Svg <a xlink:href="http://www.baidu.com" target="_blank">这是一个链接</a> </text> <!-- svg 的基本文本属性与 HTML 同名, font-size, font-family, font-weight, font-style 除了颜色除外, 这些都是。 svg文本和图形一样, 颜色都叫"填充", 同样, 也可以在文本周围添加 "笔触" --> <!-- x 和 y 属性指定了文本锚点的位置, 默认情况下, 锚点标识了文本的开始并与文本基线垂直对齐 设置 text-anchor 可以改变锚点的位置, 值有 start, center, end svg 没有办法简单绘制一个文本块并让它们自动换行 --> <!-- svg 绘图中, 还可以使用 opacity 设置透明度等; --> </svg> <script> function reEncode(data) { return decodeURIComponent( encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, (match, p1) => { const c = String.fromCharCode(`0x${p1}`); return c === '%' ? '%25' : c; }) ) } function export2Base64Img(svgDom, MIMEType, option) { var serializer = new XMLSerializer(); var source = serializer.serializeToString(svgDom); // 方式一: unescape(encodeURIComponent(txt)) // var path = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(source))); // 方式二: decodeURIComponent(encodeURIComponent(txt)) var path = "data:image/svg+xml;base64," + window.btoa(reEncode(source)); var canvas = document.createElement("canvas"), context = canvas.getContext("2d"), img = new Image(), pixelRatio = window.devicePixelRatio || 1, _exportPath, handler option = option || {}; canvas.width = parseFloat(svgDom.getAttribute('width')); // * pixelRatio canvas.height = parseFloat(svgDom.getAttribute('height')); // * pixelRatio img.src = path; img.onload = function () { // 增加底色 if (option.background) { context.beginPath(); context.rect(0, 0, canvas.width, canvas.height); context.fillStyle = option.background; context.fill(); context.closePath(); } // context.drawImage(img, 0, 0); var marker = option.watermark || ""; if (marker) { context.font = "18px 微软雅黑"; context.fillStyle = "rgba(12, 0, 70, 0.5)"; var textWidth = context.measureText(marker).width, textHegith = 50, pk = 1.2, rotate = (option.rotation || -45) * Math.PI / 180, sinReg = Math.sin(rotate), cosReg = Math.cos(rotate), width = Math.abs(canvas.width * cosReg) + Math.abs(canvas.height * sinReg), height = Math.abs(canvas.height * cosReg) + Math.abs(canvas.width * sinReg); var xf = Math.ceil(width / textWidth * pk); var yf = Math.ceil(height / textHegith); context.rotate(rotate); for (var i = 0; i < yf; i++) { for (var k = 0; k < xf; k++) { context.fillText(marker, textWidth * k * pk - canvas.height * cosReg, textHegith * i) } } } document.body.appendChild(canvas); _exportPath = canvas.toDataURL(MIMEType || 'image/png', 1) typeof handler === 'function' && handler(_exportPath) document.body.removeChild(canvas) } return new Promise(function (resolve, reject) { handler = resolve }) } var svg = document.getElementById("svg"); svg.onclick = function () { var img = export2Base64Img(svg, null, { watermark: 'copyright reserved 2020 版权所有', background: '#fff' }); img.then(function (base64src) { // console.log('路径:::', base64src) downLoad(base64src, '图.png') }) } function downLoad(url, fileName) { var oA = document.createElement("a"); oA.download = fileName || ''; oA.style.display = 'none' oA.href = url; document.body.appendChild(oA); oA.click(); oA.remove(); } </script> </body> </html>