基于 Taro 框架的微信小程序 canvas 绘图海报组件

这篇具有很好参考价值的文章主要介绍了基于 Taro 框架的微信小程序 canvas 绘图海报组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

项目需要保存收款码,效果如图:

taro小程序生成海报,微信小程序,taro,小程序

(此文仅代表个人日常工作记录,能力有限描述并不全面)

1.安装 npm i taro-plugin-canvas -S --production(taro-plugin-canvas 是基于 Taro 框架的微信小程序 canvas 绘图组件,封装了常用的操作,通过配置的方式生成分享图片)

 2.引入:import { TaroCanvasDrawer } from "../../customComponents/taro-plugin-canvas" (封装taro-plugin-canvas的tsx文件)

import Taro, { Component, CanvasContext } from '@tarojs/taro';
import PropTypes from 'prop-types';
import { Canvas } from '@tarojs/components';
import { randomString, getHeight, downloadImageAndInfo } from './utils/tools';
import {
  drawImage,
  drawText,
  drawBlock,
  drawLine,
} from './utils/draw';
import { IConfig, IIMage } from './types';
import './index.css';

interface ICanvasDrawerProps {
  config: IConfig;
  onCreateSuccess: (res: any) => void;
  onCreateFail: (err: Error) => void;
}

interface ICanvasDrawerState {
  pxWidth: number;
  pxHeight: number;
  debug: boolean;
  factor: number;
  pixelRatio: number;
}


let count = 1;
export default class CanvasDrawer extends Component<ICanvasDrawerProps, ICanvasDrawerState> {
  cache: any;
  drawArr: any[];
  canvasId: string;
  ctx: CanvasContext | null;

  static propTypes = {
    config: PropTypes.object.isRequired,
    onCreateSuccess: PropTypes.func.isRequired,
    onCreateFail: PropTypes.func.isRequired,
  };

  static defaultProps = {};

  constructor(props) {
    super(props);
    this.state = {
      pxWidth: 0,
      pxHeight: 0,
      debug: false,
      factor: 0,
      pixelRatio: 1,
    }
    this.canvasId = randomString(10);
    this.ctx = null;
    this.cache = {};
    this.drawArr = [];
  }

  componentWillMount() {
    const { config } = this.props;
    const height = getHeight(config);
    this.initCanvas(config.width, height, config.debug);
  }

  componentDidMount() {
    const sysInfo = Taro.getSystemInfoSync();
    const screenWidth = sysInfo.screenWidth;
    this.setState({
      factor: screenWidth / 750
    })
    this.onCreate();
  }

  componentWillUnmount() { }

  /**
   * @description rpx => px 基础方法
   * @param { number } rpx - 需要转换的数值
   * @param { boolean} int - 是否为 int
   * @param { number } [factor = this.state.factor] - 转化因子
   * @returns { number }
   */
  toPx = (rpx: number, int: boolean = false, factor: number = this.state.factor) => {
    if (int) {
      return Math.ceil(rpx * factor * this.state.pixelRatio);
    }
    return rpx * factor * this.state.pixelRatio;
  }
  /**
   * @description px => rpx
   * @param { number } px - 需要转换的数值
   * @param { boolean} int - 是否为 int
   * @param { number } [factor = this.state.factor] - 转化因子
   * @returns { number }
   */
  toRpx = (px: number, int: boolean = false, factor: number = this.state.factor) => {
    if (int) {
      return Math.ceil(px / factor);
    }
    return px / factor;
  }

  /**
   * @description 下载图片并获取图片信息
   * @param  {} image
   * @param  {} index
   */
  _downloadImageAndInfo = (image: IIMage, index: number, pixelRatio: number) => {
    return new Promise<any>((resolve, reject) => {
      downloadImageAndInfo(image, index, this.toRpx, pixelRatio)
        .then(
          (result) => {
            this.drawArr.push(result);
            resolve(result);
          }
        )
        .catch(err => {
          console.log(err);
          reject(err)
        });
    })
  }
  /**
   * @param  {} images=[]
   */
  downloadResource = ({ images = [], pixelRatio = 1 }: { images: IIMage[], pixelRatio: number }) => {
    const drawList: any[] = [];
    let imagesTemp = images;

    imagesTemp.forEach((image, index) => drawList.push(this._downloadImageAndInfo(image, index, pixelRatio)));

    return Promise.all(drawList);
  }

  /**
   * @param
   */
  downloadResourceTransit = () => {
    const { config } = this.props;
    return new Promise<any>((resolve, reject) => {
      if (config.images && config.images.length > 0) {
        this.downloadResource({
          images: config.images,
          pixelRatio: config.pixelRatio || 1,
        })
          .then(() => {
            resolve();
          })
          .catch((e) => {
            // console.log(e);
            reject(e)
          });
      } else {
        setTimeout(() => {
          resolve(1);
        }, 500)
      }
    })
  }

  initCanvas = (w, h, debug) => {
    return new Promise<void>((resolve) => {
      this.setState({
        pxWidth: this.toPx(w),
        pxHeight: this.toPx(h),
        debug,
      }, resolve);
    });
  }

  onCreate = () => {
    const { onCreateFail, config } = this.props;
    if (config['hide-loading'] === false) {
      Taro.showLoading({ mask: true, title: '生成中...' });
    }
    return this.downloadResourceTransit()
      .then(() => {
        this.create(config);
      })
      .catch((err) => {
        config['hide-loading'] && Taro.hideLoading();
        Taro.showToast({ icon: 'none', title: err.errMsg || '下载图片失败' });
        console.error(err);
        if (!onCreateFail) {
          console.warn('您必须实现 taro-plugin-canvas 组件的 onCreateFail 方法,详见文档 https://github.com/chuyun/taro-plugin-canvas#fail');
        }
        onCreateFail && onCreateFail(err);
      })
  }

  create = (config) => {
    this.ctx = Taro.createCanvasContext(this.canvasId, this.$scope);
    const height = getHeight(config);
    // 设置 pixelRatio
    this.setState({
      pixelRatio: config.pixelRatio || 1,
    }, () => {
      this.initCanvas(config.width, height, config.debug)
        .then(() => {
          // 设置画布底色
          if (config.backgroundColor) {
            this.ctx!.save();
            this.ctx!.setFillStyle(config.backgroundColor);
            this.ctx!.fillRect(0, 0, this.toPx(config.width), this.toPx(height));
            this.ctx!.restore();
          }
          const {
            texts = [],
            // images = [],
            blocks = [],
            lines = [],
          } = config;
          const queue = this.drawArr
            .concat(texts.map((item) => {
              item.type = 'text';
              item.zIndex = item.zIndex || 0;
              return item;
            }))
            .concat(blocks.map((item) => {
              item.type = 'block';
              item.zIndex = item.zIndex || 0;
              return item;
            }))
            .concat(lines.map((item) => {
              item.type = 'line';
              item.zIndex = item.zIndex || 0;
              return item;
            }));
          // 按照顺序排序
          queue.sort((a, b) => a.zIndex - b.zIndex);

          queue.forEach((item) => {
            let drawOptions = {
              ctx: (this.ctx as CanvasContext),
              toPx: this.toPx,
              toRpx: this.toRpx,
            }
            if (item.type === 'image') {
              if (drawOptions.ctx !== null) {
                drawImage(item, drawOptions);
              }
            } else if (item.type === 'text') {
              if (drawOptions.ctx !== null) {
                drawText(item, drawOptions)
              }
            } else if (item.type === 'block') {
              if (drawOptions.ctx !== null) {
                drawBlock(item, drawOptions)
              }

            } else if (item.type === 'line') {
              if (drawOptions.ctx !== null) {
                drawLine(item, drawOptions)
              }
            }
          });

          const res = Taro.getSystemInfoSync();
          const platform = res.platform;
          let time = 0;
          if (platform === 'android') {
            // 在安卓平台,经测试发现如果海报过于复杂在转换时需要做延时,要不然样式会错乱
            time = 300;
          }
          this.ctx!.draw(false, () => {
            setTimeout(() => {
              this.getTempFile(null);
            }, time);
          });
        })
        .catch((err) => {
          Taro.showToast({ icon: 'none', title: err.errMsg || '生成失败' });
          console.error(err);
        });
    });

  }

  getTempFile = (otherOptions) => {
    const { onCreateSuccess, onCreateFail } = this.props;
    Taro.canvasToTempFilePath({
      canvasId: this.canvasId,
      success: (result) => {
        if (!onCreateSuccess) {
          console.warn('您必须实现 taro-plugin-canvas 组件的 onCreateSuccess 方法,详见文档 https://github.com/chuyun/taro-plugin-canvas#success');
        }
        onCreateSuccess && onCreateSuccess(result);
      },
      fail: (error) => {
        const { errMsg } = error;
        console.log(errMsg)
        if (errMsg === 'canvasToTempFilePath:fail:create bitmap failed') {
          count += 1;
          if (count <= 3) {
            this.getTempFile(otherOptions);
          } else {
            if (!onCreateFail) {
              console.warn('您必须实现 taro-plugin-canvas 组件的 onCreateFail 方法,详见文档 https://github.com/chuyun/taro-plugin-canvas#fail');
            }
            onCreateFail && onCreateFail(error);
          }
        }
      },
    }, this.$scope);
  }

  render() {
    const { pxWidth, pxHeight, debug } = this.state;
    if (pxWidth && pxHeight) {
      return (
        <Canvas
          canvasId={this.canvasId}
          style={`width:${pxWidth}px; height:${pxHeight}px;`}
          className={`${debug ? 'debug' : 'pro'} canvas`}
        />
      );
    }
    return null;
  }
}

 3.使用:

  // 绘制成功回调函数 (必须实现)=> 接收绘制结果、重置 TaroCanvasDrawer 状态
  onCreateSuccess = result => {
    console.log(result, "result");
    const { tempFilePath, errMsg } = result;
    Taro.hideLoading();
    if (errMsg === "canvasToTempFilePath:ok") {
      this.setState(
        {
          shareImages: tempFilePath,
          // 重置 TaroCanvasDrawer 状态,方便下一次调用
          canvasStatus: false,
          canvasConfig: null
        },
        () => {
          this.saveToAlbum();
        }
      );
    } else {
      // 重置 TaroCanvasDrawer 状态,方便下一次调用
      this.setState({
        canvasStatus: false,
        canvasConfig: null
      });
      Taro.showToast({ icon: "none", title: errMsg || "出现错误" });
      console.log(errMsg);
    }
  };

  // 绘制失败回调函数 (必须实现)=> 接收绘制错误信息、重置 TaroCanvasDrawer 状态
  onCreateFail = error => {
    Taro.hideLoading();
    // 重置 TaroCanvasDrawer 状态,方便下一次调用
    this.setState({
      canvasStatus: false,
      canvasConfig: null
    });
    console.log(error);
  };

    const canvasConfig = {
      width: 600,
      height: 860,
      backgroundColor: "#fff",
      debug: false,
      blocks: [{}],
      images: [
        {
          x: 0,
          y: 0,
          width: 600,
          height: 860,
          paddingLeft: 0,
          paddingRight: 0,
          borderWidth: 0,
          url: "图片链接",
          borderRadius: 10,
        },
        {
          x: 100,
          y: 160,
          width: 400,
          height: 400,
          url: code, //二维码图片 
          zIndex: 9999,
        }
      ],
      texts: [
        {
          x: 300,
          y: 90,
          text: `使用线下扫码支付`,
          fontSize: 48,
          color: "#fff",
          baseLine: "middle",
          lineHeight: 38,
          textAlign:'center',
          fontWeight:'bold',
          lineNum: 1,
          // width: 400,
          letterSpacing: 10,
          zIndex: 999
        },
      ]
    };

 <TaroCanvasDrawer
    config={canvasConfig} // 绘制配置
    onCreateSuccess={this.onCreateSuccess} // 绘制成功回调
    onCreateFail={this.onCreateFail} // 绘制失败回调
  />

4.翻阅基于 Taro 框架的微信小程序 canvas 绘图组件-面圈网 非常全面文章来源地址https://www.toymoban.com/news/detail-810439.html

到了这里,关于基于 Taro 框架的微信小程序 canvas 绘图海报组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【小程序图片水印】微信小程序图片加水印功能 canvas绘图

    感觉有用的话,可以打赏一把么?一毛不嫌少,十块不嫌多 更多详细代码请关注公众号索取(备注:公众号):

    2024年04月29日
    浏览(33)
  • 微信小程序--Taro框架实际开发中的问题汇总

    前言:微信小程序相信大家都不陌生,目前小程序开发主流的框架有微信 原生开发 , uni-app ,以及今天的重点 Taro 。编者自身小程序开发经验也不多,仅针对自身在小程序开发中的问题做一次汇总,望更多像我一样的小白少走 一点弯路。 注意:本篇博客中小程序开发基于

    2024年02月16日
    浏览(38)
  • taro+vue3 搭建一套框架,适用于微信小程序和H5

    安装 Taro。可以在终端输入以下命令进行安装: 创建项目。使用以下命令创建 Taro+Vue3 项目: 其中,myApp 是项目名称。 进入项目并启动。使用以下命令进入项目并启动: 注意,需要先进入对应的目录再启动。 编写代码。在 src 目录下编写代码,可以像使用 Vue 开发 Web 应用程

    2024年02月10日
    浏览(43)
  • 基于SSM的微信小程序农业信息服务平台

    近年来,随着农业经济的不断发展,通过农贸市场等方式交易农产品的模式已经无法满足农业经济发展的需要,为了提高农产品的交易率,降低经营成本,利用计算机技术,构建一个基于SSM的微信小程序农业信息服务平台,方便农户、采购者等用户之间农产品交易以及农业相

    2024年01月22日
    浏览(37)
  • 基于uniapp的微信小程序如何刷新页面(绝对有效)

    由于我的毕业设计就是用uniapp写的微信小程序。 碰到了这样的问题,查了许多资料,最终找到了这样的方法。 现在写出来给大家节约时间和精力。 就是用uni.redirectTo 实现页面跳转从而实现页面刷新 注意:这不能刷新tabBar页面 不要傻乎乎的用在tabBar上 跳转到tabBar页面只能使

    2024年02月11日
    浏览(34)
  • 基于SpringBoot+Vue+uniapp微信小程序的微信小程序书店的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月17日
    浏览(45)
  • 基于uniapp的微信小程序学生信息选课系统[源码+远程+答疑

     网站介绍:✌本网站专注专注于计算机技术领域的毕业设计辅导,提供JAVA、微信小程序、Python、APP、PHP、微服务、NET等毕设项目的定制和成品服务!✌         ✌IT实战营站长,拥有10年软件相关系统架构及教学经验,Java、大数据培训讲师,曾任公司技术总监;其与团队

    2024年01月24日
    浏览(55)
  • 基于网易云音乐API的微信小程序——zwhdlb的音乐平台

    最近在学习小程序的开发的过程中,临时想写一个音乐小程序,看到了网易云 提供了后台api程序,这方便我们直接进行音乐小程序的开发不用再从后端开始开发,网易云音乐平时也经常在用,因此想记录一下学习过程 开发工具:微信开发者工具 界面UI组件库用到的是ColorUI

    2023年04月27日
    浏览(41)
  • 基于uni-app的微信小程序Map事件穿透处理

    业务需要在微信小程序中使用地图组件,上面需要有点位及点位的交互,同时地图上也会有一些悬浮的按钮、弹窗之类的。在微信小程序2.8.x的版本之后,地图这种原生组件是支持同层渲染的,也就是可以通过样式控制层级。在开发者工具中表现正常,但是上了真机后会发现点

    2024年02月11日
    浏览(39)
  • 基于微信小程序的微信社团小程序的设计与实现(源码+lw+部署文档+讲解等)

    🌞 博主介绍 :✌全网粉丝15W+,CSDN特邀作者、211毕业、高级全栈开发程序员、大厂多年工作经验、码云/掘金/华为云/阿里云/InfoQ/StackOverflow/github等平台优质作者、专注于Java、小程序技术领域和毕业项目实战,以及程序定制化开发、全栈讲解、就业辅导✌🌞 👇🏻 精彩专栏

    2024年02月22日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包