臨時幫朋友寫個移動端的項目, 發現 它的項目中有個 pdf 文檔的 簽名功能, 也是折騰了一下簽名組件
簽名組件原理: 利用原生canvas 配合 移動端的事件, touchstart, touchend, touchmove, 進行 在canvas 畫布上畫線, 最後,把生成的簽名, 通過 toDataURL()方法 保存 爲 base64 圖片,就拿到簽名了,實現效果如下:
清空畫布, 選擇簽名字體顏色, 保存, 功能比較簡單
github上的 簽名組件
上代碼啊
注: 本項目中使用了 vant-ui, 看演示報錯的小夥伴 ,記得刪除掉 vant-ui 的組件,然而,你可能看不到 有些效果了,比如字體顏色,清空, 確認等
1. template 模板
<template>
<div class="signHandle">
<canvas ref="signHandle" class="canvas" />
<div class="btn_container van-hairline--top" :style="{height:height + 'px'}">
<van-radio-group v-model="radio" class="radio_container" @change="radioHandle">
<van-radio v-for="item in liColors" :key="item" :checked-color="item" :name="item" />
</van-radio-group>
<div>
<van-button size="mini" @touchstart="clearHandle">清空</van-button>
<van-button type="info" size="mini" @touchstart="saveImg">確認</van-button>
</div>
</div>
</div>
</template>
2. js, 都是 canvas 的 一些 api , 就不解釋了
<script>
// 解構設備的寬度, 和 高度
const { clientWidth, clientHeight } = document.documentElement
export default {
data() {
return {
radio: '#000',
height: 50,
direction: false, // true 代表橫屏, false 代表'豎屏'
el: '', // canvas dom
ctx: '', // canvas context
background: '#fff', // canvas background-color
color: '#000', // 繪製時線條的顏色
linewidth: 3, // 線條的寬度
liColors: ['#ee0a24', '#000', '#1890ff']
}
},
created() {
this.color = this.radio
window.addEventListener(
'onorientationchange' in window ? 'orientationchange' : 'resize',
() => {
if (window.orientation === 180 || window.orientation === 0) {
this.direction = false
this.draw()
}
if (window.orientation === 90 || window.orientation === -90) {
this.direction = true
this.draw()
}
},
false
)
},
mounted() {
this.draw()
},
methods: {
radioHandle(value) {
this.color = value
this.setCanvas()
},
// 添加繪製 line
draw() {
document.addEventListener('touchmove', e => e.preventDefault(), {
passive: false
})
this.el = this.$refs.signHandle
this.initCanvas()
},
// 初始化canvas配置
initCanvas() {
const { height, direction, el } = this
if (direction) {
el.width = clientHeight
el.height = clientWidth - height
} else {
el.width = clientWidth
el.height = clientHeight - height
}
this.ctx = el.getContext('2d')
this.setCanvas()
this.drawStart()
this.drawing()
this.drawEnd()
},
// 配置 canvas
setCanvas() {
const { ctx, height, direction } = this
console.log(direction)
// 設置背景色
ctx.fillStyle = this.background
// 繪製矩形
if (direction) {
ctx.fillRect(0, 0, clientHeight, clientWidth - height)
} else {
ctx.fillRect(0, 0, clientWidth, clientHeight - height)
}
// 設置線條顏色
ctx.strokeStyle = this.color
// 設置線寬
ctx.lineWidth = this.linewidth
// 設置線條兩頭的結束點和開始點是圓形的
ctx.lineCap = 'round'
},
// 開始繪製
drawStart() {
const { el, ctx } = this
el.addEventListener(
'touchstart',
e => {
ctx.beginPath()
ctx.moveTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY)
},
false
)
},
// 繪製中
drawing() {
const { el, ctx } = this
el.addEventListener(
'touchmove',
e => {
ctx.lineTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY)
ctx.stroke()
},
false
)
},
// 繪製結束
drawEnd() {
const { el, ctx } = this
el.addEventListener('touchend', () => ctx.closePath(), false)
},
// 清空
clearHandle() {
this.initCanvas()
},
// 保存信息
saveImg() {
const imgBase64 = this.el.toDataURL()
console.log('保存簽名成功' + imgBase64)
}
}
}
</script>
3. css
.signHandle {
position: relative;
background-color: #666;
.canvas {
position: absolute;
top: 0;
left: 0;
}
.btn_container {
width: 100%;
position: fixed;
bottom: 0;
left: 0;
background-color: #fff;
display: flex;
align-items: center;
padding: 0 15px;
box-sizing: border-box;
justify-content: space-between;
.radio_container {
display: flex;
/deep/ .van-radio {
margin-right: 10px;
&:nth-child(1) {
/deep/ .van-icon {
background-color: #ee0a24;
}
}
&:nth-child(2) {
/deep/ .van-icon {
background-color: #000;
}
}
&:nth-child(3) {
/deep/ .van-icon {
background-color: #1890ff;
}
}
}
}
}
}
</style>