一文彻底解答H5页面下载和长按保存所有疑问

2,090 阅读2分钟

背景

  产品要求点击这个h5页面的下载按钮可以将图片保存到手机里,支持安卓和ios端

思路

  使用html2canvas将dom转为canvas,再使用canvas.toDataURL将canvas转为base64,创建a链接将图片下载下来.

基本方案:html -> canvas -> image -> a

踩坑

<template>
  <div class="qr-code-dialog">
    <div>
      <div class="block" @click.stop ref="posterHtml">
        <div>
          <div class="img">
            <img ref="avatarUrl" :src="avatarUrl" />
            <span>{{cardInfo.nickName}}</span>
            <div class="dialog"></div>
          </div>
          <div class="title">{{cardInfo.companyName}}</div>
          <div class="qr-code">
            <div><img ref="wxQrcode" :src="wxQrcode" /></div>
            <div>
              <div class="sign">扫描二维码</div>
              <div class="company">添加我为好友吧~</div>
            </div>
          </div>
        </div>
      </div>
      <div class="download" @click="downloadImg"><img src="/img/case/save-img.svg" /></div>
      <div class="close"><img src="/img/case/closeDialog.svg" /></div>
    </div>
  </div>
</template>

methods: {
    downloadImg () {
      let ava = this.$refs.avatarUrl,
        code = this.$refs.wxQrcode
      ava.crossOrigin = 'anoymous'
      code.crossOrigin = 'anoymous'
      ava.src = this.avatarUrl + '?time=' + new Date().valueOf()
      code.src = this.wxQrcode + '?time=' + new Date().valueOf()
      // 先获取你要转换为img的dom节点
      let node = this.$refs.posterHtml//传入的id名称
      let width = node.offsetWidth //dom宽
      let height = node.offsetHeight //dom高
      html2canvas(node, {
        width: width,
        heigth: height,
        backgroundColor: "#ffffff", //背景颜色 为null是透明
        dpi: window.devicePixelRatio * 2, //按屏幕像素比增加像素
        scale: 2,
        useCORS: true, //是否使用CORS从服务器加载图像
        allowTaint: true //是否允许跨域图像污染画布
      }).then(canvas => {
        let url = canvas.toDataURL("image/png") //这里上面不设值cors会报错
        let a = document.createElement("a")//创建一个a标签 用来下载
        let name = '名片海报' + Math.random() + '.png'
        a.download = name //设置下载的图片名称
        a.href = url//此处的url为base64格式的图片资源
        a.click() //触发点击事件
      })
    }
  }

  这里面crossOrigin = 'anoymous'和ava.src = this.cardInfo.avatarUrl + '?time=' + new Date().valueOf()以及useCORS: true这三种方式都是为了解决图片跨域的问题,ava.crossOrigin = 'anoymous'这个跨域一定要在ava.src = this.cardInfo.avatarUrl的上面,是为了兼容ios 后面发现一个问题在其他手机浏览器和PC中都是正常的,但是微信浏览器有问题,安卓微信浏览器会跳转到一个下载页面,ios微信浏览器没有效果

查看 微信sdk后发现:

  • downloadImage 仅支持 uploadImage 接口上传的图片。
  • uploadImage 接口仅支持 chooseImage 接口相册选择的图片。
  • chooseImage 接口是从本地相册选择图片。 那么问题来了,图片都在相册了还需要我们干啥?所以这条路走不通,只能看其他项目怎么实现的,发现其他项目也没有实现这个功能,但是可以实现长按保存功能,所以又开始转向长按保存.

长按保存

方案
  • 用户进入该页面获取当前用户所有信息,头像,二维码等
  • 将所有图片转为 base64,这样子ios图片就不会有跨域问题
  • 渲染 html
  • 绘制 canvas,将canvas转为 base64
  • 替换 html 为 img,src为 base64
代码

进入页面,先将图片转为base64

watch:{
 cardInfo (val) {
  	this.$nextTick(() => {
        this.getBase64(val.avatarUrl, dataURL => {
          this.avatarUrl = dataURL
        })
        this.getBase64(val.wxQrcode, dataURL => {
          this.wxQrcode = dataURL
        })
     })
 }
},
methods: {
  getBase64(url, callback) {
    var Img = new Image(),
      dataURL = ''
    Img.crossOrigin = 'Anonymous'
    Img.src = url + '?time=' + new Date().valueOf()
    Img.onload = function() {
      var canvas = document.createElement('canvas'),
        width = Img.width,
        height = Img.height
      canvas.width = width
      canvas.height = height
      canvas.getContext('2d').drawImage(Img, 0, 0, width, height)
      dataURL = canvas.toDataURL('image/png')
      return callback ? callback(dataURL) : null
    }
}

点击添加微信,显示弹框,将html转为canvas再转为base64,将base64赋值给canvasUrl,这时候最终的图片就会展示出来,并且覆盖在原来的弹框上,因为所有图片都支持长按保存功能,所以就完成这个功能.

methods: {
  downloadImg () {
    let ava = this.$refs.avatarUrl,
      code = this.$refs.wxQrcode
    ava.crossOrigin = 'anoymous'
    code.crossOrigin = 'anoymous'
    ava.src = this.avatarUrl
    code.src = this.wxQrcode
    // 先获取你要转换为img的dom节点
    let node = this.$refs.posterHtml//传入的id名称
    let width = node.offsetWidth //dom宽
    let height = node.offsetHeight //dom高
    html2canvas(node, {
      width: width,
      heigth: height,
      backgroundColor: "#ffffff", //背景颜色 为null是透明
      dpi: window.devicePixelRatio * 2, //按屏幕像素比增加像素
      scale: 2,
      useCORS: true, //是否使用CORS从服务器加载图像
      allowTaint: true //是否允许跨域图像污染画布
    }).then(canvas => {
      this.canvasUrl = canvas.toDataURL("image/png")
    })
  }
}

&emsp;&emsp;总结:一开始的需求是下载海报,后来发现微信浏览器对于下载有限制,只能退而求其次长按保存,需要注意的点有三个一个是如何解决图片跨域问题,有四个地方:Img.crossOrigin = 'Anonymous'和Img.src = url + '?time=' + new Date().valueOf()和图片在渲染之前转base64和useCORS: true,第二个地方是ava.crossOrigin = 'anoymous'需要在ava.src = this.avatarUrl之前,为了兼容ios,这里花费了我好久才找到,最后一个是所有图片都可以长按保存或者识别二维码,这是浏览器自带的功能,所以生成一个没有跨域的图片才是最终目的.这里再说一点自己的感想就是问题定位,如果没有达到你要的效果,肯定是哪里出了问题,先找到出问题的某个范围,在定位到具体某一行才能最终解决问题

参考文章: blog.csdn.net/DavidFFFFFF… juejin.cn/post/684490… www.cnblogs.com/wenfangcao/… juejin.cn/post/684490…