Skip to content

Commit

Permalink
feat: 新增signature签名组件
Browse files Browse the repository at this point in the history
  • Loading branch information
monsterxwx committed May 10, 2023
1 parent 54be77c commit c9d3534
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 10 deletions.
10 changes: 8 additions & 2 deletions examples/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
</script>

<template>
<router-view />
<div class="base-layout">
<router-view />
</div>
</template>

<style scoped>
.base-layout {
width: 100%;
height: 100%;
touch-action: none;
}
</style>
5 changes: 5 additions & 0 deletions examples/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const router = createRouter({
path: '/swiper',
name: 'swiper',
component: () => import('@/views/swiper.vue')
},
{
path: '/signature',
name: 'signature',
component: () => import('@/views/signature.vue')
}
]
})
Expand Down
54 changes: 54 additions & 0 deletions examples/src/views/signature.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<template>
<div class="signature">
<uvSignature @submit="submitImg" />
<div class="img-label">
<div class="img-title">
图片截图:
</div>
<img
v-if="imgUrl"
class="exportImg"
:src="imgUrl"
alt=""
>
</div>
<div>修改画笔 宽度 (lineWidth) 和 颜色 (pen-color) </div>
<uvSignature pen-color="#bfa" :line-width="6" />
</div>
</template>

<script setup>
import { uvSignature } from 'uv-ui'
import { ref } from 'vue'
const imgUrl = ref('')
const submitImg = (e) => {
imgUrl.value = e.image
}
</script>

<style lang="scss" scoped>
.signature {
display: flex;
padding: 10px;
width: 100%;
height: 100%;
background-color: #f7f8fa;
flex-direction: column;
gap: 20px;
.img-label {
display: flex;
flex-direction: column;
gap: 5px;
.img-title {
font-size: 14px;
font-weight: 700;
}
.exportImg {
width: 100%;
height: 200px;
}
}
}
</style>
2 changes: 1 addition & 1 deletion examples/src/views/swiper.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div style="width: 100%;height: 100%;touch-action: none;">
<div style="width: 100%;height: 100%;">
<uv-swipe>
<uv-swipe-item>
<div class="test">
Expand Down
7 changes: 5 additions & 2 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import uvForm from './form'
import uvFormItem from './form-item'
import uvTable from './table'
import uvCalendar from './calendar'
import uvSignature from './signature'

const components = [
uvButton,
Expand Down Expand Up @@ -93,7 +94,8 @@ const components = [
uvForm,
uvFormItem,
uvTable,
uvCalendar
uvCalendar,
uvSignature
]

const install = (Vue) => {
Expand Down Expand Up @@ -149,7 +151,8 @@ export {
uvForm,
uvFormItem,
uvTable,
uvCalendar
uvCalendar,
uvSignature
}

export default install
6 changes: 6 additions & 0 deletions packages/components/src/signature/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Signature from './signature.vue'
import { withInstall } from '@uv-ui/utils'

const uvSignature = withInstall(Signature)

export default uvSignature
210 changes: 210 additions & 0 deletions packages/components/src/signature/signature.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<template>
<div class="uv-signature" :style="{height:height}">
<div class="uv-signature-content" ref="wrapRef">
<canvas
ref="canvasRef"
:width="state.width"
:height="state.height"
@touchstartPassive="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
/>
</div>
<slot name="footer">
<div class="uv-signature-footer">
<div class="uv-signature-footer-clear" @click="clear">
{{ clearButtonText }}
</div>
<div class="uv-signature-footer-confim" @click="submit">
{{ confirmButtonText }}
</div>
</div>
</slot>
</div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useRect } from '@uv-ui/hooks'
const props = defineProps({
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '200px'
},
lineWidth: {
type: Number,
default: 3
},
penColor: {
type: String,
default: '#000'
},
exportImgType: {
type: String,
default: 'png'
},
clearButtonText: {
type: String,
default: '清空'
},
confirmButtonText: {
type: String,
default: '确认'
}
})
const emit = defineEmits(['start', 'signing', 'end', 'clear', 'submit'])
const wrapRef = ref(null)
const canvasRef = ref(null)
const state = reactive({
width: 0,
height: 0,
ctx: null
})
let canvasRect = ''
onMounted(() => {
state.ctx = canvasRef.value?.getContext('2d')
state.width = wrapRef.value?.offsetWidth || 0
state.height = wrapRef.value?.offsetHeight || 0
})
const touchStart = () => {
if (!state.ctx) {
return false
}
state.ctx.beginPath()
state.ctx.lineWidth = props.lineWidth
state.ctx.strokeStyle = props.penColor
canvasRect = useRect(canvasRef)
emit('start')
}
const touchMove = (event) => {
if (!state.ctx) {
return false
}
const touch = event.touches[0]
const mouseX = touch.clientX - (canvasRect?.left || 0)
const mouseY = touch.clientY - (canvasRect?.top || 0)
state.ctx.lineCap = 'round'
state.ctx.lineJoin = 'round'
state.ctx?.lineTo(mouseX, mouseY)
state.ctx?.stroke()
emit('signing', event)
}
const touchEnd = (event) => {
emit('end')
}
const clear = () => {
if (state.ctx) {
state.ctx.clearRect(0, 0, state.width, state.height)
state.ctx.closePath()
}
emit('clear')
}
const isCanvasEmpty = (canvas) => {
const empty = document.createElement('canvas')
empty.width = canvas.width
empty.height = canvas.height
return canvas.toDataURL() === empty.toDataURL()
}
const submit = () => {
const canvas = canvasRef.value
if (!canvas) {
return
}
const isEmpty = isCanvasEmpty(canvas)
const image = isEmpty
? ''
: canvas.toDataURL(
`image/${props.exportImgType}`,
props.exportImgType === 'jpg' ? 0.9 : null
)
emit('submit', {
image,
canvas
})
}
</script>
<script>
export default {
name: 'UvSignature'
}
</script>
<style lang="scss">
:root {
--uv-signature-default-gap: 5px;
--uv-signature-content-border: 1px dotted #dadada;
--uv-signature-content-border-radius: 8px;
--uv-signature-content-bg-color: #ffffff;
--uv-signature-footer-button-text-padding: 8px 16px;
--uv-signature-footer-button-text-size: 12px;
--uv-signature-footer-button-border-radius: 4px;
--uv-signature-footer-clear-button-border: 1px solid #dcdee0;
--uv-signature-footer-clear-button-color: #323233;
--uv-signature-footer-clear-button-bg-color: #ffffff;
--uv-signature-footer-confim-button-color: #ffffff;
--uv-signature-footer-confim-button-bg-color: #1989fa;
}
.uv-signature {
display: flex;
width: 100%;
flex-direction: column;
gap: var(--uv-signature-default-gap);
.uv-signature-content {
overflow: hidden;
width: 100%;
height: 100%;
border: var(--uv-signature-content-border);
border-radius: var(--uv-signature-content-border-radius);
background-color: var(--uv-signature-content-bg-color);
}
.uv-signature-footer {
display: flex;
justify-content: flex-end;
gap: var(--uv-signature-default-gap);
.uv-signature-footer-clear,
.uv-signature-footer-confim {
display: flex;
justify-content: center;
align-items: center;
padding: var(--uv-signature-footer-button-text-padding);
font-size: var(--uv-signature-footer-button-text-size);
border-radius: var(--uv-signature-footer-button-border-radius);
}
.uv-signature-footer-clear {
border: var(--uv-signature-footer-clear-button-border);
color: var(--uv-signature-footer-clear-button-color);
background-color: var(--uv-signature-footer-clear-button-bg-color);
}
.uv-signature-footer-confim {
color: var(--uv-signature-footer-confim-button-color);
background-color: var(--uv-signature-footer-confim-button-bg-color);
}
}
}
</style>
12 changes: 7 additions & 5 deletions packages/hooks/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {useChildren,useParent} from './useContext'
import {useTouch} from './useTouch'
export {
import { useChildren, useParent } from './useContext'
import { useTouch } from './useTouch'
import { useRect } from './useRect'
export {
useChildren,
useParent,
useTouch
}
useTouch,
useRect
}
30 changes: 30 additions & 0 deletions packages/hooks/useRect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { unref } from 'vue'

const isWindow = (val) => val === window

const makeDOMRect = (width, height) => {
return {
top: 0,
left: 0,
right: width,
bottom: height,
width,
height
}
}

export const useRect = (elementOrRef) => {
const element = unref(elementOrRef)

if (isWindow(element)) {
const width = element.innerWidth
const height = element.innerHeight
return makeDOMRect(width, height)
}

if (element?.getBoundingClientRect) {
return element.getBoundingClientRect()
}

return makeDOMRect(0, 0)
}

0 comments on commit c9d3534

Please sign in to comment.