uniapp,小程序,实现签名功能

1.需求介绍
有的时候开发uniapp或者小程序需要实现用户签名的一个功能,但是网上又不想购买插件,自己也不想写,这个时候就可以CV开发了
2.目标明确
我们需要把用户签名后转成base64码调取后端给的接口,我这里是以字符串(String)的形式,具体格式和后端的沟通
**3.注意事项 ** 项目一定要安装scss不然指定报错 不会的去百度这里安装太简单了就省了

  • .
    页面签名展示的效果:
  • unity android 微信签名 uniapp应用签名_vue.js

  • 第一步.新建components 新建->bobo-message -> 新建 -> bobo-message.vue 如图所示
  • unity android 微信签名 uniapp应用签名_小程序_02

  • bobo-message.vue 代码
<template>
	<view :style="{zIndex: zIndex}" class="container">
		<block v-for="message in messageQueue" :key="message.id">
			<view :class="[message.animation, backgroundClass(message)]" class="message">
				<text v-if="message.type === 'info'" class="bm-icon info"></text>
				<text v-if="message.type === 'success'" class="bm-icon success"></text>
				<text v-if="message.type === 'warn'" class="bm-icon warn"></text>
				<text v-if="message.type === 'error'" class="bm-icon error"></text>
				<text>{{message.content}}</text>
			</view>
		</block>
	</view>
</template>

<script>
	// 需要支持的单独配置项:显示时长、是否启用背景、类型、内容
	export default {
		name: 'bobo-message-cpt',
		props: {
			zIndex: {
				type: Number,
				default: 10000
			},
			duration: {
				type: Number,
				default: 2000
			},
			background: {
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				messageQueue: [],
				lastId: 0
			}
		},
		computed: {
			backgroundClass() {
				return msg => {
					return this.background || msg.background ? `background-${msg.type}` : ''
				}
			}
		},
		methods: {
			/**
			 * 展示普通提示信息
			 * @param {Object} content
			 */
			info(arg) {
				const message = {
					type: 'info'
				}
				if (typeof arg === 'object' && arg) {
					message.content = arg.content
					message.duration = arg.duration
					message.background = arg.background
				} else if(typeof arg === 'string') {
					message.content = arg
				}
				this.fadeIn(message)
			},
			/**
			 * 展示成功提示
			 * @param {Object} content
			 */
			success(arg) {
				const message = {
					type: 'success'
				}
				if (typeof arg === 'object' && arg) {
					message.content = arg.content
					// 显示时长会用在 settimeout的参数中,必须保证类型正确
					if (arg.duration && typeof arg.duration === 'number' && arg.duration >= 0) {
						message.duration = arg.duration
					}
					message.background = arg.background
				} else if(typeof arg === 'string') {
					message.content = arg
				}
				this.fadeIn(message)
			},
			/**
			 * 展示警告提示
			 */
			warn(arg) {
				const message = {
					type: 'warn'
				}
				if (typeof arg === 'object' && arg) {
					message.content = arg.content
					if (arg.duration && typeof arg.duration === 'number' && arg.duration >= 0) {
						message.duration = arg.duration
					}
					message.background = arg.background
				} else if(typeof arg === 'string') {
					message.content = arg
				}
				this.fadeIn(message)
			},
			/**
			 * 展示错误提示
			 * @param {Object} message
			 */
			error(arg) {
				const message = {
					type: 'error'
				}
				if (typeof arg === 'object' && arg) {
					message.content = arg.content
					if (arg.duration && typeof arg.duration === 'number' && arg.duration >= 0) {
						message.duration = arg.duration
					}
					message.background = arg.background
				} else if(typeof arg === 'string') {
					message.content = arg
				}
				this.fadeIn(message)
			},
			fadeIn(message) {
				message.id = this.generateId()
				message.animation = 'fadeIn'
				this.messageQueue.push(message)
				// 动画执行完毕后取消动画效果,防止列表刷新时重新执行动画
				setTimeout(() => {
					message.animation = ''
				}, 410)
				// 显示一段时间后隐藏
				setTimeout(() => {
					this.fadeOut(message)
				}, message.duration || this.duration)
			},
			fadeOut(message) {
				message.animation = 'fadeOut'
				setTimeout(() => {
					let idx = 0
					this.messageQueue.some((msg, index) => {
						if (msg.id === message.id) {
							idx = index
							return true
						}
					})
					this.messageQueue.splice(idx, 1)
				}, 500)
			},
			generateId() {
				return (new Date()).getTime() * 1000 + (this.lastId++) % 1000
			}
		}
	}
</script>

<style lang="scss" scoped>
	
	@font-face {
	  font-family: 'bobo-message-iconfont';  /* project id 1477381 */
	  src: url('https://at.alicdn.com/t/font_1477381_i3ji49ios6.eot');
	  src: url('https://at.alicdn.com/t/font_1477381_i3ji49ios6.eot?#iefix') format('embedded-opentype'),
	  url('https://at.alicdn.com/t/font_1477381_i3ji49ios6.woff2') format('woff2'),
	  url('https://at.alicdn.com/t/font_1477381_i3ji49ios6.woff') format('woff'),
	  url('https://at.alicdn.com/t/font_1477381_i3ji49ios6.ttf') format('truetype'),
	  url('https://at.alicdn.com/t/font_1477381_i3ji49ios6.svg#iconfont') format('svg');
	}

	.container {
		left: 0;
		right: 0;
		top: calc(var(--status-bar-height) + 24px);
		min-width: 10rpx;
		max-width: 550rpx;
		position: fixed;
		margin: 0 auto;
		display: flex;
		flex-direction: column;
		align-items: center;
		// 透明层允许点击穿透
		pointer-events: none;

		.message {
			display: flex;
			// 内容区域禁止点击穿透
			pointer-events: all;
			align-items: center;
			padding: 8px 16px;
			border-radius: 4px;
			margin-top: 5px;
			box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
			background: #fff;
			min-height: 37px;
			transition: all .5s;
			box-sizing: border-box;

			.bm-icon {
				font-family: bobo-message-iconfont;
				margin-right: 10rpx;
			}

			.info {
				color: #288ced;
			}

			.success {
				color: #09be70;
			}

			.warn {
				color: #ff991f;
			}

			.error {
				color: #ef4017;
			}
		}

		.background-info {
			border: 2px solid #d4eefe;
			background: #f0faff;
			color: #288ced;
			box-shadow: none !important;
		}

		.background-success {
			border: 2px solid #baf2d1;
			background: #edfff4;
			color: #09be70;
			box-shadow: none !important;
		}

		.background-warn {
			border: 2px solid #ffe7a7;
			background: #fff9e7;
			color: #ff991f;
			box-shadow: none !important;
		}

		.background-error {
			border: 2px solid #ffcfb9;
			background: #ffefe6;
			color: #ef4017;
			box-shadow: none !important;
		}

		.fadeIn {
			animation: fadeIn 0.4s both;
		}

		@keyframes fadeIn {
			from {
				opacity: 0;
				transform: translate(0, -30px);
			}

			to {
				opacity: 1;
				transform: translate(0, 0);
			}
		}

		.fadeOut {
			animation: fadeOut 0.4s forwards;
		}

		@keyframes fadeOut {
			from {
				opacity: 1;
				transform: translate(0, 0);
			}

			to {
				opacity: 0;
				margin-top: -37px;
			}
		}
	}
</style>

然后就可以选择你要在那个页面进行签名由于我这里是autograph.vue 名字你们随便起

unity android 微信签名 uniapp应用签名_unity android 微信签名_03


autograph.vue 代码如下

<template>
  <view class="signa">
    <view class="btn">
      <view class="cancel-btn" @click="toBack">返回</view>
      <view class="hand-title">手写签名</view>
      <view @click="clear" class="rewrite-btn">重写</view>
      <view @click="save" class="save-btn">使用</view>
    </view>
    <view class="canvas-wrap">
      <canvas
        class="canvas"
        disable-scroll="true"
        canvas-id="designature"
        @touchstart="starts"
        @touchmove="moves"
        @touchend="end"
      ></canvas>
    </view>
    <Message ref="Message"></Message>
  </view>
</template>

<script>
/*
 *	已兼容h5和小程序端 
 */
import boboMessage from "@/components/bobo-message/bobo-message.vue";

export default {
  data() {
    return {
      dom: null,
      line: [],
      radius: 0,
      isMove: false,
    };
  },
  components: {
    Message: boboMessage,
  },
  created() {
    this.dom = uni.createCanvasContext("designature", this);
  },
  onLoad() {
  },
  methods: {
    toBack() {
      uni.navigateBack();
    },
    end(e) {},
    distance(a, b) {
      let x = b.x - a.x;
      let y = b.y - a.y;
      return Math.sqrt(x * x + y * y);
    },
    starts(e) {
      this.line.push({
        points: [
          {
            time: new Date().getTime(),
            x: e.touches[0].x,
            y: e.touches[0].y,
            dis: 0,
          },
        ],
      });
      let currentPoint = {
        x: e.touches[0].x,
        y: e.touches[0].y,
      };
      this.currentPoint = currentPoint;
      this.drawer(this.line[this.line.length - 1]);
    },
    moves(e) {
      this.isMove = true;
      let point = {
        x: e.touches[0].x,
        y: e.touches[0].y,
      };
      (this.lastPoint = this.currentPoint), (this.currentPoint = point);
      this.line[this.line.length - 1].points.push({
        time: new Date().getTime(),
        x: e.touches[0].x,
        y: e.touches[0].y,
        dis: this.distance(this.currentPoint, this.lastPoint),
      });
      this.drawer(this.line[this.line.length - 1]);
    },
    drawer(item) {
      let x1,
        x2,
        y1,
        y2,
        len,
        radius,
        r,
        cx,
        cy,
        t = 0.5,
        x,
        y;
      var time = 0;
      if (item.points.length > 2) {
        let lines = item.points[item.points.length - 3];
        let line = item.points[item.points.length - 2];
        let end = item.points[item.points.length - 1];
        x = line.x;
        y = line.y;
        x1 = lines.x;
        y1 = lines.y;
        x2 = end.x;
        y2 = end.y;
        var dis = 0;
        time = line.time - lines.time + (end.time - line.time);
        dis = line.dis + lines.dis + end.dis;
        var dom = this.dom;
        var or = Math.min(
          (time / dis) * this.linePressure + this.lineMin,
          this.lineMax
        );
        cx =
          (x - Math.pow(1 - t, 2) * x1 - Math.pow(t, 2) * x2) /
          (2 * t * (1 - t));
        cy =
          (y - Math.pow(1 - t, 2) * y1 - Math.pow(t, 2) * y2) /
          (2 * t * (1 - t));
        dom.setLineCap("round");
        dom.beginPath();
        dom.setStrokeStyle("black");
        dom.setLineWidth(5);
        dom.moveTo(x1, y1);
        dom.quadraticCurveTo(cx, cy, x2, y2);

        dom.stroke();
        dom.draw(true);
      }
    },
    clear() {
      this.dom.clearRect(0, 0, 1000, 1000);
      this.dom.draw();
      this.isMove = false;
    },
    save() {
      // uni.showLoading({
      //   title: "加载中",
      //   mask:true
      // });

      if (!this.isMove) return this.$refs.Message.error("尚未进行签名!");
      const { type } = this.$route.query;
      uni.canvasToTempFilePath({
        canvasId: "designature",
        fileType: "png", 
        quality: 1, //图片质量
        success: async (res) => {
          try {
            //开始
			  console.log("开始");
			  console.log(res);
            
          } catch (error) {
            setTimeout(()=>{
                uni.hideLoading();
            },2000)
          }
        }

      });

    }, //save方法结束
  },
};
</script>

<style scoped lang="scss">
.signa {
  position: relative;
  overflow: hidden;
  // background-color: #fbfbfb;
  height: 100vh;
  width: 100vw;
  z-index: 1;
  .canvas-wrap {
    display: flex;
    justify-content: center;
    align-items: center;
    width: calc(100vw - 180rpx);
    height: 100vh;
  }
  .canvas {
    width: 100%;
    height: 90vh;
    background-color: #f4f8fb;
    position: absolute;
    z-index: 9999;
    // left: 45px;
    // border: 1px solid #d6d6d6;
  }
  .btn {
    height: 100vh;
    width: 160rpx;
    right: 0;
    position: fixed;
    // background-color: #007AFF;
    font-size: 40rpx;
    .cancel-btn {
      position: fixed;
      top: 30rpx;
      right: 0;
      color: $uni-text-color-blue;
      transform: rotate(90deg);
    }
    .hand-title {
      position: fixed;
      top: 45%;
      right: -40rpx;
      color: $uni-text-color-blue;
      transform: rotate(90deg);
    }
    .rewrite-btn {
      position: fixed;
      top: 82%;
      right: 0;
      color: $uni-text-color-blue;
      transform: rotate(90deg);
    }
    .save-btn {
      position: fixed;
      bottom: 30rpx;
      right: -10rpx;
      padding: 0 10rpx;
      color: #fff;
      background: $uni-color-primary;
      transform: rotate(90deg);
    }
  }
}
</style>

需要注意的是这里一定要在uni.scss文件里面定义不然指定报错

unity android 微信签名 uniapp应用签名_unity android 微信签名_04

uni.scss

$uni-text-color-blue:#10468c;
$uni-color-primary: #255796;

unity android 微信签名 uniapp应用签名_unity android 微信签名_05


unity android 微信签名 uniapp应用签名_ios_06

不定义的话就会报这个错误 (到时候网上去搜索又是说版本问题)就难搞!

unity android 微信签名 uniapp应用签名_unity android 微信签名_07


在pages.json找到对应的页面加上这个去掉页面导航

"app-plus": {
	"titleNView":false
	}

unity android 微信签名 uniapp应用签名_unity android 微信签名_08

运行项目打开可以看到是这个样子说明已经没啥问题了

unity android 微信签名 uniapp应用签名_vue.js_09


手动签名转成base64就已经完成了!打开控制台就能看到! res已经把图片转成了base64码了

unity android 微信签名 uniapp应用签名_小程序_10


找到代码里面的这个打印语句 然后在try里面就可以写调用后端的接口来就行上传了!

unity android 微信签名 uniapp应用签名_vue.js_11


上面有一个开启动画的,可以调用接口前开启动画,接口响应200的时候关闭动画 防止用户上传的时候还能手动签名!

unity android 微信签名 uniapp应用签名_unity android 微信签名_12

完结!!!