技术博客-5 jsPlumb缩略图的生成

在查找缩略图生成的相关资料时,发现了两个比较有用的插件:html2canvas和canvas2image。顾名思义,前者是将html对象转换为canvas画布,而后者则是将canvas导出为不同格式的image图像。

由此,就得到了实现缩略图生成的方法:先使用html2canvas将选定的div转化为canvas,再使用canvas2image将canvas转化为图片形式保存到后端数据库中。保存成了图片的形式以后,前端的排版就好处理了。不过实际上canvas元素自带了一个toDataURL()方法,可以直接将canvas转换为可以直接保存到数据库中的base64图像,不需要第二个插件就能完成所需功能。

然而,理想很丰满,现实很骨感。在实现中遇到了两个难以解决的bug:

缩略图生成不全

原因:选定div的height过大,html2canvas默认截图当前显示的内容,屏幕之外未被显示的内容则不会被截进canvas中,自然也就不会生成到缩略图中。

解决方法:参考博客1博客2,先将滚动条移动到顶,然后克隆一份备份的节点,对克隆的节点进行截图。代码如下:

window.pageYoffset = 0;
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
//美观考虑,去除jsplumb元素选中效果
$("#" + window.sessionStorage.getItem("active_node")).removeClass("selected");
//暂时去除画布边框
$("#canvas").find("#border").attr("style","height:1000px;border:0px");
//获取节点高度,后面为克隆节点设置高度。
        
var height = $("#canvas").height()
//克隆节点,默认为false,不复制方法属性,为true是全部复制。
var cloneDom = $("#canvas").clone(true);
//设置克隆节点的css属性,因为之前的层级为0,我们只需要比被克隆的节点层级低即可。
cloneDom.css({
    "background-color": "white",
    "position": "absolute",
    "top": "0px",
    "z-index": "-1",
    "height": height
});
$("body").append(cloneDom);
cloneDom.attr("id", "canvas1");

这样做有一个小小的副作用:运行时会在页面左上角“闪现”一下备份的画布节点(这个实现方法不允许隐藏这个备用节点),同时边框也会闪一下。

效果图:

之后就要解决jsplumb箭头不显示的问题了。

箭头不显示

箭头不显示的原因,是因为jsPlumb把其他元素都转化成了div,唯独把箭头转化成了不能用html2canvas处理的svg矢量图。为此,需要专门对svg进行处理。这里用到了另一个插件canvg,具体代码如下:

if (typeof html2canvas !== 'undefined') {
    //以下是对svg的处理
    var nodesToRecover = [];
    var nodesToRemove = [];
    var svgElem = $("#canvas1").find('svg');//divReport为需要截取成图片的dom的id

    svgElem.each(function (index, node) {
        var parentNode = node.parentNode;
        var svg = node.outerHTML.trim();

        var canvas = document.createElement('canvas');
        canvg(canvas, svg); 
        if (node.style.position) {
            canvas.style.position += node.style.position;
            canvas.style.left += node.style.left;
            canvas.style.top += node.style.top;
        }

        nodesToRecover.push({
            parent: parentNode,
            child: node
        });
        parentNode.removeChild(node);

        nodesToRemove.push({
            parent: parentNode,
            child: canvas
        });

        parentNode.appendChild(canvas);
    });
}

最终生成的图片效果如下,可以看到箭头也可以正常地进行显示了:

img

html2canvas的异步处理问题

到这里,缩略图的生成已经解决了,但还遇到了一个问题:如何将缩略图通过ajax上传。但这里还踩了一个异步函数的大坑:

在原来的程序中,首先使用get_network()函数获取当前的模型并加工成JSON数据,本来打算在这里加入截图函数canvas_gen(),然后将得到的dataURL作为JSON的一个属性一并上传,但这里遇到一个问题:html2canvas是异步执行,如果用同步的方式执行canvas_gen()get_network()返回的JSON属性一定是undefined。上网查找了控制函数异步执行顺序的一些方式,但都没找到很好的解决方式。于是改在canvas_gen()中html2canvas的回调函数里执行get_network(),虽然不是很漂亮的解决方式,但至少能够正确地将缩略图作为一个属性,插入到JSON数据中了。代码如下:

    var dataURL;
    html2canvas(document.querySelector("#canvas1"),{
        //Whether to allow cross-origin images to taint the canvas
        allowTaint: true,
        //Whether to test each image if it taints the canvas before drawing them
        taintTest: false,
    }
    ).then(canvas => {
        $("#canvas1").remove();
        document.body.appendChild(canvas);
        $("canvas").attr("id", "cvs");
        var cvs = document.getElementById("cvs");
        dataURL = cvs.toDataURL();
        //Canvas2Image.saveAsImage(canvas, 760, 1000, 'png');
        $("#canvas").find("#border").attr("style","height:1000px;border:0.5px solid black");
        $("canvas").remove();
        var data = get_network();
        data["graph"] = dataURL;
        //上一版本中的save_network
        //如果可能的话,希望使用异步回调更漂亮地解决
        save_network_origin(data);
    });

这里save_network_origin(data)函数中有ajax的POST操作,这样至少可以保证这几个异步函数确实是按照期望的顺序执行的。

posted @ 2020-05-25 23:07  ITAS2024  阅读(1025)  评论(0编辑  收藏  举报