Taro框架使用canvas生成图片-附带源码和效果

简介: Taro框架使用canvas生成图片-附带源码和效果

为什么选择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


image.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日-附带源码注释和效果


image.png


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;


相关文章
uniapp根据官方文档上传图片的方法
uniapp根据官方文档上传图片的方法
|
2月前
|
编解码
四、ArkTS 常用组件-图片(Image)
ArkTS 常用组件-图片(Image)简介:Image 组件用于在应用中显示图片,支持本地和网络图片的加载,提供了多种参数类型如 string、Resource 和 media.PixelMap。常用属性包括图片尺寸设置(width()、height())、图片缩放模式(objectFit())及图片插值(interpolation()),确保图片在不同场景下的最佳显示效果。此外,Image 组件还支持图片资源的灵活引用方式,如通过 $r() 函数引用 resources 目录下的图片资源。
88 2
|
9月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的视频播放器附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的视频播放器附带文章和源代码设计说明文档ppt
69 3
|
8月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的纹理生成图片附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的纹理生成图片附带文章源码部署视频讲解等
61 2
|
8月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的壁纸网站附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的壁纸网站附带文章和源代码设计说明文档ppt
60 1
|
7月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的图片推荐系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的图片推荐系统附带文章源码部署视频讲解等
71 0
|
9月前
|
前端开发 JavaScript
使用canvas实现代码雨高级升阶版【附带源码和使用方法】
使用canvas实现代码雨高级升阶版【附带源码和使用方法】
73 0
|
前端开发
React基础语法03-引入本地图片和服务器图片的方法
React基础语法03-引入本地图片和服务器图片的方法
144 0
|
前端开发 API
前端(十五)——开源一个用react封装的图片预览组件
前端(十五)——开源一个用react封装的图片预览组件
278 0
|
前端开发 小程序 JavaScript
微信小程序 - DZMDrawingBoard - (Canvas封装的画板、手写签名、生成图片、保存相册...库)
微信小程序 - DZMDrawingBoard - (Canvas封装的画板、手写签名、生成图片、保存相册...库)
226 0

相关实验场景

更多