zoukankan      html  css  js  c++  java
  • 将HTML元素转换成图片供用户下载(html2canvas + canvas2Image)

    这是项目中遇到的一个问题,起初觉得把一个html元素生成图片,提供给用户下载的需求挺容易实现的,毕竟看过一些截图的插件,但是在真正操作中遇到了各种各样的问题,比如移动端上截图显示不清晰,html元素中含有跨域的img图片导致污染canvas等等等。。到现在我还没有真正解决这里的一些问题,先暂时记录一下使用的情况吧;

    需求:

    如下图,将一个包含有文字、图片的元素生成一张图片;
    

    工具:

    html2canvas + canvas2Image 
    

    使用问题:

    起初觉得使用html2canvas还是挺简单的,只需要html2canvas(DOM)就能解决,但是遇到的问题并没那么简单;
    1、首先需求是在移动的中使用,这就遇到了Retina的问题;
    现在的手机屏幕大多数都是分辨率为2,也就是原来的两个像素到了手机上成一个像素显示,这也导致了canvas截得的图在移动端上显示变得很模糊;
    正常的处理方式当然是将图片放大分辨率的倍数,然后再放入原大小的元素中;
    网上看了很多对于该问题的方法,但是没有一个完全解决的,但有 大神github Demo,很大的解决了我的问题;
    修改了两处代码(具体修改请见大神github Demo),主要是用于放大和设置对应大小的canvas,已经可以很完美的解决图片模糊的问题了
    但是!!!!! 我的移动端页面是试用rem进行布局,rem值不定导致canvas绘图区域的宽高计算错误,无法完整包含整个元素;
    最后的解决办法: 在调用html2canvas的时候,canvas.width 和 canvas.height 设置为一个远大于元素宽度的值,使其canvas的绘图区域等于canvas的大小,不会出现截图不完整的情况

             var main = {
                    var main = {
                    init:function(){
                        main.setListener();
                    },
                    //设置监听事件
                    setListener:function(){
                        main.html2Canvas();
                    },
                    //获取像素密度
                    getPixelRatio:function(context){
                        var backingStore = context.backingStorePixelRatio ||
                        context.webkitBackingStorePixelRatio ||
                        context.mozBackingStorePixelRatio ||
                        context.msBackingStorePixelRatio ||
                        context.oBackingStorePixelRatio ||
                        context.backingStorePixelRatio || 1;
                        return (window.devicePixelRatio || 1) / backingStore;
                    },
                    //绘制dom 元素,生成截图canvas
                    html2Canvas: function () {
                        var shareContent = $("#view")[0];// 需要绘制的部分的 (原生)dom 对象 ,注意容器的宽度不要使用百分比,使用固定宽度,避免缩放问题
                        var width = shareContent.offsetWidth;  // 获取(原生)dom 宽度
                        var height = shareContent.offsetHeight; // 获取(原生)dom 高
                        var offsetTop = shareContent.offsetTop + 999;  //元素距离顶部的偏移量
    
                        var canvas = document.createElement('canvas');  //创建canvas 对象
                        var context = canvas.getContext('2d');
                        var scaleBy = main.getPixelRatio(context);  //获取像素密度的方法 (也可以采用自定义缩放比例)
                        canvas.width = width * scaleBy + 999;   //这里 由于绘制的dom 为固定宽度,居中,所以没有偏移
                        canvas.height = (height + offsetTop) * scaleBy;  // 注意高度问题,由于顶部有个距离所以要加上顶部的距离,解决图像高度偏移问题
                        context.scale(scaleBy, scaleBy);
    
                        var opts = {
                            // allowTaint:true,
                            // 允许加载跨域的图片
                            useCORS:true,
                            tainttest:true, //检测每张图片都已经加载完成
                            scale:scaleBy, // 添加的scale 参数
                            canvas:canvas, //自定义 canvas
                            logging: true, //日志开关,发布的时候记得改成false
                            width, //dom 原始宽度
                            height:height //dom 原始高度
                        };
                        html2canvas(shareContent, opts).then(function (canvas) {
                            var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height);
                            var body = document.getElementsByTagName("body");
                            // body[0].appendChild(canvas);
                            // body[0].appendChild(img);
                            $('#view').html(img);
                            img.setAttribute('width', width);
                            img.setAttribute('height', height);
                        });
                        setTimeout(function() {
                            $('.loadingbox').fadeOut(1500);
                        });
                    }
                };
    
                //最后运行代码
                // setTimeout(function() {
                    main.init();
                // }, 1000);
    

    2、html元素中包含外部域名的图片,导致canvas无法截图:
    解决方式:
    ①、使用官方提供的proxy代理,需要后端;官方解决图片跨域方式
    ②、修改html2canvas源码,对跨域图片做特殊处理

    function ImageContainer(src, cors) {
        this.src = src;
        this.image = new Image();
        var self = this;
        var loation = location.hostname;         //获取主域名
        var syc = this.src.indexOf(loation) < 0 && this.src.indexOf('data:image/png;base64') < 0;     //用于区分是否是跨域的图片
        this.tainted = null;
        this.promise = new Promise(function(resolve, reject) {
            self.image.onload = resolve;
            self.image.onerror = reject;
            if (cors) {
                if (syc) { 
                    self.image.crossOrigin = "";     //跨域图片处理1.去掉此处的anonymous代码改为空
                } else {
                    self.image.crossOrigin = "anonymous";
                };
            }
            syc? self.image.src = src+"?"+new Date().getTime() : self.image.src = src;   //跨域图片处理2.在源src后面添加一个随机数如时间戳
            if (self.image.complete === true) {
                resolve(self.image);
            }
        });
    }
    
    然后修改html2canvas参数 useCORS:true;//这个地方只需要将useCORS设置成true,千万不要加allowTaint:true这两个不要同时加,我当时在看官网文档的时候,看到这两个参数都是解决跨域的,后来就两个都加上去了,发现两个加上去就会有问题
    
    

    至此,才算刚刚能达到需求的要求,但是对于rem不定的布局中,如何真正准确计算canvas的绘图区域大小还是没有真正的搞懂,只是暂时能够使用,先记录下来,最后放上整合好的html2canvas.js

    2017.07.06:
    关于在截取的元素中插入图片跨域的问题,由于公司将图片保存在其他云平台(如七牛类型的),并设置了域名访问限制,导致了上面问题2方法无效,即使是以上代码是允许访问的域名中执行也不行。。。。所以只能转换思路,将图片转换成base64,但是蛋疼的是前端将线上图片转换成base64最常见的方法就是canvas 和 XMLHttpRequest,却也都无法请求下来, 都是报Access-Control-Allow-Origin不允许的错误;所以。。。。只能让后端来完成转成base64了,极度的蛋疼。。。

    2017.08.21:
    运营反馈在iphone的微信中,生成的图片会消失,变成一片白色;经过测试,生成canvas没有问题,canvas生成img也没有问题,问题就出在了img插入文本中的时候,原来使用的是$('#view').html(img); #view中包含了原来要生成canvas的dom,可能是直接覆盖出的为题,最后用两个div分别包裹dom ;和生成的img,img生成后,隐藏dom,暂时测试没有问题,不知道上线后会不会有妖蛾子

  • 相关阅读:
    Android Studio 单刷《第一行代码》系列 05 —— Fragment 基础
    Android Studio 单刷《第一行代码》系列 04 —— Activity 相关
    Android Studio 单刷《第一行代码》系列 03 —— Activity 基础
    Android Studio 单刷《第一行代码》系列 02 —— 日志工具 LogCat
    Android Studio 单刷《第一行代码》系列 01 —— 第一战 HelloWorld
    IDEA 内网手动添加oracle,mysql等数据源,以及server returns invalid timezone错误配置
    eclipse maven设置
    IntelliJ IDE 常用配置
    eclipse maven 常见问题解决方案
    Maven 安装和配置
  • 原文地址:https://www.cnblogs.com/milo-wjh/p/7121575.html
Copyright © 2011-2022 走看看