人的一生就是进行尝试,尝试的越多,生活就越美好。——爱默生
在前一阶段的工作中,突然接到了这个需求:手写签批的页面在移动端竖屏时强制页面横屏展示进行签字,一开始我觉着只要将页面使用 css3 的 transform 进行 rotate 一下就可以了,但是当我尝试后发现并不是像我想象的那样简单。
在介绍横屏签字之前,我想先说明一下我实现签批使用的插件以及插件所调用的方法,这样在之后说到横屏签字的时候,大佬们不会感觉唐突。
项目使用 vue-signature-pad 插件进行签名功能实现,强调一下如果使用vue2进行开发,安装的 vue-signature-pad 的版本我自测 2.0.5 是可以的
npm i vue-signature-pad@2.0.5
// main.js
import Vue from 'vue'
import App from './App.vue'
import VueSignaturePad from 'vue-signature-pad'
Vue.use(VueSignaturePad)
Vue.config.productionTip = false
new Vue({
render: (h) => h(App),
}).$mount('#app')
这里我为了方便直接写了一个demo放到App.vue中,没有封装成组件
// app.vue
<template>
<div id="app">
<div style="background: #fff">
<vue-signature-pad
id="signature"
width="95%"
height="400px"
ref="signaturePad"
:options="options"
/>
</div>
<button @click="save">保存</button>
<button @click="resume">重置</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
options: {
penColor: '#000',
},
}
},
methods: {
save() {
const { isEmpty, data } = this.$refs.signaturePad.saveSignature()
console.log(isEmpty)
console.log(data)
},
//清除重置
resume() {
this.$refs.signaturePad.clearSignature()
},
},
}
</script>
<style lang="scss">
html,
body {
padding: 0;
margin: 0;
}
#app {
width: 100vw;
height: 100vh;
background: #ececec;
}
</style>
代码比较通俗易懂,就是调用组件封装好的方法,保存后能够解构出data为base64编码的图片,之后需要将base64编码格式转换成File文件格式的图片最后进行接口请求,那么转换方法如下展示
<template>
<div id="app">
<div style="background: #fff">
<vue-signature-pad
id="signature"
width="95%"
height="300px"
ref="signaturePad"
:options="options"
/>
</div>
<div v-for="(item, index) in imgList" :key="index">
<img :src="item.src" alt="" width="100" />
</div>
<button @click="save" class="btn">保存</button>
<button @click="resume" class="btn">重置</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
options: {
penColor: '#000',
},
imgList: [],
}
},
methods: {
save() {
const { isEmpty, data } = this.$refs.signaturePad.saveSignature()
this.imgList.push({
src: data,
})
let res = this.dataURLtoFile(data, 'demo')
console.log(res)
},
// 清除重置
resume() {
this.$refs.signaturePad.clearSignature()
},
// 将base64转换为文件
dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
},
},
}
</script>
<style lang="scss">
html,
body {
padding: 0;
margin: 0;
}
#app {
width: 100vw;
height: 100vh;
background: #ececec;
}
.btn {
width: 35%;
color: #fff;
background: #5daaf3;
border: none;
height: 40px;
border-radius: 20px;
margin-top: 20px;
margin-left: 40px;
}
</style>
之后根据需求调用接口将文件图片作为入参即可。
经过上面的操作,我们就实现了前端的签批的完整流程,还是比较容易理解的。
在实现这个功能不久之后,客户那边提出了新的需求:手机竖屏时将签字功能进行横屏展示。
刚开始接到这个需求的时候,通过我所掌握的技术首先就是想到用CSS3的transform:rotate方法进行页面90deg的旋转,将签字组件也进行旋转之后进行签名;由于我对canvas不是很了解,所以我把包裹在签字组件外的div标签进行了旋转后签字发现落笔点位置错乱。
<div style="background: #fff; transform: rotate(-90deg)">
<vue-signature-pad
id="signature"
width="95%"
height="300px"
ref="signaturePad"
:options="options"
/>
</div>
既然不能旋转外层的div,那我想到一种欺骗方式:不旋转div,样式修改成与横屏样式相似,然后将生成的图片进行一个旋转,这样就ok了!那么我们的目标就明确了,找到能够旋转bas64编码的方法然后返回一个旋转后的base64图片在转换成file文件传递给后端问题就解决了。
经过一个苦苦寻找,终于找到了方法并实现了这个功能,话不多说,先撸为敬(样式大佬们自己改下,我这里展示下转换后的图片)。
<template>
<div id="app">
<div style="background: #fff">
<vue-signature-pad
id="signature"
width="95%"
height="300px"
ref="signaturePad"
:options="options"
/>
</div>
<div v-for="(item, index) in imgList" :key="index">
<img :src="item.src" alt="" width="100" />
</div>
<div class="buttons">
<button @click="save" class="btn">保存</button>
<button @click="resume" class="btn">重置</button>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
options: {
penColor: '#000',
},
imgList: [],
fileList: [],
}
},
methods: {
save() {
const { isEmpty, data } = this.$refs.signaturePad.saveSignature()
this.rotateBase64Img(data, 90, (res) => {
console.log(res) // 旋转后的base64图片src
this.fileList.push({
file: this.dataURLtoFile(res, 'sign'),
name: 'sign',
})
this.imgList.push({
src: res,
})
})
},
// 清除重置
resume() {
this.$refs.signaturePad.clearSignature()
},
// 将base64转换为文件
dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
},
// 通过canvas旋转图片
rotateBase64Img(src, edg, callback) {
var canvas = document.createElement('canvas')
var ctx = canvas.getContext('2d')
var imgW //图片宽度
var imgH //图片高度
var size //canvas初始大小
if (edg % 90 != 0) {
console.error('旋转角度必须是90的倍数!')
throw '旋转角度必须是90的倍数!'
}
edg < 0 && (edg = (edg % 360) + 360)
const quadrant = (edg / 90) % 4 //旋转象限
const cutCoor = { sx: 0, sy: 0, ex: 0, ey: 0 } //裁剪坐标
var image = new Image()
image.crossOrigin = 'anonymous'
image.src = src
image.onload = function () {
imgW = image.width
imgH = image.height
size = imgW > imgH ? imgW : imgH
canvas.width = size * 2
canvas.height = size * 2
switch (quadrant) {
case 0:
cutCoor.sx = size
cutCoor.sy = size
cutCoor.ex = size + imgW
cutCoor.ey = size + imgH
break
case 1:
cutCoor.sx = size - imgH
cutCoor.sy = size
cutCoor.ex = size
cutCoor.ey = size + imgW
break
case 2:
cutCoor.sx = size - imgW
cutCoor.sy = size - imgH
cutCoor.ex = size
cutCoor.ey = size
break
case 3:
cutCoor.sx = size
cutCoor.sy = size - imgW
cutCoor.ex = size + imgH
cutCoor.ey = size + imgW
break
}
ctx.translate(size, size)
ctx.rotate((edg * Math.PI) / 180)
ctx.drawImage(image, 0, 0)
var imgData = ctx.getImageData(
cutCoor.sx,
cutCoor.sy,
cutCoor.ex,
cutCoor.ey
)
if (quadrant % 2 == 0) {
canvas.width = imgW
canvas.height = imgH
} else {
canvas.width = imgH
canvas.height = imgW
}
ctx.putImageData(imgData, 0, 0)
callback(canvas.toDataURL())
}
},
},
}
</script>
<style lang="scss">
html,
body {
padding: 0;
margin: 0;
}
#app {
width: 100vw;
height: 100vh;
background: #ececec;
}
.btn {
width: 35%;
color: #fff;
background: #5daaf3;
border: none;
height: 40px;
border-radius: 20px;
margin-top: 20px;
margin-left: 40px;
}
</style>
那么经过翻转后当我们横屏移动设备时,保存出的图片会进行90度旋转,传递给后端的图片就是正常的了(代码可直接食用)
后来我发现签字的笔锋太细了,打印出来的效果很差,于是通过查阅,只要设置 options 中的 minWidth和maxWidth 大一些即可, 到此所有需求就已经都解决了。
其实平时开发中没有对canvas用到很多,导致对这块的知识很薄弱,我在查阅的时候找到过用原生实现此功能,不过因为时间不够充裕,为了完成需求耍了一个小聪明,后续应该对canvas更多的了解一下,在深入了解上面的旋转方法具体是如何实现的,希望这篇文章能够对遇到这种需求并且时间紧迫的你有所帮助!
作者:爱泡澡的小萝卜
链接:https://juejin.cn/post/7260697932173590565
这篇文章介绍了JS和jquery获取各种屏幕的宽度和高度的代码,有需要的朋友可以参考一下:网页可见区域宽: document.body.clientWidth,网页可见区域高: document.body.clientHeight
假设目标元素:#element,该元素相对于文档顶部的偏移距离,获取该元素的高度.那应该如何确定元素在可见区域内呢?假设当元素的上边框刚好出现在浏览器的底部时(零界点)
家有时候有需求在屏幕方向改变的时候重新执行某个渲染函数,以获取方向改变后的实际宽高,但是首次加载的执行函数要在其他地方执行,这时候可以加一个flag的状态值,默认为false
有些时候我们玩手机更喜欢使用手势滑动带来的用户操作体验。Vue touch directive是一个用于移动设备操作指令的轻量级的VUE组件。使用它可以轻松实现屏幕触控、滑动触发事件,提高用户体验
这在家办公也不让闲着点。虽然说需求提出来了,但是我们身为一个前端er,还是要有自己的想法呀,我们要统计一波数据看看到底有多少人在横屏使用我们的产品。
在开发中可能有遇到过屏幕录制的需求,无论是教学、演示还是游戏录制,都需要通过屏幕录制来记录和分享内容。一般在App内H5页基于客户端能力实现的较多,现在浏览器中的 MediaRecorder 也提供了这种能力
getBoundingClientRect会受到transform的影响。比如你的元素设置了transform:scale(2), 那么getBoundingClientRect返回的width会是元素实际宽度的2倍,top等位置信息也会因为元素尺寸变化而发生变化.
JavaScript 中的一些新功能非常值得期待,唤醒锁定 API 就是其中之一。它允许我们与主机系统进行交互,可以帮助开发人员使用 JavaScript 指示操作系统保持屏幕唤醒状态!
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!