React+Taro+TS签字版功能

前言

目前项目要用到签字版这个功能,因为之前没有接触过,之前没怎么接触过小程序的画板,然后在网上也找了相对应的文章,最后整理了一下,感觉可能比较易懂一点吧,就想着发出来给大家伙做个参考

正文

实现的大概思路:你在画板上触摸的时候都有相对应的事件,这些事件都会返回相对的坐标,用画板的API把坐标连起来,渲染颜色,最后在渲染到画板上即可

技术

技术:React+Taro+TS+less

代码(解释都在代码里的注释)

tsx

tsx 复制代码
import { Component } from "react";
import Taro, { Config } from "@tarojs/taro";
import { View, Button, Canvas } from "@tarojs/components";
import "./index.less";

/** 创建画板的实例 */
let ctx: any = Taro.createCanvasContext("canvas", this);
/** 坐标 */
let startX = 0;
let startY = 0;
/** 宽高 */
let canvasW = 0;
let canvasH = 0;

export default class Signature extends Component<any, any> {
  state = {
    isPaint: false,
    tempFilePath: "",
    color: "#000",
    colorList: [
      { name: "red", value: "#ff1132" },
      { name: "green", value: "#4fcd56" },
      { name: "yellow", value: "#ffd767" },
      { name: "blue", value: "#0883d8" },
      { name: "black", value: "#000" },
      { name: "pink", value: "#ff6899" },
    ],
  };
  componentDidMount() {
    /** 获取画板的宽高,因为要获取DOM,要等到DOM渲染完成,所以给了个延迟定时器 */
    setTimeout(() => {
      this.getCanvasSize();
    }, 200);
    /** 初始化 */
    this.initCanvas();
  }
  /** 销毁阶段 */
  componentWillUnmount() {
    ctx = null;
  }
  /** 初始化 */
  initCanvas() {
    /** 获取画板 */
    ctx = Taro.createCanvasContext("canvas", this);
    /** 线条颜色 */
    ctx.setStrokeStyle(this.state.color);
    /** 线条粗细 */
    ctx.setLineWidth(4);
    /** 线条结束的样式 选择值可参考官方文档 */
    ctx.setLineCap("round");
    /** 线条交点的样式 选择值可参考官方文档 */
    ctx.setLineJoin("round");
  }
  /** 开始触摸 */
  canvasStart(e) {
    /** 获取坐标然后赋值 */
    startX = e.changedTouches[0].x;
    startY = e.changedTouches[0].y;
    /** 丢弃任何当前定义的路径并且开始一条新的路径。 它把当前的点设置为0.0,需要调用 fill 或者 stroke 才会使用路径进行填充或描边 */ 
    /** 具体可看官方文档或者百度 */
    ctx.beginPath();
  }
  /** 触摸中 */
  canvasMove(e) {
    /** 判断是否有值 */
    if (startX !== 0) {
      this.setState({
        isPaint: true,
      });
    }
    /** 获取坐标 */
    let x = e.changedTouches[0].x;
    let y = e.changedTouches[0].y;
    /** 把路径移动到画布中的指定点,不创建线条。用 stroke 方法来画线条*/ 
    /** 具体可看官方文档 */
    ctx.moveTo(startX, startY);
    /** 增加一个新点,然后创建一条从上次指定点到目标点的线。用 stroke 方法来画线条 */ 
    /** 创建新坐标到老坐标之间的线 */ 
    /** 具体可看官方文档 */
    ctx.lineTo(x, y);
    /** 画出当前路径的边框。默认颜色色为黑色 */ 
    /** 开始给线条颜色 */ 
    /** 具体可看官方文档 */
    ctx.stroke();
    /** 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 */ 
    /** 把之前的操作成果展示到画布上 */ 
    /** 具体可看官方文档 */
    ctx.draw(true);
    /** 赋值新的坐标 */
    startX = x;
    startY = y;
  }
  /** 触摸结束 */
  canvasEnd(e) {
    // console.log("结束");
  }
  // 清除
  clearDraw = () => {
    /** 赋值 */
    this.setState(
      {
        isPaint: false,
        tempFilePath: "",
      },
      () => {
        /** 清零 */ 
        startX = 0;
        startY = 0;
        /** 清除画布上在该矩形区域内的内容*/ 
        /** 参数 前两个时坐标,后两个时画布的宽高 */
        ctx.clearRect(0, 0, canvasW, canvasH);
        /** 把之前的操作成果展示到画布上 */
        ctx.draw(true);
      }
    );
  };
  /** 确认按钮 */
  createImg() {
    /** 判断画布是否为空 */
    if (!this.state.isPaint) {
      Taro.showToast({
        title: "签名内容不能为空!",
        icon: "none",
      });
      return false;
    }
    /** 生成图片 */
    /** 把当前画布指定区域的内容导出生成指定大小的图片。 在 draw() 回调里调用该方法才能保证图片导出成功。暂不支持离屏 canvas */ 
    /** 具体可查看官方文档 */
    Taro.canvasToTempFilePath({
      canvasId: "canvas",
      success: (res) => {
        console.log(res.tempFilePath);
        this.setState({
          tempFilePath: res.tempFilePath,
        });
        // this.uploadToAliyun(res.tempFilePath)
      },
      fail(err) {
        console.log(err);
      },
    });
  }
  // 获取 canvas 的尺寸(宽高)
  getCanvasSize() {
    /** createSelectorQuery具体可看官方文档 */
    const query = Taro.createSelectorQuery();
    query
        /** 要获取画板的id */
      .select("#canvas")
      .boundingClientRect((res) => {
        /** 赋值 */
        canvasW = res.width;
        canvasH = res.height;
      })
      .exec();
  }
  /** 选择颜色 */
  colorEvent(value) {
    console.log("传过来的颜色", value);
    /** 因为setState 时异步操作 所以必须等赋值完成之后在执行初始化 */
    this.setState(
      {
        color: value,
      },
      () => {
        this.initCanvas();
        console.log("赋值完的颜色", this.state.color);
      }
    );
  }
  render() {
    return (
      <View className='signature'>
        <View className='canvas-box'>
          <Canvas
            id='canvas'
            canvasId='canvas'
            className='canvas'
            disableScroll
            /** 触摸开始 */
            onTouchStart={this.canvasStart.bind(this)}
            /** 触摸中 */
            onTouchMove={this.canvasMove.bind(this)}
            /** 触摸结束 */
            onTouchEnd={this.canvasEnd.bind(this)}
            /** 触摸被中断 */
            onTouchCancel={this.canvasEnd.bind(this)}
          ></Canvas>
        </View>

        <View className='color'>
          {this.state.colorList.map((item) => {
            return (
              <View
                key={item.value}
                className='item'
                style={{ backgroundColor: item.value }}
                onClick={() => {
                  this.colorEvent(item.value);
                }}
              ></View>
            );
          })}
        </View>
        <View>
          当前颜色:
          <View
            style={{
              backgroundColor: this.state.color,
              width: "60px",
              height: "60px",
            }}
          ></View>
        </View>
        <View className='layout-flex buttons'>
          <Button className='cancel' onClick={this.clearDraw}>
            清除
          </Button>
          <Button className='confirm' onClick={this.createImg.bind(this)}>
            提交
          </Button>
        </View>

        <View>图片路径:</View>
        <View className='word-break'>{this.state.tempFilePath}</View>
      </View>
    );
  }
}

less

less 复制代码
.signature {
  height: 100%;
  width: 100%;
}
.canvas-box {
  border: 1Px solid #e4e4e4;
  margin-bottom: 30Px;
  position: relative;
}
.canvas {
  width: 100%;
  height: 345Px;
}

.buttons {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20Px;
  margin-top: 20Px;
}
Button {
  width: 320px;
  height: 80px;
  line-height: 80px;
  margin: 0;
  text-align: center;
  border: 1Px solid #F8AF18;
  font-size: 30px;
}
.color{
  width: 100%;
  display: flex;
  justify-content: space-evenly;
  .item{
    width: 60px;
    height: 60px;
  }
}
.confirm {
  color: #fff;
  background: #F8AF18;
}
.cancel {
  color: #F8AF18;
  background: #fff;
}
.word-break {
  word-break: break-all;
}
相关推荐
linda261812 小时前
本质上,Taro 是一个 跨平台编译框架
前端·taro
namehu3 天前
Taro 小程序 Video 组件 referrer-policy="origin" 属性失效排查记
前端·taro
vvilkim13 天前
Taro 状态管理全面指南:从本地状态到全局方案
taro
vvilkim16 天前
Taro:跨端开发的终极解决方案
taro
雨中的风铃子17 天前
taro小程序如何实现新用户引导功能?
小程序·taro
云边小卖铺.19 天前
React+Taro创建小程序
react.js·小程序·taro
沈春庭20 天前
【MoodVine】Taro+React对于大文件的轮询请求实现
前端·javascript·react.js·微信小程序·taro·promise
楽码25 天前
AI提问:进行深度交互
aigc·openai·taro
不爱吃糖的程序媛1 个月前
鸿蒙版Taro 搭建开发环境
华为·harmonyos·taro
PyAIGCMaster1 个月前
react+taro 开发第五个小程序,解决拼音的学习
react.js·小程序·taro