H5前端实现移动端手写Canvas签名(支持横竖屏,自定义图片旋转角度)

37 篇文章 0 订阅
3 篇文章 0 订阅

=================== 2023年01月更新 ===================

发现好多小伙伴查看这个Demo案例,于是将组件发布到 Npm - 【@jensonhui/signature-h5】了,需要的小伙伴可以直接安装使用,文档请查看README

安装:

yarn add @jensonhui/signature-h5

or

cnpm install @jensonhui/signature-h5

使用:main.js

// main.js
import Vue from 'vue';
import App from './App.vue';

// 手写签名
import signatureH5 from '@jensonhui/signature-h5';
import '@jensonhui/signature-h5/lib/signatureH5.css';

Vue.config.productionTip = false;
Vue.use(signatureH5);

new Vue({
  render: (h) => h(App),
}).$mount('#app');



// template
<template>
  <div id="app">
    <signature-h5 />
  </div>
</template>


=================== 2022年7月更新 ===================

最近发现,开源出来的源码、教程被某度收录到文库中了,但这不是关键,重点是它们在收费查看下载,恶心至极,索性直接把源码放出来,大家不用花冤枉money了!!(文下有地址)。

移动端下业务需要全屏手写签名,需求:兼容横竖屏,无论横竖屏下,签字图都需要是横向显示

这里直接用 SignaturePad插件 ,主要记录踩坑:

实现效果:


思路分析:

1. 移动H5虽可以判断横竖屏,但考虑到多设备访问问题(微信下,有的手机需要用户开启横屏模式才可以旋转,一般人不知道怎么设置),所以强制横屏显示只能放弃。

2. 无法强制横屏,那么我们是否可以通过CSS将页面旋转达到视觉横屏效果呢? 答案当然是可以,通过 transform: rotate(90deg) 即可实现。

3. 通过transform可以将页面旋转,但是会面临新的问题:canvas画布旋转后会导致落笔精度丢失,如果你是手写canvas可以通过计算X,Y调整落笔,由于笔者采用插件,所以只能另寻思路。

4. 不能旋转canvas区域,那么我们只旋转按钮和文字提示区域,绘图canvas区域通过css媒体查询判断横竖屏,赋值不同得宽高,这样来达到视觉上得强制横屏。


 踩坑记录:


1 . 移动端屏幕旋转,canvas触控点失灵,签名位置错乱

解决方案不唯一,

首先canvas的父容器(class='recruit-canvas')固定定位在body下,默认宽高继承body,100%;

其次,定位canvas采用定位,脱离文档流,动态设定宽高即可(宽高设置无效看踩坑三)

2 . 安卓微信下,网页无法旋转,IOS下网页旋转正常

ios手机权限高于微信,所以微信**旋转方向**默认开启跟随系统

android手机下,微信默认关闭旋转跟随系统,打开设置步骤:我的-设置-通用-开启横屏模式(跟随系统旋转)

3 . canvas赋值宽高无效

canvas.style.width/height 与 canvas.width/height 设置不同,

单独设置canvas.width/height前提需要在html标签中初始化默认width,height属性;
单独设置canvas.style.width/height,需要在后面添加'px'单位,否则失效;

备注:实际测试中Google chrome,发现不设置属性依然可以设置canvas.width/height,可能与兼容性有关


实现方式:

方式两种:区别在于用户感知

1.  通过window.resize或window.orientation判断横屏竖屏状态,设置对应宽高 - 无感知


2.  监听window.resize, 重新加载reload - 有感知,页面会刷新

// 这里为部分布局html,详情实现请看 DEMO

<template>
  <div class='recruit-canvas'>
    <div class="canvas-box" ref="canvasRef" v-show="!previewImage">
      <canvas ref="canvasMapRef" id="canvas-map" width="100" height="100"></canvas>
    </div>

    <div class="btn-box flex-row">
      <span class="del-btn" @click="clearCanvasHandle">清除</span>
      <span class="sure-btn" @click="makeCanvasHandle">确认</span>
    </div>


    <img v-show="previewImage" class="preview-image" :src="previewImage" alt="生成预览">
  </div>
</template>


<style lang='css' scoped>
.recruit-canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.recruit-canvas .preview-image {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 999;
  transform: translate(-50%, -50%);
}
.recruit-canvas .canvas-box,
.recruit-canvas .btn-box {
  position: absolute;
  top: 50%;
  z-index: 100;
}
.recruit-canvas .btn-box {
  left: -22%;
  z-index: 1000;
  text-align: center;
  transform: rotate(90deg);
  -o-transform: rotate(90deg);
  -ms-transform: rotate(90deg);
  -moz-transform: rotate(90deg);
  -webkit-transform: rotate(90deg);
}
.recruit-canvas .btn-box .del-btn,
.recruit-canvas .btn-box .sure-btn {
  display: inline-block;
  width: 100px;
  height: 24px;
  margin: 0 10px;
  line-height: 24px;
  border-radius: 6px;
  background-color: #fff;
}
.recruit-canvas .btn-box .del-btn {
  color: #FF5E00;
}
.recruit-canvas .btn-box .sure-btn {
  color: #fff;
  background: linear-gradient(100deg, #FF4E01 0%, #FFBC01 100%);
}
.recruit-canvas .canvas-box {
  left: 22%;
  height: 80vh;
  width: 70vw;
  overflow: hidden;
  border: 1px dashed #D4D4D4;
  transform: translateY(-50%);
  background-color: #fff;
}
.recruit-canvas .canvas-box #canvas-map {
  width: 100%;
  height: 100%;
}
@media screen and (orientation: portrait) {
  /*竖屏 css*/
}
@media screen and (orientation: landscape) {
  /*横屏 css*/
  .recruit-canvas .canvas-box {
    top: 20px;
    left: 10%;
    width: 80vw;
    height: 70vh;
    transform: translateY(0);
  }
  .recruit-canvas .btn-box {
    width: 60%;
    left: 20%;
    top: 86%;
    transform: rotate(0);
  }
}
</style>

传送门:

查看完整DEMO源码 ( 兼容横竖屏,最终生成是横屏效果图 ) 

特别感谢:

我是中小学生 ,程先生哈,提供了思路方向

要在Vue移动端实现手写签名功能,可以使用HTML5的canvas元素和JavaScript实现。首先,你需要创建一个canvas元素,并设置其宽高比,以便在横模式下正确显示。然后,你需要监听设备方向变化事件,根据设备方向动态调整canvas元素的宽高比,以便在横模式下正确显示。最后,你需要监听用户手写事件,并在canvas元素上绘制用户手写签名。 以下是示例代码,可以在移动设备上测试: ```html <template> <div class="canvas-container"> <canvas ref="canvas"></canvas> </div> </template> <script> export default { mounted() { // 监听设备方向变化事件 window.addEventListener("orientationchange", this.onOrientationChange); // 初始化canvas this.initCanvas(); }, methods: { initCanvas() { const canvas = this.$refs.canvas; const ctx = canvas.getContext("2d"); // 设置canvas宽高比为3:2 canvas.width = 300; canvas.height = 200; // 监听用户手写事件 canvas.addEventListener("touchstart", this.onTouchStart); canvas.addEventListener("touchmove", this.onTouchMove); canvas.addEventListener("touchend", this.onTouchEnd); }, onOrientationChange() { const canvas = this.$refs.canvas; const ctx = canvas.getContext("2d"); // 根据设备方向动态调整canvas宽高比 if (window.orientation === 90 || window.orientation === -90) { canvas.width = 200; canvas.height = 300; } else { canvas.width = 300; canvas.height = 200; } // 清空canvas ctx.clearRect(0, 0, canvas.width, canvas.height); }, onTouchStart(event) { const canvas = this.$refs.canvas; const ctx = canvas.getContext("2d"); // 开始绘制路径 ctx.beginPath(); // 获取触点位置 const touch = event.touches[0]; const x = touch.pageX - canvas.offsetLeft; const y = touch.pageY - canvas.offsetTop; // 移动到触点位置 ctx.moveTo(x, y); }, onTouchMove(event) { const canvas = this.$refs.canvas; const ctx = canvas.getContext("2d"); // 获取触点位置 const touch = event.touches[0]; const x = touch.pageX - canvas.offsetLeft; const y = touch.pageY - canvas.offsetTop; // 绘制直线到触点位置 ctx.lineTo(x, y); ctx.stroke(); }, onTouchEnd() { const canvas = this.$refs.canvas; const ctx = canvas.getContext("2d"); // 结束路径绘制 ctx.closePath(); } }, beforeDestroy() { // 移除事件监听 window.removeEventListener("orientationchange", this.onOrientationChange); const canvas = this.$refs.canvas; canvas.removeEventListener("touchstart", this.onTouchStart); canvas.removeEventListener("touchmove", this.onTouchMove); canvas.removeEventListener("touchend", this.onTouchEnd); } }; </script> <style scoped> .canvas-container { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } canvas { border: 1px solid #ccc; } </style> ``` 在上面的示例中,我们创建了一个canvas元素,并设置了其宽高比为3:2,以便在竖模式下正确显示。我们还监听了设备方向变化事件,根据设备方向动态调整canvas元素的宽高比。我们还监听了用户手写事件,并在canvas元素上绘制用户手写签名。当用户完成签名后,可以将canvas元素上的图像数据保存到服务器或本地存储中。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值