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>