需求:
小程序实现生成海报功能,海报上面加头像和姓名以及二维码
实现:Taro 小程序(js + redux + sass)
import { ComponentClass } from 'react'
import Taro, { Component } from '@tarojs/taro'
import { View, Image, Text, Video, Button } from '@tarojs/components'
import { connect } from '@tarojs/redux'
import {
getUserInfoApi,
getLittleAppQRCodeApi
} from '../../service/api.service';
import './collagePoster.scss'
@connect(({ globalData, authData, payInfoData }) => ({
globalData,
authData,
payInfoData
}))
class collagePoster extends Component {
config = {
navigationBarTitleText: '生成海报',
pages: [
'pages/collagePoster/collagePoster'
]
};
constructor(props) {
super(props);
this.state = {
headPhoto: '', // 头像
avatorSrc: '',
studentName: '', // 姓名
bgImg: 'https://appd.knowbox.cn/ss/miniapp/shareImg/collagePosterBg.png', // 背景图
bgSrc: '',
codeImg: '', // 二维码
codeSrc: '',
shareText: '给我家孩子报了编程课,在家就能上课,大厂课程品质有保障,有一起参团的不?5节课超划算,手慢则无!',
timeStart: 0,
timeEnd: 0,
isCanToAlbum: false, // 是否授权保存图片到相册
showModal: false // 是否展示弹窗
};
}
// onLoad
async componentWillMount() {
Taro.showLoading({
title: '海报生成中...',
mask: true,
})
const {token} = this.props.authData;
await this.getUserInfoFn(token);
}
// onReady
componentDidMount() { }
// onShow
componentDidShow() { }
// onHide
componentDidHide() { }
// onUnload
componentWillUnmount() { }
// 获取头像和姓名以及二维码等信息
async getUserInfoFn(data) {
const resp = await getUserInfoApi(data);
const {Collageid} =this.props.payInfoData.data;
let path = `pages/payCollage/payCollage?collageId=${Collageid}`
const codeResp = await getLittleAppQRCodeApi(path);
const codeImg = codeResp.data;
const {headPhoto, studentName} = resp.data;
this.setState({
headPhoto,
studentName,
codeImg
});
const BGIMG = await this.downloadHttpImg(this.state.bgImg);
const headPhotoImg = await this.downloadHttpImg(headPhoto);
const codeImgSrc = await this.downloadHttpImg(codeImg);
this.setState({
bgSrc: BGIMG,
avatorSrc: headPhotoImg,
codeSrc: codeImgSrc
}, () => {
this.sharePosteCanvas()
})
}
// 封装的下载图片函数
// httpImg: string 图片地址 要是https的哦!
downloadHttpImg (httpImg) {
return new Promise(((resolve, reject) => {
Taro.downloadFile({
url: httpImg,
success: (res) => {
if (res.statusCode === 200) {
resolve(res.tempFilePath)
} else {
Taro.showToast({
title: '图片下载失败!',
icon:'none',
duration: 2000
})
}
},
fail: (res) => {
Taro.showModal({
title: '提示图片下载失败!',
content: `原因:${JSON.stringify(res)}`,
success(res) {
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}
})
}))
}
// 画图
sharePosteCanvas () {
let studentName = this.state.studentName;
let avaterSrc = this.state.avatorSrc;
let codeSrc = this.state.codeSrc;
let bgImg = this.state.bgSrc;
const ctx = Taro.createCanvasContext('posterCanvas');
Taro.createSelectorQuery().select('#collagePosterId').boundingClientRect((rect) => {
let height = rect.height,
width = rect.width,
studentNameTop = height * 0.27,
avatorWidth = width * 0.17, // 头像大小
avatorTop = height * 0.1394,
avatorleft = (width - avatorWidth) / 2,
codeWidth = width * 0.2, // 二维码大小
codeHeight = width * 0.2 * 1.16, // 二维码大小
codeTop = height * 0.76,
codeLeft = width * 0.67,
circle = {
x : avatorleft + avatorWidth / 2,
y : avatorTop + avatorWidth / 2,
r : avatorWidth / 2
},
textLeft = width / 2;
ctx.setFillStyle('#fff');
ctx.fillRect(0, 0, width, height);
// 背景
if (bgImg) {
ctx.drawImage(bgImg, 0, 0, rect.width, height);
}
// 头像
if (avaterSrc) {
ctx.save();
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2, false);
ctx.setFillStyle('#EEEEEE')
ctx.fill()
ctx.clip(); //剪切路径
ctx.drawImage(avaterSrc, avatorleft, avatorTop, avatorWidth, avatorWidth);
ctx.restore();
}
// 用户信息 - 姓名
if (studentName) {
ctx.setFontSize(16);
ctx.setFillStyle('#fff');
ctx.setTextAlign('center');
ctx.fillText(studentName, textLeft, studentNameTop, 300);
}
// 绘制二维码
if (codeSrc) {
ctx.drawImage(codeSrc, codeLeft , codeTop, codeWidth, codeHeight)
}
}).exec()
setTimeout(() => {
ctx.draw(); // 这里有个需要注意就是,这个方法是在绘制完成之后在调用,不然容易其它被覆盖。
wx.hideLoading();
}, 1000)
setTimeout(() => {
this.handleSaveImg()
}, 1000)
}
handleTimeStart () {
this.setState({
timeStart: new Date().getTime()
})
}
// 保存图片
handleSaveImg () {
let that = this;
Taro.getSetting({
success: (res) => {
Taro.authorize({
scope: 'scope.writePhotosAlbum',
success: (res) => {
this.downloadImgToAlbum()
},
fail: (res) => {
// 授权失败 不能保存图片
that.setState({
showModal: true,
isCanToAlbum: false
})
}
})
}
})
}
// 保存图片到相册
downloadImgToAlbum () {
let that = this;
Taro.canvasToTempFilePath({
canvasId: 'posterCanvas',
success: (res) => {
let imgUrl = res.tempFilePath;
Taro.saveImageToPhotosAlbum({
filePath: imgUrl,
success (res) {
that.setState({
showModal: true,
isCanToAlbum: true
})
},
fail (res) {
that.setState({
showModal: true,
isCanToAlbum: false
})
}
})
}
});
}
// 关闭弹窗
handleCloseModal () {
this.setState({
showModal: false
})
}
// 复制文字
copyText () {
Taro.setClipboardData({data: this.state.shareText}).then(() => {
Taro.showToast({
title: '复制成功'
})
})
}
render() {
const {
headPhoto,
studentName,
showModal,
isCanToAlbum,
bgImg,
codeImg,
shareText
} = this.state;
return (
'collagePoster'
ref='collagePoster'
id='collagePosterId'
// onTouchStart={this.handleTimeStart}
onLongPress={this.handleSaveImg}
>
'main'>
'userInfoWrapper'>
{headPhoto && '头像' className='avator'> }
{studentName}
{codeImg && '二维码' className='codeImg'> }
bgImg} alt='背景图' className='bg_img'>
'space'>
{
showModal ?
'model-poster'>
'model-body'>
"modal-btn-close" onClick={this.handleCloseModal}>
'model-title'>{isCanToAlbum ? '- 海报已保存至相册 -' : '- 海报已生成,请截图保存 -'}
'model-tip'>复制文字一起分享,成功率翻3倍
'model-content'>{shareText}
: null
}
);
}
}
复制代码
坑
- 由接口拿到的图片地址是https的要经过wx.downloadFile转换一下。若不转换则在实际的手机上绘制的图是空白的无法正常实现绘制。
- 需要配置小程序域名信息中的downloadFile 合法域名。若不配置则在部分手机上打开调试工具的时候能正常运行,不打开调试工具的话不能正常绘图。