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
})
}
View Code
一个小例子;
<!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>
View Code