一行代码解决svg保存成png文件
先说下我的功能需求,大家看看和大家的是否相同,能否借鉴。
下面是我的整个的思路过程,如果想直接找解决方案,直接看方案三。
功能需求:
添加一个按钮,点击按钮时,将拓扑图保存为图片文件并导出。
就是将在svg画布上画的图,保存成图片并导出。
问题描述:
在svg画布上画的拓扑图,每个节点使用的是图片,将整个svg导出时,导出的图片文件上面不显示节点图片。如图1-1、1-2:
图1-1 svg画布画好的拓扑图图1-2 导出的图片
我svg中绘制过程中,引入图片的代码如图1-3:
图1-3 svg节点引入图片
图片放在static下面,如图1-4:
图1-4 引入的图片存放位置方案思路:
方案一:无须引入插件
首先是制造一个button,用的element的框架。
<el-button size = 'mini' type="primary" @click="saveSvgToPng" v-loading.fullscreen.lock="fullscreenLoading">导出图片</el-button>
然后写这个点击事件响应函数:
saveSvgToPng: function () {
//采用直接转成canvas的方式
let width = 1000;
let height = 666;
var serializer = new XMLSerializer();
var source = '<?xml version="1.0" standalone="no"?>\r\n'+serializer.serializeToString(svg.node());
var image = new Image();
image.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d")
image.onload = function(){
context.drawImage(image, 0, 0);
var a = document.createElement("a");
a.download = "aaaa.png";
a.href = canvas.toDataURL('image/png');
a.click();
}
},
导出内容如图1-2,未显示网元节点图片。未搞清楚原因,查了一些资料,好像是因为canvas画布被污染了,所以无法使用toDataURL这种方法了。而canvas画布被污染的原因好像是因为svg中网元节点图片跨域了,我现在有这个疑问:在static下面的图片,存在跨域问题吗,因为我导出的时候,并没有报出跨域的错误。
然后我又尝试了方案二:
方案二:使用插件html2canvas
按照官网步骤,引入html2canvas插件,修改html结构,将svg放入一个div内,并给div一个ref标签,便于引用,如图1-5.
图1-5 html结构然后修改点击响应函数,代码如下:
函数1:
dataURLToBlob: function (dataurl) {
var arr = dataurl.split(',');
var mime = arr[0].match(/:(.*?);/)[1];
var bstr = atob(arr[1]);
var n = bstr.length;
var u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type: mime})
},
单击响应函数2:
catchSvgToPng: function () {
const _this = this
let a = document.createElement('a');
setTimeout(() => {
// 此处用于解决截图不清晰问题,将生成的canvas放大,然后再填充到原有的容器中就会清晰
const width = 1000;
const height = 400;
const canvas2 = document.createElement('canvas');
const scale = 2;
canvas2.width = width * scale;
canvas2.height = height * scale;
const context1 = canvas2.getContext('2d')
if(context1) {
context1.scale(scale, scale);
}
const opts = {
scale,
canvas: canvas2,
// logging: true, //日志开关,便于查看html2canvas的内部执行流程
width,
height,
// 【重要】开启跨域配置
useCORS: true
};
html2canvas(_this.$refs.imageWrapper,opts).then((canvas) => {
const context = canvas2.getContext('2d');
if(context) {
context.scale(2,2);
context.mozImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;
}
// canvas转换成url,然后利用a标签的download属性,直接下载,绕过上传服务器再下载
// _this.screenUrl = canvas.toDataURL()
// var imgBlob = canvas.toDataURL('image/jpeg', 1.0); //将图片转为base64
// imgBlob = imgBlob.toString().substring(imgBlob.indexOf(",") + 1);//截取base64以便上传
let blob = _this.dataURLToBlob(canvas.toDataURL('image/jpeg', 1.0));
a.setAttribute("href", URL.createObjectURL(blob));
a.setAttribute("download", "xxxxxx.png");
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(blob);
document.body.removeChild(a);
});
},1000)
},
导出的图片如图1-6所示:
图1-6 html2canvas导出的图片
导出的图片依然无网元节点图片,已经按照网上查到的资料,对污染的canvas进行处理,不让canvas被污染,依然没有效果。现在不清楚是什么原因导致的导出的图片无节点图片,因此也不知如何解决。
这个问题到这里已经困扰了我将近一周之久,查了好多资料,试了又试,像一支无头苍蝇一样撞来撞去,毫无头绪。因为之前科学上网的软件到期了,一直没有续费,所以之前的查资料都是在百度上进行。
在这里!!!强烈建议大家科学上网!!!当我把软件续费以后,用谷歌重新试着查询之前的资料,发现之前尝试的一种插件,但苦于没有找到例子而不知道怎么使用,就一直未动。当我谷歌出使用流程以后,我抱着试一试的心态重新试了一下,这就是我的方案三。
方案二:使用插件saveSvgAsPng
废话不多说,上个官网网址,写的很清楚。
github:https://github.com/exupero/saveSvgAsPng
翻译版本:https://www.helplib.com/GitHub/article_110596
按照这上面的提示,一步步操作。
1.用npm把插件装上
npm install save-svg-as-png
2.在需要使用的vue文件中把js文件import进来
import saveSvg from 'save-svg-as-png';
3.对之前的按钮点击响应函数进行编写
saveSvgToPng: function () {
//调研使用saveSvgAsPng 的方式
saveSvg.saveSvgAsPng(document.getElementById("topologySvg"), "diagram.png",{height:800});
},
官网上面给的例子直接使用的
saveSvgAsPng(document.getElementById("topologySvg"), "diagram.png")
但是这样的话,在我的vue框架下,是显示这个方法无效,所以需要使用前面import的文件名来进行调用。
我们调转这个函数,看下怎么回事。
out$.saveSvgAsPng = (el, name, options) => {
requireDomNode(el);
out$.svgAsPngUri(el, options || {}, uri => out$.download(name, uri));
};
这个是引入插件中该方法的位置,很显然该方法需要三个参数,操作的元素、保存的文件名称以及一些参数。至于具体可以设置哪些参数来控制导出的png图片,官网给了一个清单,写的很清楚。比如我们想设置导出图片的宽度和高度为我们想要的值,就可以这么写:
saveSvg.saveSvgAsPng(document.getElementById("topologySvg"), "diagram.png",{height:800,width:1400});
至此,该功能就实现了,一行代码可以搞定的功能琢磨了一周多,以后一定要科学上网!!!百度真得往后稍稍。