为什么选择Taro
从对比stars看Taro优势比较大,从社区群体上看uni-app在开发这块还是很有潜力的,毕竟一直都在更新中,并且已有现有的开发工具
这是掘进上对比的Taro和uni-app的文章,有对比目前所流行的框架支持度以及生态如何
遇到问题canvas画图,然后保存图片
canvasToTempFilePath: fail canvas is empty
点击canvas按钮我请求一张网络图片,一直抛这个异常,查阅文章,网上基本都是小程序生成图片,很少有关于Taro
代码如下-采坑
wxDrawImage(){ let that = this; var canvas = Taro.createCanvasContext('shareCanvas',this) canvas.drawImage('https://www.vipbic.com/template/default/public/img/logo.png',0,0,this.state.canvasWidth,this.state.canvasWidth * 1.5) canvas.setTextAlign('center') canvas.setFillStyle('#ffffff') canvas.setFontSize(12) canvas.fillText("生成的文字", this.state.canvasWidth * 0.5, this.state.canvasWidth * 1.26) canvas.stroke(); canvas.draw(true,()=>{ Taro.canvasToTempFilePath({ canvasId: 'shareCanvas', success: function(res) { console.log(res) Taro.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function(res) { console.log(res) }, fail: function(err) { console.log(err) } },that) } },that) }) }
如需正确使用,需将Taro.createCanvasContext('shareCanvas',this)
替换Taro.createCanvasContext('shareCanvas',this.$scope)
,小编我也是Google,百度搜了不少文章才知道,也许是我对react理解不够深入吧
此次是后更新时间2019年7月13日-附带源码注释和效果
TestCanvas.js 组件 import Taro, { Component } from '@tarojs/taro' import { View, Canvas, Image } from '@tarojs/components' import { AtButton } from 'taro-ui' import base64src from '../base64src' import shareImg from '../logo.png' import './index.scss' let baseUrlCode = ''; export default class TestCanvas extends Component { constructor(props){ super(props); this.state = { canvasWidth:560, canvasHeight:978, bgImgPath:'', posterImage:'' } } componentWillMount(){ // base64 需要转换 let str3 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKQAAACkAQMAAAAjexcCAAAABlBMVEX///8AAABVwtN+AAABc0lEQVRIidWWQZbDMAhDuYHuf0tuwCCBpzNrtGlem8Q/fXUsBDji246qygxkX/vES1pofzNRxZsGKeKgPVmjHifn4dBHuQ5ELyOtFLqr7Cc+qgd9QmvE1YSHKpqf42+MT3TMBiiY+G/BCy15oyIQZMC8w5miNKD0lAn8gYMWBZolTLbAQzuEoO5aQ/tvHHOnfHFQE3DiNYqBUp1cx9GG9avZkRKVRGmK9cmdMvXaGwxnKSnLREOGZq1rIz517pQzMbUVRjx1DJQvPu77zGWgbRAK3zOx4ve1PJTeoCqr1N7dKSdIjYN9LyYLDVSeo/tQW+otlDlYcl9iksZDlXrjviBfze60mHoqmqr5W/vONNT9hViP1tMGypakkKraz08MlAWe1p79z2umd/ocqDi+ZDFQ/n+bj9qzra797lS7Faj7yymzCgPVrk3tTuqM6h6q6smFaGvspFRJ2b39yUD5QJ+3zfRQRrO0H1YVjc8e8Ua/6/gBOpv1YBO8iNcAAAAASUVORK5CYII=' base64src(str3, res => { baseUrlCode = res }); Taro.getSystemInfo() .then(res => { this.setState({ // canvasWidth:res.windowWidth*2, // canvasHeight:res.windowHeight*2 }) }) } // 获取微信相册授权信息 getSetting(){ return new Promise((resolve,reject)=>{ Taro.getSetting() .then((res)=>{ if (!res.authSetting['scope.writePhotosAlbum']) { Taro.authorize({ scope:'scope.writePhotosAlbum', }) .then(res=>{ if(res.errMsg == 'authorize:ok'){ resolve(true) }else{ reject(false) } }) .catch(()=>{ reject(false) }) }else{ resolve(true) } }) .catch(()=>{ reject(false) }) }) } // 下载网络图片 downLoad(){ let that = this; wx.downloadFile({ url: 'https://bw-online-img.oss-cn-hangzhou.aliyuncs.com/miniprogram/home/06.png', success: function (res) { that.state.bgImgPath = res.tempFilePath; that.openShareImg(); } }) } // 绘制图片 wxDrawImage(callback){ const {canvasHeight} = this.state Taro.showLoading({ title: '海报生成中', mask: true }); const WIDTH = 560; var ctx = Taro.createCanvasContext('shareCanvas',this.$scope) ctx.fillStyle="#fff"; ctx.fillRect(0,0,WIDTH,canvasHeight); ctx.clearRect(0,0,0,0); Taro.getImageInfo({src:this.state.bgImgPath}) .then((res)=>{ // 获取图片的高度 const HEIGHT = res.height; const IMAHEWIDTH = res.width; ctx.drawImage(shareImg, (WIDTH-180)/2, -20, 180, 120); ctx.restore(); ctx.setFillStyle('#333333') // 颜色 ctx.setFontSize(26); let str1 = '限时特卖|03月2610:00-03月28日09:59'; let left1 = (WIDTH-(ctx.measureText(str1).width))/2 ctx.fillText(str1,left1,88+26); //字体加设计高度 ctx.fillStyle="#D8D8D8"; ctx.fillRect(0,152,WIDTH,560); ctx.clearRect(0,0,0,0); ctx.drawImage(this.state.bgImgPath, (WIDTH-IMAHEWIDTH)/2,152+(560-HEIGHT)/2, IMAHEWIDTH, HEIGHT); ctx.restore(); let str2 = '限时特卖限时特限时特卖限时特卖限时特卖'; let [contentLeng, contentArray, contentRows] = this.textByteLength(str2, 20); let hs = contentRows*38; for (let m = 0; m < contentArray.length; m++) { ctx.setFillStyle('#333333') ctx.setTextAlign('left'); ctx.font = 'normal bold 28px sans-serif'; ctx.fillText(contentArray[m],32,786+38*m); } // 图片转码 ctx.drawImage(baseUrlCode,WIDTH-140-32,754,140,140); ctx.restore(); ctx.setFillStyle('#333333') // 颜色 ctx.setFontSize(32); let str4 = '¥2999.00'; let left4 = ctx.measureText(str4).width ctx.font = 'normal bold 32px sans-serif' ctx.fillText(str4,32,798+hs) let str5 = '跨境商品'; let width5 = ctx.measureText(str5).width ctx.fillStyle='#FFDDDD'; ctx.fillRect(left4+32+16,766+hs,width5+20,38) ctx.setFillStyle('#E61717'); ctx.setFontSize(24) ctx.font = 'normal lighter 24px sans-serif' ctx.fillText(str5,left4+32+41,795+hs); ctx.setFillStyle('#999999') // 颜色 ctx.setFontSize(26); let str6 = '来自蓝鲸淘小店'; ctx.fillText(str6,32,canvasHeight-38); //字体加设计高度 ctx.setFillStyle('#999999') // 颜色 ctx.setFontSize(26); let str7 = '长按识别二维码'; let width7 = ctx.measureText(str7).width ctx.fillText(str7,WIDTH-width7-32,canvasHeight-38); //字体加设计高度 ctx.draw(true,()=>{ callback && callback() }) }) } // 授权提示 showModal(){ let that = this; Taro.showModal({ title: '授权提示', content: '打开保存图片权限', success (res) { if (res.confirm) { Taro.openSetting({ success (res) { if(res.authSetting['scope.writePhotosAlbum']){ // 调用画图 that.wxDrawImage(()=>{ that.saveImage() }) }else{ Taro.showToast({ title: '授权失败', icon: 'none' }); } }, fail(){ Taro.showToast({ title: '授权失败', icon: 'none' }); } }) } else if (res.cancel) { Taro.showToast({ title: '授权失败', icon: 'none' }); } } }) } // 打开分享 openShareImg(){ this.getSetting().then((res)=>{ if(!res){ this.showModal() }else{ this.wxDrawImage(()=>{ this.saveImage() }) } }).catch(()=>{ this.showModal() }) } openImage(){ } // 图片保存 saveImage(){ let that = this; const {canvasWidth, canvasHeight} = this.state Taro.canvasToTempFilePath({ width: canvasWidth, height: canvasHeight, destWidth: canvasWidth * 2, destHeight: canvasHeight * 2, x: 0, y: 0, canvasId: 'shareCanvas', success: function(res) { Taro.hideLoading(); that.setState({ posterImage: res.tempFilePath }) } },that.$scope) } /** * 生成海报获取文字 * @param string text 为传入的文本 * @param int num 为单行显示的字节长度 * @return array */ textByteLength (text, num){ let strLength = 0; let rows = 1; let str = 0; let arr = []; for (let j = 0; j < text.length; j++) { if (text.charCodeAt(j) > 255) { strLength += 2; if (strLength > rows * num) { strLength++; arr.push(text.slice(str, j)); str = j; rows++; } } else { strLength++; if (strLength > rows * num) { arr.push(text.slice(str, j)); str = j; rows++; } } } arr.push(text.slice(str, text.length)); return [strLength, arr, rows] // [处理文字的总字节长度,每行显示内容的数组,行数] } render () { const {canvasWidth, canvasHeight, posterImage} = this.state const style= { position: 'fixed', top: 0, left: '1000px' } return ( <View className='canvas'> <Canvas canvasId="shareCanvas" style={{width:canvasWidth+'px',height:canvasHeight+'px',...style}}></Canvas> <AtButton type='primary' circle onClick={this.downLoad.bind(this)}>canvas保存图片</AtButton> { posterImage ? (<Image className='img' src={posterImage} style={{width:canvasWidth/2+'px',height:canvasHeight/2+'px',backgroundColor:'#fff'}}></Image>) : ''} </View> ) } } test.js页面 import Taro, { Component } from '@tarojs/taro' import { View } from '@tarojs/components' import TextCanvas from './TestCanvas' class Test extends Component { componentDidMount() { } componentDidShow() { } async onPullDownRefresh() { } render () { return ( <View> <TextCanvas></TextCanvas> </View> ) } } export default Test 用到的base64src.js const fsm = wx.getFileSystemManager(); const FILE_BASE_NAME = 'tmp_base64src'; //自定义文件名 function base64src(base64data, cb) { const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || []; if (!format) { return (new Error('ERROR_BASE64SRC_PARSE')); } const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`; const buffer = wx.base64ToArrayBuffer(bodyData); fsm.writeFile({ filePath, data: buffer, encoding: 'binary', success() { cb(filePath); }, fail() { return (new Error('ERROR_BASE64SRC_WRITE')); }, }); }; export default base64src;