本文最后更新于:2023年1月12日 下午
前言
今天我们来聊聊关于前端生成海报的问题,一般我们制作海报,都会选择前端进行生成。将需要分享出去的部分的dom直接生成图片,然后把这个图片分享给用户。在这里我们通常使用的是html2canvas
这个库,下面我们就来实现一下
html2canvas文档在这里 http://html2canvas.hertzen.com
查看本文的完整代码可直接移步到文章末尾
动手时间(挖坑时间)
根据官方文档安装依赖
假设下面就是我们要生成的海报内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 => { const base64 = canvas.toDataURL('image/png') console.log(base64)
}) } }
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
| function toDataUrl(src, callback) { const image = new Image() image.crossOrigin = 'Anonymous' image.onload = function () { const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') 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) }, [])
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