• 前端小工具:CanvasDraw签名工具


    前端小工具:CanvasDraw签名工具

    移动端签名提示函

    1、安装

    npm install react-canvas-draw --save or yarn add react-canvas-draw

     支持导出图片,文件数据流,

    移动端横屏显示,

    用于react项目

    贴代码,大家都喜欢的。

    import { Modal } from 'antd-mobile';
    import PropTypes from 'prop-types';
    import React, { PureComponent } from 'react';
    import CanvasDraw from 'react-canvas-draw';
    import style from './style';
    class CanvasDraws extends PureComponent {
      constructor(props) {
        super(props);
        this.signatureRef = React.createRef();
        this.canvasRef = React.createRef();
        this.placeholderRef = React.createRef();
        this.state = {
          visible: false,
          imgUrl: '',
          // 是否已经签名
          isAutograph: false,
          // 比例是否旋转
          isScaleRotate: true,
          signatureWidth: 300,
          signatureHeight: 500,
        };
      }
      componentDidMount() {
        this.setWidthHeight();
        window.addEventListener('resize', () => {
          this.setWidthHeight();
          this.setPlaceholderView();
        });
      }
      // 设置宽高
      setWidthHeight = () => {
        const width = document.documentElement.clientWidth;
        const height = document.documentElement.clientHeight;
        // 如果宽度大于高度,则不旋转签名比例
        if (width > height) {
          this.setState({
            isScaleRotate: false,
            signatureWidth: width,
            signatureHeight: height - 64,
          });
        } else {
          this.setState({
            isScaleRotate: true,
            signatureWidth: width - 64,
            signatureHeight: height,
          });
        }
      };
      // 显示签名函
      showCanvasDraws = () => {
        this.setState({
          visible: true,
        });
      };
      // 关闭签名提示函
      handleCloseVisible = () => {
        this.setState({
          isAutograph: false,
          visible: false,
        });
      };
      handleClear = () => {
        this.setPlaceholderView();
        this.canvasRef.current.clear();
        this.setState({
          isAutograph: false,
        });
      };
      handleConfirm = async () => {
        const { onComplete } = this.props;
        // const imgUrl = this.canvasRef.current.canvas.drawing.toDataURL('image/png');
        this.canvasRef.current.canvas.drawing.toBlob(async blob => {
          const blobRotate = await this.rotateBlob(blob);
          const blobUrl = window.URL.createObjectURL(blobRotate);
          this.setState({
            imgUrl: blobUrl,
          });
          this.handleCloseVisible();
          onComplete(blobRotate);
        });
      };
      // 设置占位符显示隐藏功能
      setPlaceholderView(view = 'block') {
        if (this.placeholderRef.current && this.placeholderRef.current.style) {
          this.placeholderRef.current.style.display = view;
        }
      }
      handleIsSignState = () => {
        this.setPlaceholderView('none');
        this.setState({
          isAutograph: true,
        });
      };
      // 旋转图片
      rotateBlob = (data = '') => {
        const boldUrl = window.URL.createObjectURL(data);
        const { isScaleRotate } = this.state;
        //传入需要旋转的base64图片
        return new Promise(resolve => {
          const imgView = new Image();
          imgView.src = boldUrl;
          const canvas = document.createElement('canvas');
          const context = canvas.getContext('2d');
          const cutCoor = { sx: 0, sy: 0, ex: 0, ey: 0 };
          imgView.onload = () => {
            const imgW = imgView.width;
            const imgH = imgView.height;
            canvas.width = imgH;
            canvas.height = imgW;
            canvas.fillStyle = '#fff';
            let imgData = null;
            // 如果比例旋转变化,则旋转图片,否则默认返回
            if (isScaleRotate) {
              const size = imgH;
              cutCoor.sx = size;
              cutCoor.sy = size - imgW;
              cutCoor.ex = size + imgH;
              cutCoor.ey = size + imgW;
              context.translate(size, size);
              // 旋转图片
              context.rotate((Math.PI / 2) * 3);
              context.drawImage(imgView, 0, 0);
              imgData = context.getImageData(cutCoor.sx, cutCoor.sy, cutCoor.ex, cutCoor.ey);
            } else {
              context.drawImage(imgView, 0, 0);
              imgData = context.getImageData(0, 0, imgW, imgH);
            }
            for (let i = 0; i < imgData.data.length; i += 4) {
              // 当该像素是透明的,则设置成白色
              if (imgData.data[i + 3] === 0) {
                imgData.data[i] = 255;
                imgData.data[i + 1] = 255;
                imgData.data[i + 2] = 255;
                imgData.data[i + 3] = 255;
              }
            }
            context.putImageData(imgData, 0, 0);
            canvas.toBlob(blob => {
              resolve(blob);
            }, 'image/jpeg');
          };
        });
      };
      // 签名模板
      renderSignatureRegion() {
        const { visible, isScaleRotate, signatureWidth, signatureHeight, isAutograph } = this.state;
        return (
          <style.CanvasDraw>
            <div className={isScaleRotate ? 'rotate' : 'normal'}>
              <div
                className="signature-regin"
                ref={this.signatureRef}
                style={{  signatureWidth }}
              >
                {visible && (
                  <CanvasDraw
                    ref={this.canvasRef}
                    loadTimeOffset={5}
                    brushRadius={2}
                    lazyRadius={0}
                    brushColor="#444"
                    backgroundColor="#fff"
                    catenaryColor="#0a0302"
                    gridColor="transparent"
                    hideInterface
                    canvasWidth={signatureWidth}
                    canvasHeight={signatureHeight}
                    onChange={() => this.handleIsSignState()}
                  />
                )}
                <div className="signature-placeholder" ref={this.placeholderRef}>
                  <p>签名区域</p>
                </div>
                <button
                  aria-label="Close"
                  className="am-modal-close close-signature"
                  onClick={this.handleCloseVisible}
                ></button>
              </div>
              <div className="signature-btn">
                <div className="signature-tips">
                  <a>
                    请保持手机<b>横屏</b>,并按照从左到右的方向居中签名
                  </a>
                </div>
                <div className="signature-btn-container">
                  <button onClick={() => this.handleClear()} className="clear-signature">
                    清除签名
                  </button>
                  <button
                    onClick={() => this.handleConfirm()}
                    className={isAutograph ? 'submit-signature' : 'submit-signature disabled'}
                    disabled={!isAutograph}
                  >
                    保存签名
                  </button>
                </div>
              </div>
            </div>
          </style.CanvasDraw>
        );
      }
      render() {
        const { visible, imgUrl } = this.state;
        const { agreedraw } = this.props;
        return (
          <style.Signature>
            <div className="signature default">
              <div onClick={this.showCanvasDraws}>
                <div className="signature-block">
                  {agreedraw ? (
                    <img className="preview" src={imgUrl}></img>
                  ) : (
                    <div className="placeholder">
                      <p>点击此处签名</p>
                    </div>
                  )}
                </div>
              </div>
              {/* 签名区域 */}
              <Modal popup className="signature-region-wrap" animationType="slide-up" visible={visible}>
                {this.renderSignatureRegion()}
              </Modal>
            </div>
          </style.Signature>
        );
      }
    }
    // 类型检查
    CanvasDraws.propTypes = {
      agreedraw: PropTypes.bool,
      onComplete: PropTypes.func,
    };
    // 默认值
    CanvasDraws.defaultProps = {
      // 是否签名
      agreedraw: false,
      // 签名完成
      onComplete: () => {},
    };
    
    export default CanvasDraws;

    css:只能参考哇

    import { pxToRem as rem } from 'lib/style.lib';
    import styled from 'styled-components';
    
    export default {
      Signature: styled.div`
        .signature {
          position: relative;
          border-radius: 2px;
          text-align: center;
          height: ${rem(276)};
    
          background: #fff;
          display: flex;
          align-items: center;
          justify-content: center;
          i &.default {
            border-radius: ${rem(4)};
            box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.05);
          }
          &.default {
            background: #eaebee;
          }
          &.result {
            .preview {
              max-height: ${rem(280)};
            }
            /* &:after {
              @include border2(full, #e0e0e0, rem(4px));
            } */
          }
          .signature-block {
             100%;
          }
          .preview {
            display: block;
             100%;
            height: ${rem(276)};
            box-sizing: border-box;
            border: 1px solid #eaebee;
          }
          .placeholder {
            color: #d1d1d1;
            i {
              font-size: ${rem(38)};
              svg {
                display: block;
                margin: 0 auto;
              }
            }
            p {
              font-size: ${rem(44)};
              line-height: 1.1;
              color: #949ca8;
            }
          }
          .tip-signature {
            line-height: 1.1;
            font-size: ${rem(28)};
          }
        }
      `,
      CanvasDraw: styled.div`
        // 弹出框
        .signature-regin {
          position: relative;
          left: 0;
        }
        .close-signature {
          position: absolute;
          right: 15px;
          top: 0;
          padding: 8px 0;
           30px;
          height: 30px;
          color: #999;
          font-size: ${rem(40)};
    
          .am-modal-close-x {
            color: #999 !important;
            font-size: 18px !important;
            &:after {
              display: none !important;
            }
            svg {
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
            }
          }
        }
    
        .am-modal-close {
          top: 15px;
        }
        .signature-placeholder {
          position: absolute;
          top: 50%;
          left: -24px;
           100%;
          transform: translateY(-55%);
          pointer-events: none;
          i {
            color: #d1d1d1;
            font-size: ${rem(56)};
          }
          p {
            margin-top: 5px;
            font-size: ${rem(140)};
            color: #e8eaed;
          }
        }
    
        .signature-btn {
          display: flex;
          background: rgba(13, 110, 255, 0.1);
          padding: 12px 32px 12px 24px;
          justify-content: space-between;
          align-items: center;
    
          a {
            font-size: 15px;
            color: #1272ff;
          }
    
          button {
             80px;
            height: 38px;
            border: 1px solid rgba(13, 110, 255);
            border-radius: 19px;
          }
          button:nth-of-type(1) {
            background-color: rgba(90, 154, 255, 0.1);
            color: #1272ff;
            margin-right: 16px;
          }
          button:nth-of-type(2) {
            background-color: #1272ff;
            color: #fff;
            &.disabled {
              background-color: #93afd8;
            }
          }
        }
        .rotate {
          .signature-regin {
            left: 64px;
          }
          .signature-placeholder {
            transform: translateY(-55%) rotate(90deg);
          }
          .close-signature {
            top: auto !important;
            bottom: 15px !important;
          }
          .signature-btn {
            position: absolute;
            left: 0;
            top: 0;
             64px;
            height: 100%;
            padding: 15px 10px;
            flex-direction: column;
            background: rgba(13, 110, 255, 0.1);
    
            .signature-tips {
              display: flex;
              transform: rotate(90deg) translateX(50%);
               500px;
            }
    
            .signature-btn-container {
              padding-right: ${rem(60)};
              display: flex;
              transform: rotate(90deg) translateX(-50%);
              button {
                position: relative;
              }
            }
          }
        }
      `,
    };

    我没有才华,只能是贴代码了。
    大家加油了。

  • 相关阅读:
    MySQL 数据实时同步到 Elasticsearch 的技术方案选型和思考
    编写高质量可维护的代码之优化逻辑判断
    Java8 lambda表达式常见用法
    jar安装到maven本地仓库
    微信小程序获取用户手机号
    spring aop 、Redis实现拦截重复操作
    redis自定义RedisCacheManager
    locust做并发测试实战
    几个绕过短信验证码限制的漏洞挖掘
    通达OA任意用户登录和后台GetShell漏洞复现
  • 原文地址:https://www.cnblogs.com/bore/p/16107213.html
Copyright © 2020-2023  润新知