Advertisement

【taro react】---- 兼容微信小程序和H5的海报绘制插件

阅读量:

该海报绘制组件是一个基于React和Taro.js开发的跨环境(Web与微信)海报制作工具。其主要功能包括:
初始化与状态管理

  • 接收海报参数(如背景类型、尺寸、坐标等)
  • 初始化状态变量(如是否已开始绘制)
    创建画布
  • 在浏览器环境中获取并渲染canvas
  • 提供重复绘制机制以解决跨设备兼容性问题
    核心绘图功能
  • 绘制背景图像
  • 绘制矩形
  • 绘制文字
  • 处理圆角区域
  • 多种绘图样式(图片、文本)
    动态管理与导出
  • 实时更新进度
  • 提供下载图片功能
  • 导出生成海报至相册
    辅助功能
  • 图表统计分析
  • 日期时间格式化
  • 高亮显示未完成任务
    该组件通过Taro API实现跨环境适配,并支持复杂的绘图逻辑和动态管理机制。

1. 环境

  1. taro 3.2.12
  2. taro react 【Class Component】

2. 预览

  1. H5版式视觉预览测试
  2. 微信小程序版式预览测试

3. 实现

1. 创建标签

复制代码
    <View className="rui-canvas-container">
        <Canvas
          type="2d"
          className="canvas"
          style={`width:${shareInfo.width}px;height:${shareInfo.height}px;margin-bottom:20px;`}
          canvasId={canvasId}
          id={canvasId}
        ></Canvas>
    </View>

注意: type=“2d”!!! type=“2d”!!! type=“2d”!!!

2. 初始化海报的大小和绘制对象总数

复制代码
    let { shareInfo } = this.props;
    this.width = shareInfo.width || 450;
    this.height = shareInfo.height || 450;
    this.count = 0;
    this.total = this.countConfigList(shareInfo);

3. 计算需要绘制的list的总量

复制代码
    // 计算需要绘制的list的总量
    countConfigList(item, total = 1) {
      (function allTotal(item) {
    if (item && item.list && Array.isArray(item.list) && item.list.length) {
      total += item.list.length;
      item.list.forEach(allTotal);
    }
      })(item);
      return total;
    }

4. 获取对应的上下文对象

复制代码
    componentDidMount() {
    Taro.nextTick(() => {
      this.create();
    });
      }
      create(){
    	const dpr = (this.dpr = Taro.getSystemInfoSync().pixelRatio);
    if (Taro.getEnv() === Taro.ENV_TYPE.WEB) {
      const divElement = document.querySelector(`#${this.state.canvasId}`);
      divElement.style = `width:${this.width * dpr}px;height:${
        this.height * dpr
      }px;margin-bottom:20px;`;
      this.canvas = divElement.children[0];
      if (!this.canvas) {
        let timer = setTimeout(() => {
          this.create();
          clearTimeout(timer);
        });
        return false;
      }
      this.canvas.width = this.width * dpr;
      this.canvas.height = this.height * dpr;
      this.ctx = this.canvas.getContext("2d");
      this.ctx.scale(dpr, dpr);
      this.draw();
    } else {
      // 小程序环境
      const page = Taro.getCurrentInstance().page;
      Taro.createSelectorQuery()
        .in(page)
        .select(`#${this.state.canvasId}`)
        .fields({
          node: true,
          size: true,
        })
        .exec((res) => {
          if (!res[0] || !res[0].node) {
            let timer = setTimeout(() => {
              this.create();
              clearTimeout(timer);
            });
            return console.error("商品海报获取不到 canvas 标签!");
          }
          const { node, width, height } = res[0];
          const canvas = (this.canvas = node);
          this.ctx = canvas.getContext("2d");
          canvas.width = width * dpr;
          canvas.height = height * dpr;
          this.ctx.scale(dpr, dpr);
          this.draw();
        });
    }
      }

在操作过程中需要注意的是,有时会遇到无法正常获取canvas的情况。为了提高效率采用递归方法进行处理,并持续调用脚本进行尝试,直到成功加载canvas为止。同时,在成功加载画布后并设置画布的宽度等参数

5. 开始绘制

复制代码
    // 绘制海报
      draw() {
    let { shareInfo } = this.props;
    if (shareInfo.type == "img") {
      this.downloadImage(shareInfo.pic)
        .then((src) => {
          shareInfo.path = src;
          this.drawIMage(
            shareInfo,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, shareInfo)
          );
        })
        .catch((err) => {
          this.drawRect(
            shareInfo,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, shareInfo)
          );
        });
    } else {
      this.drawRect(
        shareInfo,
        this.ctx,
        this.hasDrawOverAndLoopDraw.bind(this, shareInfo)
      );
    }
      }

将海报的初始背景单独绘制!!!

6. 公用判断和继续绘制

复制代码
    // 提取公用判断和继续绘制
    hasDrawOverAndLoopDraw(item) {
    this.hasDrawOver();
    this.loopDraw(item.list);
      }

7. 判断是否绘制完成

复制代码
    // 判断是否绘制完成
    hasDrawOver() {
    this.count++;
    if (this.count >= this.total) {
      console.log("绘制完成");
      let timer = setTimeout(() => {
        const { onCreateSuccess = () => {} } = this.props;
        let imageBase64 = this.canvas.toDataURL("image/png", 1);
        onCreateSuccess({
          errMsg: "canvasToTempFilePath:ok",
          tempFilePath: imageBase64,
        });
        this.state.filePath = imageBase64;
        clearTimeout(timer);
      }, 30);
    }
      }

8. 循环绘制

复制代码
    // 循环绘制
      loopDraw(list) {
    if (list && Array.isArray(list)) {
      list.forEach((item) => {
        if (item.type == "img") {
          this.downloadImage(item.pic)
            .then((src) => {
              item.path = src;
              this.drawIMage(
                item,
                this.ctx,
                this.hasDrawOverAndLoopDraw.bind(this, item)
              );
            })
            .catch((err) => {
              this.drawRect(
                item,
                this.ctx,
                this.hasDrawOverAndLoopDraw.bind(this, item)
              );
            });
        } else if (item.type == "rect") {
          this.drawRect(
            item,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, item)
          );
        } else if (item.type == "text") {
          this.drawText(
            item,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, item)
          );
        }
      });
    }
      }

9. 下载图片

复制代码
    downloadImage(src) {
    return new Promise((resolve, reject) => {
      if (src.startsWith("#")) {
        resolve(src);
        return;
      }
      let img =
        Taro.getEnv() === Taro.ENV_TYPE.WEB
          ? new Image()
          : this.canvas.createImage();
      if (Taro.getEnv() === Taro.ENV_TYPE.WEB) {
        img.setAttribute("crossOrigin", "anonymous");
      }
      img.onload = function () {
        resolve(img);
      };
      img.onerror = () => reject(`下载图片失败 ${src}`);
      img.src = src;
    });
      }

10. 绘制图片

复制代码
    // 绘制图片方法
      drawIMage(opts, ctx, callback = () => {}) {
    if (opts.borderRadius) {
      this.drawBorderRadius(opts, ctx);
    }
    ctx.drawImage(opts.path, opts.x, opts.y, opts.width, opts.height);
    ctx.save(true);
    callback();
      }

11. 绘制矩形

复制代码
    // 绘制矩形
      drawRect(item, ctx, callback = () => {}) {
    let { color = "#fff", x = 0, y = 0, width = 100, height = 100 } = item;
    if (item.borderRadius) {
      this.drawBorderRadius(item, ctx);
    }
    ctx.fillStyle = color;
    ctx.fillRect(x, y, width, height);
    ctx.save(true);
    callback();
      }

12. 绘制文字

复制代码
    // 绘制文字
      drawText(opts, ctx, callback = () => {}) {
      let textArr = [];
      let len = opts.len;
      let rows = opts.rows ? opts.rows - 1 : 2;
      let clen = 2 * len;
    
      let h = (function (text) {
        let k = 0;
        let strLength = 0;
        for (let j = 0; j < text.length; j++) {
          if (text.charCodeAt(j) > 255) {
            strLength += 2;
          } else {
            strLength++;
          }
          if (
            strLength % clen == 0 ||
            (strLength % clen == clen - 1 &&
              text[j + 1] &&
              text.charCodeAt(j + 1) > 255)
          ) {
            textArr.push({
              title: text.substring(k, j),
              index: strLength,
            });
            k = j;
          }
          if (
            j == text.length - 1 &&
            textArr.indexOf(text.substring(k)) == -1
          ) {
            textArr.push({
              title: text.substring(k),
              index: strLength,
            });
          }
        }
        return {
          len: strLength,
          arr: textArr,
        };
      })(opts.title);
      let fontWeight = opts.fontWeight || "normal";
      ctx.textAlign = opts.textAlign || "left"; // 文字居中
      ctx.textBaseline = opts.textBaseline || "normal";
      ctx.fillStyle = opts.color || "#000"; // 文字颜色:黑色
      ctx.font = `${fontWeight} ` + opts.fontSize + "px Arial"; // 文字字号:20px
      for (let i = 0; i < h.arr.length; i++) {
        let title = h.arr[i].title;
        let index = h.arr[i].index;
        if (i < rows) {
          ctx.fillText(title, opts.x, opts.y + i * opts.space, opts.width);
        } else if (i == rows) {
          if (index % clen == 0 || index % clen > clen - 4) {
            ctx.fillText(
              title.substring(0, title.length - 3) + "...",
              opts.x,
              opts.y + i * opts.space,
              opts.width
            );
          } else if (index % clen < clen - 2) {
            ctx.fillText(title, opts.x, opts.y + i * opts.space, opts.width);
          }
        } else {
          break;
        }
      }
      ctx.save(true);
      callback();
      }

13. 绘制圆角

复制代码
    //绘制圆角
      drawBorderRadius(opts, ctx) {
    let css = {
      borderRadius: 0,
      width: 100,
      height: 100,
      x: 0,
      y: 0,
    };
    css = { ...css, ...opts };
    
    if (css.borderRadius && css.width && css.height) {
      const r = Math.min(css.borderRadius, css.width / 2, css.height / 2);
      const width = css.width;
      const height = css.height;
      const x = css.x;
      const y = css.y;
      ctx.beginPath();
      ctx.save(true);
      ctx.strokeStyle = "transparent";
      ctx.arc(r + x, r + y, r, 1 * Math.PI, 1.5 * Math.PI);
      ctx.lineTo(r + x, 0 + y);
      ctx.arc(width - r + x, r + y, r, 1.5 * Math.PI, 2 * Math.PI);
      ctx.lineTo(width + x, height - r + y);
      ctx.arc(width - r + x, height - r + y, r, 0, 0.5 * Math.PI);
      ctx.lineTo(width - r + x, height + y);
      ctx.arc(r + x, height - r + y, r, 0.5 * Math.PI, 1 * Math.PI);
      ctx.closePath();
      ctx.stroke();
      ctx.clip();
      ctx.save(true);
    }
      }

14. 完整代码

复制代码
    /* * @Descripttion:
     * @version: 1.0.0
     * @Author: Rattenking
     * @Date: 2022-05-23 09:26:40
     * @LastEditors: Rattenking
     */
    import React, { Component } from "react";
    import { Canvas, View } from "@tarojs/components";
    import Taro from "@tarojs/taro";
    import "./index.scss";
    
    export default class RuiSelfPoster extends Component {
      constructor(props) {
    super(props);
    let { shareInfo } = this.props;
    this.width = shareInfo.width || 450;
    this.height = shareInfo.height || 450;
    this.count = 0;
    this.total = this.countConfigList(shareInfo);
      }
      state = {
    canvasId: "ruiSelfPoster",
    scale: 750,
    width: 300,
    height: 300,
    filePath: "",
      };
    
      componentDidMount() {
    Taro.nextTick(() => {
      this.create();
    });
      }
      // 计算需要绘制的list的总量
      countConfigList(item, total = 1) {
    (function allTotal(item) {
      if (item && item.list && Array.isArray(item.list) && item.list.length) {
        total += item.list.length;
        item.list.forEach(allTotal);
      }
    })(item);
    return total;
      }
      // 获取对应的上下文对象
      create() {
    const dpr = (this.dpr = Taro.getSystemInfoSync().pixelRatio);
    if (Taro.getEnv() === Taro.ENV_TYPE.WEB) {
      const divElement = document.querySelector(`#${this.state.canvasId}`);
      divElement.style = `width:${this.width * dpr}px;height:${
        this.height * dpr
      }px;margin-bottom:20px;`;
      this.canvas = divElement.children[0];
      if (!this.canvas) {
        let timer = setTimeout(() => {
          this.create();
          clearTimeout(timer);
        });
        return false;
      }
      this.canvas.width = this.width * dpr;
      this.canvas.height = this.height * dpr;
      this.ctx = this.canvas.getContext("2d");
      this.ctx.scale(dpr, dpr);
      this.draw();
    } else {
      // 小程序环境
      const page = Taro.getCurrentInstance().page;
      Taro.createSelectorQuery()
        .in(page)
        .select(`#${this.state.canvasId}`)
        .fields({
          node: true,
          size: true,
        })
        .exec((res) => {
          if (!res[0] || !res[0].node) {
            let timer = setTimeout(() => {
              this.create();
              clearTimeout(timer);
            });
            return console.error("商品海报获取不到 canvas 标签!");
          }
          const { node, width, height } = res[0];
          const canvas = (this.canvas = node);
          this.ctx = canvas.getContext("2d");
          canvas.width = width * dpr;
          canvas.height = height * dpr;
          this.ctx.scale(dpr, dpr);
          this.draw();
        });
    }
      }
      // 绘制海报
      draw() {
    let { shareInfo } = this.props;
    if (shareInfo.type == "img") {
      this.downloadImage(shareInfo.pic)
        .then((src) => {
          shareInfo.path = src;
          this.drawIMage(
            shareInfo,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, shareInfo)
          );
        })
        .catch((err) => {
          this.drawRect(
            shareInfo,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, shareInfo)
          );
        });
    } else {
      this.drawRect(
        shareInfo,
        this.ctx,
        this.hasDrawOverAndLoopDraw.bind(this, shareInfo)
      );
    }
      }
      // 循环绘制
      loopDraw(list) {
    if (list && Array.isArray(list)) {
      list.forEach((item) => {
        if (item.type == "img") {
          this.downloadImage(item.pic)
            .then((src) => {
              item.path = src;
              this.drawIMage(
                item,
                this.ctx,
                this.hasDrawOverAndLoopDraw.bind(this, item)
              );
            })
            .catch((err) => {
              this.drawRect(
                item,
                this.ctx,
                this.hasDrawOverAndLoopDraw.bind(this, item)
              );
            });
        } else if (item.type == "rect") {
          this.drawRect(
            item,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, item)
          );
        } else if (item.type == "text") {
          this.drawText(
            item,
            this.ctx,
            this.hasDrawOverAndLoopDraw.bind(this, item)
          );
        }
      });
    }
      }
      // 提取公用判断和继续绘制
      hasDrawOverAndLoopDraw(item) {
    this.hasDrawOver();
    this.loopDraw(item.list);
      }
      // 判断是否绘制完成
      hasDrawOver() {
    this.count++;
    if (this.count >= this.total) {
      console.log("绘制完成");
      let timer = setTimeout(() => {
        const { onCreateSuccess = () => {}, onCreateFail = () => {} } =
          this.props;
        let imageBase64 = this.canvas.toDataURL("image/png", 1);
        onCreateSuccess({
          errMsg: "canvasToTempFilePath:ok",
          tempFilePath: imageBase64,
        });
        this.state.filePath = imageBase64;
        // this.getTempFile(null);
        clearTimeout(timer);
      }, 30);
    }
      }
      downloadImage(src) {
    return new Promise((resolve, reject) => {
      if (src.startsWith("#")) {
        resolve(src);
        return;
      }
      let img =
        Taro.getEnv() === Taro.ENV_TYPE.WEB
          ? new Image()
          : this.canvas.createImage();
      if (Taro.getEnv() === Taro.ENV_TYPE.WEB) {
        img.setAttribute("crossOrigin", "anonymous");
      }
      img.onload = function () {
        resolve(img);
      };
      img.onerror = () => reject(`下载图片失败 ${src}`);
      img.src = src;
    });
      }
      //保存海报
      saveToAlbum(filePath = "") {
    return new Promise((resolve, reject) => {
      if (!this.state.filePath || this.state.filePath == "") {
        this.$api._toast("正在生成海报,请稍后保存!");
        reject("正在生成海报,请稍后保存!");
        return;
      }
      filePath = filePath || this.state.filePath;
      let data = filePath.slice(22);
      const fs = wx.getFileSystemManager();
      const envPath = wx.env.USER_DATA_PATH;
      let path = `${envPath}/${+new Date()}.png`;
      fs.writeFile({
        filePath: path,
        data: data,
        encoding: "base64",
        success: (res) => {
          wx.saveImageToPhotosAlbum({
            filePath: path,
            success: function (res) {
              resolve(res);
              fs.readdir({
                dirPath: envPath,
                success(res) {
                  res.files.forEach((el) => {
                    // 遍历文件列表里的数据 (把不是.png后缀的过滤掉)
                    if (el.includes(".png")) {
                      // 删除存储的垃圾数据
                      // 这里注意。文件夹也要加上,如果直接文件名的话会无法找到这个文件
                      fs.unlink({
                        filePath: `${envPath}/${el}`,
                        fail(e) {
                          console.log("readdir文件删除失败:", e);
                        },
                      });
                    }
                  });
                },
              });
            },
            fail: function (err) {
              console.log(err);
              reject(err);
            },
          });
        },
        fail: (err) => {
          console.log(err);
          reject(err);
        },
      });
    });
      }
      render() {
    let { canvasId } = this.state;
    let { shareInfo } = this.props;
    return (
      <View className="rui-canvas-container">
        <Canvas
          type="2d"
          className="canvas"
          style={`width:${shareInfo.width}px;height:${shareInfo.height}px;margin-bottom:20px;`}
          canvasId={canvasId}
          id={canvasId}
        ></Canvas>
      </View>
    );
      }
      // 绘制矩形
      drawRect(item, ctx, callback = () => {}) {
    let { color = "#fff", x = 0, y = 0, width = 100, height = 100 } = item;
    if (item.borderRadius) {
      this.drawBorderRadius(item, ctx);
    }
    ctx.fillStyle = color;
    ctx.fillRect(x, y, width, height);
    ctx.save(true);
    callback();
      }
      //绘制圆角
      drawBorderRadius(opts, ctx) {
    let css = {
      borderRadius: 0,
      width: 100,
      height: 100,
      x: 0,
      y: 0,
    };
    css = { ...css, ...opts };
    
    if (css.borderRadius && css.width && css.height) {
      const r = Math.min(css.borderRadius, css.width / 2, css.height / 2);
      const width = css.width;
      const height = css.height;
      const x = css.x;
      const y = css.y;
      ctx.beginPath();
      ctx.save(true);
      ctx.strokeStyle = "transparent";
      ctx.arc(r + x, r + y, r, 1 * Math.PI, 1.5 * Math.PI);
      ctx.lineTo(r + x, 0 + y);
      ctx.arc(width - r + x, r + y, r, 1.5 * Math.PI, 2 * Math.PI);
      ctx.lineTo(width + x, height - r + y);
      ctx.arc(width - r + x, height - r + y, r, 0, 0.5 * Math.PI);
      ctx.lineTo(width - r + x, height + y);
      ctx.arc(r + x, height - r + y, r, 0.5 * Math.PI, 1 * Math.PI);
      ctx.closePath();
      ctx.stroke();
      ctx.clip();
      ctx.save(true);
    }
      }
      // 绘制图片方法
      drawIMage(opts, ctx, callback = () => {}) {
    if (opts.borderRadius) {
      this.drawBorderRadius(opts, ctx);
    }
    ctx.drawImage(opts.path, opts.x, opts.y, opts.width, opts.height);
    ctx.save(true);
    callback();
      }
      // 绘制文字
      drawText(opts, ctx, callback = () => {}) {
      let textArr = [];
      let len = opts.len;
      let rows = opts.rows ? opts.rows - 1 : 2;
      let clen = 2 * len;
    
      let h = (function (text) {
        let k = 0;
        let strLength = 0;
        for (let j = 0; j < text.length; j++) {
          if (text.charCodeAt(j) > 255) {
            strLength += 2;
          } else {
            strLength++;
          }
          if (
            strLength % clen == 0 ||
            (strLength % clen == clen - 1 &&
              text[j + 1] &&
              text.charCodeAt(j + 1) > 255)
          ) {
            textArr.push({
              title: text.substring(k, j),
              index: strLength,
            });
            k = j;
          }
          if (
            j == text.length - 1 &&
            textArr.indexOf(text.substring(k)) == -1
          ) {
            textArr.push({
              title: text.substring(k),
              index: strLength,
            });
          }
        }
        return {
          len: strLength,
          arr: textArr,
        };
      })(opts.title);
      let fontWeight = opts.fontWeight || "normal";
      ctx.textAlign = opts.textAlign || "left"; // 文字居中
      ctx.textBaseline = opts.textBaseline || "normal";
      ctx.fillStyle = opts.color || "#000"; // 文字颜色:黑色
      ctx.font = `${fontWeight} ` + opts.fontSize + "px Arial"; // 文字字号:20px
      for (let i = 0; i < h.arr.length; i++) {
        let title = h.arr[i].title;
        let index = h.arr[i].index;
        if (i < rows) {
          ctx.fillText(title, opts.x, opts.y + i * opts.space, opts.width);
        } else if (i == rows) {
          if (index % clen == 0 || index % clen > clen - 4) {
            ctx.fillText(
              title.substring(0, title.length - 3) + "...",
              opts.x,
              opts.y + i * opts.space,
              opts.width
            );
          } else if (index % clen < clen - 2) {
            ctx.fillText(title, opts.x, opts.y + i * opts.space, opts.width);
          }
        } else {
          break;
        }
      }
      ctx.save(true);
      callback();
      }
    }

4. 使用

1. 参数

1.1 参数说明
参数名 参数说明
shareInfo 海报的绘制参数
ref 获取组件,用于保存海报使用
onCreateSuccess 绘制完整回调函数
1.2 shareInfo说明
参数名 参数说明 参数值说明 默认值
type 海报背景的类型 ‘img’或者’rect’ ‘rect’
pic 海报背景图(只有type='img’有效) ‘’
width 海报的宽 Number 450
height 海报的高 Number 450
x 海报的背景起始坐标x Number 0
y 海报的背景起始坐标y Number 0
list 海报中的其他内容 Arrary []

1.3 list 说明

参数名 参数说明 参数值说明 默认值
value 海报内容以对象进行设置(数组的值可能是文字,图片和长方形) Object
1.3.1 rect 长方形参数说明
参数名 参数说明 参数值说明 默认值
type 绘制长方形 String ‘rect’
color 绘制长方形的颜色 String ‘#fff’
width 绘制长方形的宽 Number 100
height 绘制长方形的高 Number 100
x 绘制长方形的起始坐标x Number 0
y 绘制长方形的起始坐标y Number 0
borderRadius 绘制长方形的圆角 Number
list 绘制长方形中的内容(注意次参数和shareInfo的list参数等同解析) Array
1.3.2 img 长方形参数说明
参数名 参数说明 参数值说明 默认值
type 绘制图片 String ‘img’
pic 绘制图片的地址 String ‘’
width 绘制图片的宽 Number
height 绘制图片的高 Number
x 绘制图片的起始坐标x Number
y 绘制图片的起始坐标y Number
borderRadius 绘制图片的圆角 Number
list 绘制图片中的内容(注意次参数和shareInfo的list参数等同解析) Array
1.3.3 text 长方形参数说明
参数名 参数说明 参数值说明 默认值
type 绘制文字 String ‘text’
title 绘制的文字 String ‘’
x 绘制文字的起始坐标x Number
y 绘制文字的起始坐标y Number
len 绘制一行文字的个数 Number
rows 绘制文字的行数 Number 2
fontWeight 绘制文字的字体的粗细 String ‘normal’
textAlign 绘制文字横向显示位置 String ‘left’
textBaseline 绘制文字纵向显示位置 String ‘normal’
color 绘制文字的颜色 String ‘#000’
fontSize 绘制文字的字体大小 Number
space 绘制文字的行间距 Number
list 绘制图片中的内容(注意次参数和shareInfo的list参数等同解析) Array

2. 实例

2.1 引入组件
复制代码
    import RuiSelfPoster from "@com/RuiSelfPoster/RuiSelfPoster";
2.2 配置海报参数
复制代码
    let shareConfig = {
      type: "img",
      width: 520,
      height: 520,
      pic: bgUrl,
      x: 0,
      y: 0,
      list: [
        {
          type: "text",
          color: "#ffffff",
          rows: 2,
          len: 8,
          space: 45,
          fontSize: 34,
          textAlign: "center",
          textBaseline: "top",
          title: hallInfo.hallname,
          y: 36,
          x: 260,
          width: 280,
        },
        {
          type: "text",
          color: "#fdddd0",
          rows: 1,
          len: 14,
          space: 40,
          fontSize: 20,
          title: `联系电话: ${hallInfo.phone || hallInfo.linktel}`,
          y: 445,
          x: 260,
          width: 205,
        },
        {
          type: "text",
          color: "#fdddd0",
          rows: 1,
          len: 14,
          space: 40,
          fontSize: 20,
          title: `店铺ID: ${hallInfo.tyfoShopid}`,
          y: 445,
          x: 55,
          width: 205,
        },
        {
          type: "text",
          color: "#fdddd0",
          rows: 1,
          len: 23,
          space: 40,
          fontSize: 20,
          title: `店铺地址: ${hallInfo.halladdress}`,
          y: 475,
          x: 55,
          width: 410,
          list: [
            {
              type: "rect",
              color: "#fff",
              x: 113,
              y: 127,
              height: 294,
              width: 294,
              borderRadius: 24,
              list: [
                {
                  type: "img",
                  width: 288,
                  height: 288,
                  pic: code || this.$icon.staticicon.commonPosterQRCode,
                  x: 116,
                  y: 130,
                },
              ],
            },
          ],
        },
      ],
    };
    this.setState({
      canvasStatus: true,
      config: shareConfig,
    });
2.3 使用组件
复制代码
    constructor(props) {
    super(props);
    this.childRef = null;
      }
      render() {
    let { close = () => {} } = this.props;
    let { canvasStatus, filePath, config } = this.state;
    return (
      <React.Fragment>
        <RuiMask
          close={(e) => {
            close.call(this);
            this.setState({ canvasStatus: true, shareImage: "" });
          }}
          zIndex={10242}
        >
          <View>
            {/* 绘制完成图片显示 */}
            {filePath && !canvasStatus ? (
              <React.Fragment>
                <View className="rui-poster-content">
                  <Image className="rui-share-image" src={filePath} lazy-load />
                  <View className="rui-flex-cc">
                    {this.$api.hasWeapp() ? (
                      <View
                        className="rui-save-btn"
                        onClick={this.saveToAlbum.bind(this)}
                      >
                        保存到相册
                      </View>
                    ) : (
                      <View className="rui-save-btn rui-pr">
                        <Image src={filePath} className="rui-save-img"></Image>
                        <Text>长按保存图片</Text>
                      </View>
                    )}
                  </View>
                </View>
              </React.Fragment>
            ) : (
              <React.Fragment />
            )}
            {/* // 由于部分限制,目前组件通过状态的方式来动态加载 */}
            {canvasStatus ? (
              <View className="rui-poster-content">
                <RuiSelfPoster
                  className="rui-share-image"
                  ref={this.setChildRef}
                  onCreateSuccess={this.onCreateSuccess.bind(this)}
                  shareInfo={config}
                />
              </View>
            ) : (
              <React.Fragment />
            )}
          </View>
        </RuiMask>
      </React.Fragment>
    );
      }
      // 设置Ref
      setChildRef = (ref) => {
    if (ref) {
      this.childRef = ref;
    }
      };
2.4 海报绘制成功导出base64地址
复制代码
    onCreateSuccess(result) {
    const { tempFilePath, res, errMsg } = result;
    // console.log(tempFilePath);
    if (
      res === "canvasToTempFilePath:ok" ||
      errMsg === "canvasToTempFilePath:ok"
    ) {
      this.setState({ filePath: tempFilePath, canvasStatus: false });
    }
      }
2.5 保存海报
复制代码
    saveToAlbum() {
    let { close = () => {} } = this.props;
    this.childRef
      .saveToAlbum()
      .then((res) => {
        this.$api._toast("保存成功!");
        close.call(this);
        // this.$event.closeLayer.call(this, "isShowPoster");
      })
      .catch((err) => console.log(err));
      }

5. 总结

  1. 此处绘制海报没有对海报进行缩放处理,而是按照海报的原尺寸在屏幕之外进行的绘制,当绘制完成,导出base64地址,在img标签中显示图片!
  2. 此处保存海报使用的是保存base64的方式,保存图片,所以注意清空其他图片!
  3. 当使用圆角,是对当前进行裁剪,圆角区域外的其他绘制不在显示,因此使用次组件,需要先绘制圆角区域以外的内容,最后绘制圆角和圆角内的部分;

作为返回总目录的传送门,请您访问此链接:前往总目录

全部评论 (0)

还没有任何评论哟~