关于前端使用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:开发中想的比较多,现在总结文档可能有一些遗漏。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • 夜色中,装点着璀璨灯火的大榕树下,一个美丽的女子坐在星星点点的秋千上缓缓摇荡。 “阿斯,你快来推我呀!” 身材欣长...
    听花的小孩阅读 543评论 0 0
  • R:要做一个用心的人,要用心做事,因为这世界其实也有“心”。 I:用心做事,用心对人,环境会给你相应反馈,自己继续...
    dbnfjfkkf阅读 154评论 0 0