8
目前要开发一个小程序项目,领导要我上手直接就是Taro,语法基本是React的语法(可叹我是个vue爱好者,之前只是用过RN[0.43]版本,目前都0.59版本了),开发起来比原生的效率要高一点,Taro——一套遵循 React 语法规范的多端统一开发框架

为什么选择Taro

框架名称 github-stars UI框架 语法 研发团队
Taro 18k Taro-ui React语法 京东
uni-app 7k uni-app插件 Vue语法 DCloud
mpvue 17k mpvue-weui Vue语法 美团
Chameleon 12k Chameleon-ui 小程序语法 滴滴

从对比stars看Taro优势比较大,从社区群体上看uni-app在开发这块还是很有潜力的,毕竟一直都在更新中,并且已有现有的开发工具
这是掘进上对比的Taro和uni-app的文章,有对比目前所流行的框架支持度以及生态如何

遇到问题canvas画图,然后保存图片

canvasToTempFilePath: fail canvas is empty

点击canvas按钮我请求一张网络图片,一直抛这个异常,查阅文章,网上基本都是小程序生成图片,很少有关于Taro
clipboard.png

代码如下-采坑

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;

羊先生
1.9k 声望821 粉丝