关于前端使用html2canvas.js生成长图开发文档

1.需求分析:

把用户在ueditor里面编辑好的文本的html代码转化为长图;

2.技术分析:

这里主要用到了html2canvas.js,官网地址:http://html2canvas.hertzen.com/

3.技术支持:

Firefox 3.5+
Google Chrome
Opera 12+
IE9+
Safari 6+

4.具体实践:

(1).首先是了解对html2canvas.js的初步使用:

//参数说明:id:所需要截图的元素id
html2canvas(document.getElementById('id')).then(function(canvas){
    document.body.appendChild(canvas);
});

基本参数说明,官网上都有,具体可以参照官网说明,这里不再做详细解释;

(2).通过以上的配置,其实可以简单的实现生成长图的功能,但这里有几个坑需要说明一下;

  • 截图不清楚(这是很大的一个问题):
    通过上网查询相关资料以及熟悉源码,发现,它是通过在canvas上模拟dom结构以及样式来绘制成的canvas画布,那么,想让画布清晰,我们可以在canvas.toDataURL(type, encoderOptions);时做一些操作,先来看一下这个api的语法:

那么这样一看,想配置图片质量可以通过:

canvas.toDataURL("image/jpeg", 1.0);

不过在我经过大量实践之后,发现图片的清晰度并没有多大的改善,反而生成的图片越来越大,那么这肯定不是我们想要的,被pass掉;

解决方案:
通过修改源码,让canvas可以配置放大比例,主要修改的有两处:

==>代码第 999 行 renderWindow 的方法中 修改判断条件 增加一个options.scale存在的条件:
源码:

if (options.type === "view") {
                canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0});
            } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) {
                canvas = renderer.canvas;
            } else {
                canvas = crop(renderer.canvas, {width:  options.width != null ? options.width : bounds.width, height: options.height != null ? options.height : bounds.height, top: bounds.top, left: bounds.left, x: 0, y: 0});

            }

修改为:

if (options.type === "view") {
                canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0});
            } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement) {
                canvas = renderer.canvas;
            }else if(options.scale && options.canvas !=null){
                log("放大canvas",options.canvas);
                var scale = options.scale || 1;
                canvas = crop(renderer.canvas, {width: bounds.width * scale, height:bounds.height * scale, top: bounds.top *scale, left: bounds.left *scale, x: 0, y: 0});
            }
            else {
                canvas = crop(renderer.canvas, {width:  options.width != null ? options.width : bounds.width, height: options.height != null ? options.height : bounds.height, top: bounds.top, left: bounds.left, x: 0, y: 0});
            }

==>代码第 943 行 html2canvas 的方法中 修改width,height:
源码:

return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {
    if (typeof(options.onrendered) === "function") {
        log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
        options.onrendered(canvas);
    }
    return canvas;
});

修改为:

width = options.width != null ? options.width : node.ownerDocument.defaultView.innerWidth;
height = options.height != null ? options.height : node.ownerDocument.defaultView.innerHeight;
return renderDocument(node.ownerDocument, options, width, height, index).then(function(canvas) {
    if (typeof(options.onrendered) === "function") {
        log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
        options.onrendered(canvas);
    }
    return canvas;
});

使用方法:

var realHtml = document.getElementById("html-canvas");//需要截图的包裹的(原生的)DOM 对象
        var width = realHtml.offsetWidth; //获取dom 宽度
        var height = realHtml.offsetHeight; //获取dom 高度
        var canvas = document.createElement("canvas"); //创建一个canvas节点
        var scale = 2; //定义任意放大倍数 支持小数
        canvas.width = width * scale; //定义canvas 宽度 * 缩放
        canvas.height = height * scale; //定义canvas高度 *缩放
        canvas.getContext("2d").scale(scale,scale); //获取context,设置scale
        var opts = {
            tainttest:true, //检测每张图片都已经加载完成
            scale:scale, // 添加的scale 参数
            useCORS:true,
            canvas:canvas, //自定义 canvas
            logging: true, //日志开关
            width:width, //dom 原始宽度
            height:height //dom 原始高度
        };


            html2canvas(realHtml, opts).then(function (canvas) {
                //如果想要生成图片 引入canvas2Image.js 下载地址:
                //https://github.com/hongru/canvas2image/blob/master/canvas2image.js
                //var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height);
                imgUri = canvas.toDataURL("image/"+img_type).replace("image/"+img_type, "image/octet-stream");
              
                //var saveLink = document.createElement('a');
                //saveLink.href =imgUri;
                //saveLink.download = 'ipaiban'+new Date().getTime()+'.'+img_type;
               // saveLink.click();
                $('#img').attr('src',imgUri);
                //console.log(img);
                //Canvas2Image.saveAsJPEG(canvas, canvas.width, canvas.height);
                //localStorage.removeItem("img-html");
                //localStorage.removeItem('img-type');
                //localStorage.removeItem('canvas_multiple');
             
            });
  • 通过以上修改之后,还会出现一个更大的问题,关于截图不完整的问题:
    上面的配置之后,我们的需要截取的div结构,必须是从左上角页面坐标(0,0)开始的,这里我使用定位index为负数,在Body下面用absolute定位一个层级,把ueditor中的代码取出来放到这个定位的层级中,然后拿这个层级中的div去生成长图;
    事实证明,我的想法是正确的,但是实际操作中还是碰到了一个问题,我在本地测试完美,而放入编辑器页面还是会出现一些莫名其妙的问题,最后思来想去,可能是编辑器页面的结构以及样式影响到了这个,所以决定新开一个页面去生成长图,正好还可以用来后续的分享到微博使用。

  • 关于图片的跨域问题:
    初步使用配置项useCORS:true,去解决,另外,源码中我也做了一些修改,现在忘了改的哪个地方了,=0=,回头我再找找;

好了,通过以上的操作,基本已经完美解决了前端生成长图的功能,当然,具体的需求和选择样式部分还是需要具体的代码去实践的,这里不在做详细介绍;

相关文件修改目录:

  • 页面文件:
    bianji.jsp
    ps:现在还属于测试期间,我又复制了一份bianji.jsp,命名为editor.jsp供用户测试使用,和bianji.jsp放在同级目录下,页面中已经做了区域注释,可以搜索关键字create-img-dialog,可以快速找到相应的html代码;

  • 具体生成长图的文件:
    imgDownload.html
    ps:真正长图生成的过程在这个文件中;

  • js需求逻辑文件:
    newindex/js/page.toolbarnew_minimalist.js
    ps:放在了文件的开头,我已做了详细的注释以及区域注释

  • css样式文件:
    newindex/css/page.loadnew_1.css
    ps:写在了文件的最后,已经添加了区域注释,class几乎都是以ld-开头命名;

  • img图片文件:
    统一放到了images目录下,方便后期的维护;

ps:开发中想的比较多,现在总结文档可能有一些遗漏。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 157,672评论 24 688
  • 夜色中,装点着璀璨灯火的大榕树下,一个美丽的女子坐在星星点点的秋千上缓缓摇荡。 “阿斯,你快来推我呀!” 身材欣长...
    听花的小孩阅读 173评论 0 0
  • R:要做一个用心的人,要用心做事,因为这世界其实也有“心”。 I:用心做事,用心对人,环境会给你相应反馈,自己继续...
    dbnfjfkkf阅读 66评论 0 0