如何实现分享海报

本文最后更新于:2023年1月12日 下午

前言

今天我们来聊聊关于前端生成海报的问题,一般我们制作海报,都会选择前端进行生成。将需要分享出去的部分的dom直接生成图片,然后把这个图片分享给用户。在这里我们通常使用的是html2canvas这个库,下面我们就来实现一下

html2canvas文档在这里 http://html2canvas.hertzen.com

查看本文的完整代码可直接移步到文章末尾

动手时间(挖坑时间)

根据官方文档安装依赖

1
yarn add html2canvas

假设下面就是我们要生成的海报内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// from https://mobile.ant.design/zh/components/image
const demoSrc =
'https://images.unsplash.com/photo-1567945716310-4745a6b7844b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=60'

function SharePoster() {

return <div ref={wrapperRef} className='share-poster'>
{/**商品的图片 */}
<img src={demoSrc} className='share-poster--img' onLoad={imgeLoad}/>
<div>这是标题</div>
<div>这是另一个标题</div>
<div>这是还是一个标题</div>
</div>
}

export default SharePoster

现在,内容有了,该是生火…不对,该是生成海报的时候了,添加html2canvas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import html2canvas from 'html2canvas'
import { useRef } from "react"
import './index.less'

const demoSrc =
'https://images.unsplash.com/photo-1567945716310-4745a6b7844b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=60'


function SharePoster() {
const wrapperRef = useRef(null)

// 等待图片加载完成
const imgeLoad = () => {
if (wrapperRef.current) {
html2canvas(wrapperRef.current, {
useCORS: true
}).then(canvas => {
// 将生成的canvas转换成base64
const base64 = canvas.toDataURL('image/png')
// 到这里我们的海报就生成了
console.log(base64)

// 下载到本地
// const a = document.createElement('a')
// a.href = base64
// a.download = +new Date() + '.png'
// a.click()
})
}
}

return <div ref={wrapperRef} className='share-poster'>
{/**商品的图片 */}
<img src={demoSrc} className='share-poster--img' onLoad={imgeLoad}/>
<div>这是标题</div>
<div>这是另一个标题</div>
<div>这是还是一个标题</div>
</div>
}

export default SharePoster

恭喜🎉🎉🎉,你在1分钟不到的时间,就完成了产品的需求,我们现在发布到线上吧

测试:我说大兄弟呀,你这写的啥呀,下载下来的海报,除了文字,商品图片都没有,你是在玩我吗?

!!!咋回事,本地环境不是没毛病吗

填坑时间

首先我们有了解到canvas不支持加载跨域的图片资源,canvas会认为这回污染画布,而html2canvas用到了canvas,所以就存在转换图片时出错。

那我们怎么解决这个问题呢,我们可以从另一个方向思考,既然不让直接加载图片,那我们加载base64的图片总可以吧

将图片转换成base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// from https://www.w3docs.com/snippets/javascript/how-to-convert-the-image-into-a-base64-string-using-javascript.html
function toDataUrl(src, callback) {
const image = new Image()
// 执行一个跨域请求
// https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/img#attr-crossorigin
image.crossOrigin = 'Anonymous'
image.onload = function () {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
// 将图片画到canvas中
ctx.drawImage(image, 0, 0);
const dataURL = canvas.toDataURL('image/png')

callback(dataURL)
}
// 加载图片
image.src = src
}

更改SharePoster组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ....
const [base64, setBase64] = useState('')

useEffect(() => {
toDataUrl(demoSrc, setBase64)
}, [])

// 等待图片转换成base64
if (!base64) {
return null
}

return <div ref={wrapperRef} className='share-poster'>
{/**商品的图片 */}
{/**现在这里使用base64,而不是图片地址 */}
<img src={base64} className='share-poster--img' onLoad={imgeLoad}/>
<div>这是标题</div>
<div>这是另一个标题</div>
<div>这是还是一个标题</div>
</div>

对于做了cdn处理的图片,需要在图片资源尾部加个时间戳,避免加载缓存导致生成海报失败

1
2
3
4
5
6
// ...

useEffect(() => {
toDataUrl(demoSrc + 'v=' + +new Date(), setBase64)
}, [])
// ...

完整代码 https://codesandbox.io/s/share-poster-vg3gt6


如何实现分享海报
https://www.yxlazy.xyz/2022/06/30/如何实现分享海报/
作者
yxlazy
发布于
2022年6月30日
更新于
2023年1月12日
许可协议