一、dom-to-image基本使用

npm地址

import domtoimage from 'dom-to-image'

export function dom2img(node) {
  return domtoimage.toPng(node, { cacheBust: true })
    .then(function(dataUrl) {
      var img = new Image();
      img.src = dataUrl;
      document.body.appendChild(img);
      return dataUrl
    })
    .catch(function(error) {
      console.error('oops, dom2img went wrong!', error);
    });
}

// 实现将dom转成图片
// node: this.$refs.id 或者 document.getElementById('id')
dom2img(node).then(img => {
 // 处理base64格式的img
}

可以添加参数 , 比如控制大小,修改样式

domtoimage.toJpeg(node, {
   width: 330,
    height: 155,
    cacheBust: true,
    style: {
      margin: 0,
      background: '#fff',
    }
  })

二、注意点

dom-to-image
将dom转图片有个前提,就是一定要在文档中显示,任何样式导致不显示都导致无法生成图片,比如:visibility:hidden,display: none;
宽高不能是100%,否则也是转换失败

如果是动态引入的组件转换,要保证组件没有需要异步加载的部分,比如一些背景图是请求接口的, 只能在页面显示后,通过$refs来获取dom,动态引入不知道什么时候加载完成的

toPng 要去掉dom样式中的margin,否则切图出来的会有被截断的问题

三、生成的图片上传至服务器

平时上传图片主要是用到<input>元素, 用户选择的文件最终是File对象,所以,我们将生成的图片转成File对象,然后通过formData上传即可。
这里主要用png格式和blob格式举例

1、base64 转 File 格式
function base64toFile(dataUrl, fileName = 'image') {
  let arr = dataUrl.split(',')
  let mime = arr[0].match(/:(.*?);/)[1]

  let bstr = window.atob(arr[1]) // base64编码
  let n = bstr.length
  let u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n) // unicode 编码
  }
  return new File([u8arr], `${fileName}.png`, {
    type: mime
  })
}
2、blob 转 File 格式

File继承自Blob,是特殊类型的 Blob。

function blobtoFile(blob, fileName = 'image') {
  const file = new File([blob], fileName, {
    type: blob.type
  })
  return file
}
3、在页面展示生成的图片
function showImg(dataUrl) {
  var img = new Image()
  img.src = dataUrl
  document.body.appendChild(img)
}

4、最终dom转图片函数
// XXX domtoimage 转图片有个前提,一定要在文档中显示,任何样式导致不显示都导致无法生成图片
// toPng(node, {width: 330, height: 155, quality: 0.95 }), 可以添加参数
// toPng 要去掉dom样式中的margin, 否则切图会有问题
import domtoimage from 'dom-to-image'
export function dom2img(node, fnType = 'toPng') {
  return domtoimage[fnType](node, {
      cacheBust: true
    })
    .then(function(dataUrl) {
      // png
      if (fnType === 'toPng') {
        // 显示图片
        showImg(dataUrl)
        // 生成file格式用于上传
        return base64toFile(dataUrl)
      }

      // blob 
      if (fnType === 'toBlob') {
        // 显示图片: 通过Blob Url,blob://模式类似于http://, 
        const blobUrl = URL.createObjectURL(dataUrl)
        showImg(blobUrl)
        // 防止内存溢出,要手动释放
        setTimeout(() => {
          URL.revokeObjectURL(blobUrl)
        }, 200)

        // 生成file格式用于上传, 这个有问题,接口报错-- FIXME
        return blobtoFile(dataUrl)
      }

      return dataUrl
    })
    .catch(function(error) {
      console.error('oops, dom2img went wrong!', error)
    })
}
5、上传图片
function uploadImgToBoot(imgFile) {
  const formData = new FormData();
  formData.append('file', imgFile)
  return uploadZhImg(formData).then(res => {
    return res
  })
}
6、问题未解决

blob转File后,上传图片接口报错, 未找到原因


四、vue实现将不显示的组件转成图片,而组件之间出现循环引用

在这里插入图片描述

1、首先在html中放一个占位div,用于渲染dom

 <!-- 用于挂载占位,不需要显示-->
 <div id="hideNode" class="show-in-back"></div>
 
 // 不能使用visibility:hidden 或者 display:none, 会导致dom转换失败
 .show-in-back {
   position: absolute;
   top: 0;
   z-index: -1000;
 }

2、这里使用动态引入避免组件死循环:

// XXX 可以使用这种动态引入的前提是: 组件没有需要异步加载的部分,比如合集中的背景图是请求接口的, 只能通过$refs来
// 动态引入组件,避免组件之间出现循环引入,当出现死循环时,import的结果为undefined
const ApplyCard = () => import('@/views/apply/components/ApplyCard.vue')

这里有两种vue的引入方式: 模板编译和runtime模式

// 默认runtime模式,指向了"dist/vue.runtime.common.js"位置
import Vue from 'vue'
// 下面引入采用的是模板编译引入链接
// import Vue from 'vue/dist/vue.esm.js'

3、挂载点内容会被vue组件覆盖,所以每次要新建一个挂载点内容,设置id="toImg"

// 新建挂载点内容
const node = document.getElementById('hideNode')
node.innerHTML = '<div id="toImg"></div>'

4、利用vue动态构建组件,并挂载到id="toImg"

ApplyCard().then(res => {
  // 获取vue组件
  const temp = res.default

  const Card = Vue.extend(temp)
	
  // 动态挂载相应的组件
  // 获取组件,这里采用的是模板编译 
  // -- 存在问题:上面更换了vue,导致在组件没有显示的页面,渲染组件会报错: element组件以及自定义全局组件没有引入
  // const vueComponent = new Card({
  //   el: '#toImg',
  //   propsData: {
  //     info: {}
  //   }
  // })
  
  // 动态挂载相应的组件改成render函数渲染
  const vueComponent = new Vue({
    el: '#toImg',
    render: function (h) {
      return <Card info={info}></Card>
    }
  })

  // 等加载完之后,获取dom
  vueComponent.$nextTick(() => {
    const node = vueComponent.$el
    // 生成图片并分享招乎
    dom2img(node).then(img => {
      // 处理base64格式的img
    })
  })
})
Logo

前往低代码交流专区

更多推荐