美文网首页
一行代码解决svg保存成png文件

一行代码解决svg保存成png文件

作者: 渴望认清自己 | 来源:发表于2019-03-11 21:40 被阅读0次

    先说下我的功能需求,大家看看和大家的是否相同,能否借鉴。
    下面是我的整个的思路过程,如果想直接找解决方案,直接看方案三。

    功能需求:

    添加一个按钮,点击按钮时,将拓扑图保存为图片文件并导出。

    就是将在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});
    

    至此,该功能就实现了,一行代码可以搞定的功能琢磨了一周多,以后一定要科学上网!!!百度真得往后稍稍。

    相关文章

      网友评论

          本文标题:一行代码解决svg保存成png文件

          本文链接:https://www.haomeiwen.com/subject/cndipqtx.html