=================== 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源码 ( 兼容横竖屏,最终生成是横屏效果图 )