最近开发上线一个app用户生成海报保存图片到本地并分享图片到微信好友。因为在app环境下,所以选择了html2canvas来分享海报。
html2canvas介绍
html2canvas是一个比较成熟的开源库,它能将dom转化为canvas,省去了使用原生canvas一点一点去绘制。
下载html2canvas(安装依赖)
npm install html2canvas -S
在vue中使用html2canvas
<view class="answer_canvas" style="width: 100%;height: 100%;position: absolute;left: -1000%;" data-type="background-image">
<view class="poster_main answer_draw_canvas" data-type="background-image">
<view id="capture">
<view class="answer_draw_canvas" style="margin: 20rpx;">
<image class="poster_img answer_draw_canvas" src="https://img.mayituifu.com/2023/10/4c32249068b943f5857bd84bb44af2f4.png" data-type="image" data-url="https://img.mayituifu.com/2023/10/4c32249068b943f5857bd84bb44af2f4.png" data-delay="1" />
</view>
<view class="inner answer_draw_canvas game_img" data-type="background-image">
<image v-for="imgItem in gitemGamePic" class="gameimg answer_draw_canvas" :src="thisBaseUrl+imgItem" data-type="image" :data-url="thisBaseUrl+imgItem" mode="" data-delay="1"></image>
</view>
<view class="game-list-box1 answer_draw_canvas" data-type="background-image">
<view class="game-list-item answer_draw_canvas" v-for="(gitem,gindex) in hbwydata" :key="gindex" data-type="background-image">
<view class="game-item-info answer_draw_canvas" data-type="background-image">
<image style="width: 80rpx;height: 80rpx;" class="answer_draw_canvas game-item-image" :src="gitem.imgs" data-type="radius-image" :data-url="gitem.imgs" data-delay="1"></image>
<view class="game-info-box answer_draw_canvas" data-type="background-image">
<view class="game-info-title answer_draw_canvas" data-type="background-image" @click="savePoster(gitem.gamePic)">
<span class="answer_draw_canvas" style="font-weight: 700;" data-type="text" :data-text="gitem.prodName">{{gitem.prodName}}</span>
</view>
<view class="gameLabel3" data-type="background-image answer_draw_canvas">
<view class="label answer_draw_canvas" data-type="text" :data-text="gitem.versionClass" style="background:#FDF9F1">{{gitem.versionClass}}</view>
<view class="label answer_draw_canvas" data-type="text" :data-text="versionType[gitem.versionType]">{{versionType[gitem.versionType]}}</view>
<view class="label answer_draw_canvas" data-type="text" :data-text="speed[gitem.speed]">{{speed[gitem.speed]}}</view>
</view>
</view>
</view>
<view class="game-item-content answer_draw_canvas" @click="gotopage('/basePages/pages/gameDetails/index?id='+gitem.prodId)" data-type="text" :data-text="gitem.brief">{{gitem.brief}}</view>
<view class="game-item-income answer_draw_canvas">
<view class="game-income-item answer_draw_canvas">
<view class="game-income-item-val answer_draw_canvas" data-type="text" :data-text="gitem.goldType ? gitem.goldType : ''">{{ gitem.goldType ? gitem.goldType : '' }}</view>
<view class="game-income-item-key answer_draw_canvas" data-type="text" data-text="毕业天数">毕业天数</view>
</view>
<view class="answer_draw_canvas" data-type="text" style="width: 1rpx;height: 28rpx;background: #e5e5e5;"></view>
<view class="game-income-item answer_draw_canvas" data-type="background-image">
<view class="game-income-item-val answer_draw_canvas" data-type="text" :data-text="gitem.signCommission ? gitem.signCommission + '%' : 0 + '%'">{{gitem.signCommission ? gitem.signCommission : 0}}%
</view>
<view class="game-income-item-key answer_draw_canvas" data-type="text" data-text="佣金比例">佣金比例</view>
</view>
<view class="answer_draw_canvas" data-type="text" style="width: 1rpx;height: 28rpx;background: #e5e5e5;"></view>
<view class="game-income-item answer_draw_canvas" data-type="background-image">
<view class="game-income-item-val answer_draw_canvas" data-type="text" :data-text="gitem.fullamount ? gitem.fullamount + `元` : 0">{{gitem.fullamount ? gitem.fullamount + `元` : 0}}</view>
<view class="game-income-item-key answer_draw_canvas" data-type="text" data-text="满赞金额">满赞金额</view>
</view>
</view>
</view>
</view>
<view style="padding: 0 30rpx;display: flex;justify-content: space-between;align-items: center;padding-bottom: 40rpx;" class="answer_draw_canvas" data-type="background-image">
<view class="answer_draw_canvas" data-type="background-image">
<view class="answer_draw_canvas small-title" data-type="text" data-text="长按识别-探索游戏" style="margin-bottom: 16rpx;">长按识别-探索游戏</view>
<view class="answer_draw_canvas little-title" style="margin-bottom: 6rpx;" data-type="text" data-text="180神影火龙-单件套装一">180神影火龙-单件套装一</view>
<view class="answer_draw_canvas little-title" data-type="text" data-text="专为散人设计,公平公正.等你来战!">专为散人设计,公平公正.等你来战!</view>
</view>
<view>
<image class="answer_draw_canvas" :src="codeImg" style="width: 120rpx;height: 120rpx;" data-type="image" :data-url="codeImg"></image>
</view>
</view>
</view>
</view>
</view>
<view v-if="createCanvas" :prop="canvasImageMsg" :change:prop="canvasImage.updateEcharts"
></view>
常用配置项
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
allowTaint | boolean | false | 是否允许跨域图像污染画布 |
useCORS | boolean | false | 是否跨域加载图片 |
backgroundColor | string | #fff | 背景色 |
width | number | 元素宽度 | 单元格 |
height | number | 元素高度 | 单元格 |
scale | number | window.devicePixelRatio | 缩放比例 |
scrollX | number | 元素x轴滚动位置 | 生成后x轴滚动位置 |
scrollY | number | 元素y轴滚动位置 | 生成后y轴滚动位置 |
html2canvas绘制跨域图片
然limg标签需要添加crossorigin="anonymous"
生成在线地址
在内存中生成我们所需要的canvas对象,然后利用canvas的api toDataUrl和toBlob转换成图片数据,然后可保存下载。
js代码
<script lang="renderjs" module="canvasImage">
import html2canvas from 'html2canvas'
export default {
data() {
return {
baseUrl: ''
}
},
methods: {
generateImage(callback) {
setTimeout(() => {
const dom = document.getElementById('capture'); // 需要生成图片内容的 dom 节点
html2canvas(dom, {
width: dom.clientWidth, //dom 原始宽度
height: 560,
scrollY: 0, // html2canvas默认绘制视图内的页面,需要把scrollY,scrollX设置为0
scrollX: 0,
useCORS: true, //支持跨域
allowTaint: true, ///允许跨域图片
// scale: 1, // 设置生成图片的像素比例,默认是1,如果生成的图片模糊的话可以开启该配置项
}).then((canvas) => {
const base64 = canvas.toDataURL('image/png');
this.baseUrl = base64
callback&&callback(base64);
}).catch(err=>{})
}, 300);
},
updateEcharts(newValue, oldValue, ownerInstance, instance) {
// 监听 service 层数据变更
console.log(newValue,'newValue')
// console.log(this.baseUrl,'this.baseUrl')
if(newValue){
this.generateImage((base64)=>{
console.log(base64,'base64')
ownerInstance.callMethod('receiveRenderData', base64)
})
}
}
}
}
</script>
// #ifdef APP-PLUS
this.canvasImageMsg = 'generate'
// 因为uniapp中不能获取dom元素,所以引入renderjs
// 生成图片后的回调函数
receiveRenderData(base64) {
this.poperShow = true
uni.hideLoading({})
this.posterLink = base64
this.canvasImageMsg = null
},
遇到的问题
1、图片生成的时间长
第一个环节-html2canvas,配置 ignoreElements 函数或者在DOM上增加data-html2canvas-ignore
属性,去除冗余的DOM,其次精简需要渲染的DOM范围,
第二个环节-canvas to Blob,提前生成blob,上传到后台,获取在线url,增加缓存在线地址。
2、报错errcode 1009
生成的图片地址为本地地址,所以在上线以后保存下载会报错,下载不下来,解决方案是把图片提前上传到服务器,在线图片下载则不会出现此问题。