canvas.toDataURL时出现图片资源跨域问题,可行性前端解决方案

问题场景

在大屏项目里,需要对设计好的大屏进行生成图片和保存数据时保存一张截图作为管理页面的封面,由于有很多图表组件的渲染涉及到 img 标签,比如地图组件或轮播图插件,当他们使用不同域的资源,在生成图片时,调用canvas的toDataURL时就会出现代码执行失败的情况。

试过网上提供的其他方案例如给img标签添加 crossorigin = ‘anonymous’ 和给img标签的src加上随机数,这两个方案对于我使用html-to-image插件来生成图片都没有解决问题。

解决方案

第一种:封装一个img组件,内部将img的资源转成base64进行渲染
<template>
  <img
    v-if="innerSrc"
    class="base64-image"
    v-bind="$attrs"
    :src="innerSrc"
    alt=""
    v-on="$listeners"
  >
</template>

<script>
export default {
  name: 'Base64Image',
  props: {
    src: {
      type: String,
      default: ''
    }
  },
  mixins: [],
  data () {
    return {
      innerSrc: ''
    }
  },
  computed: {},
  watch: {
    src: {
      immediate: true,
      handler (val) {
        this.toImage(val)
      }
    }
  },
  created () {},
  mounted () {
    this.toImage(this.src)
  },
  methods: {
    getBase64Image (src) {
      // eslint-disable-next-line
      if (!src) return Promise.reject('未提供图像源')
      return new Promise((resolve, reject) => {
        const img = new Image()
        img.crossOrigin = 'Anonymous'
        img.onload = function () {
          const canvas = document.createElement('canvas')
          const ctx = canvas.getContext('2d')
          canvas.height = img.height
          canvas.width = img.width
          ctx.drawImage(img, 0, 0)
          const dataURL = canvas.toDataURL('image/png')
          resolve(dataURL)
        }
        img.onerror = function () {
          // eslint-disable-next-line
          reject('加载图像失败')
        }
        img.src = src
      })
    },
    async toImage (src) {
      try {
        this.innerSrc = await this.getBase64Image(src)
      } catch (error) {
        console.error('将图像转换为base64时出错:', error)
      }
    }

  }
}
</script>

<style lang="scss" scoped>
/* Your styles here */
</style>
第二种:重新请求图片资源,不使用本地缓存资源

因为项目里生成图片的操作使用的html-to-image插件,这里提供与它相关代码

const node = document.querySelector('')
// 获取node 下的所有img标签,拿到他们的src
const imgTags = node.querySelectorAll('img')
const requests = Array.from(imgTags).map(img => {
    const src = img.getAttribute('src')
    return fetch(src, {
        headers: { 'Access-Control-Allow-Origin': '*' }
    }).then(response => {
        if (response.ok) {
            return response.blob()
        } else {
            throw new Error('网络请求失败')
        }
    }).then(blob => {
        return new Promise((resolve, reject) => {
            const reader = new FileReader()
            reader.onload = () => resolve(reader.result)
            reader.onerror = reject
            reader.readAsDataURL(blob)
        })
    }).then(dataUrl => {
        img.setAttribute('src', dataUrl)
    }).catch(error => {
        console.error(error)
    })
})
// 等所有请求都结束后执行生成图片操作
Promise.all(requests).then(() => {
    toPng(node)
        .then((dataUrl) => {
            const link = document.createElement('a')
            link.download = `${this.pageInfo.name}.png`
            link.href = dataUrl
            link.click()
            link.addEventListener('click', () => {
                link.remove()
            })
        }).catch((error) => {
            console.info(error)
            this.$message.warning('出现未知错误,请重试')
        })
}).catch(error => {
    console.error(error)
})

上面的解决方案也仅是针对Img标签跨域问题,当截图的内容里有Canvas,Canvas里有图片资源跨域和使用css 的background-image的时候,还是需要在canvas渲染的时候,先将图片预先处理成base64资源再进行渲染,这样再截图是没有问题的,可以借助方案一里的getBase64Image方法进行数据预处理。

另外,大屏项目地址:DataRoom

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值