修改
@ -1,245 +0,0 @@
|
||||
<template>
|
||||
<view class="signature-wrap">
|
||||
<canvas :canvas-id="cid" :id="cid" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd"
|
||||
style="height:600rpx" :style="[{ width: formatSize(width)}, customStyle]"></canvas>
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* sign canvas 手写签名
|
||||
* @description 设置线条宽度、颜色,撤回,清空
|
||||
* @tutorial
|
||||
* @property {String} cid canvas id 不设置则默认为 v-sign-时间戳
|
||||
* @property {String, Number} width canvas 宽度
|
||||
* @property {String, Number} height canvas 高度
|
||||
* @property {Object} customStyle 自定义样式
|
||||
* @property {String} lineColor 画笔颜色
|
||||
* @property {Number} lineWidth 画笔大小,权重大于 v-sign-pen 组件设置的画笔大小
|
||||
* @event {Function} init 当创建完 canvas 实例后触发,向外提供 canvas实例,撤回,清空方法
|
||||
* @example <v-sign @init="signInit"></v-sign>
|
||||
*/
|
||||
import {
|
||||
formatSize
|
||||
} from './utils'
|
||||
// convas 实例
|
||||
let canvasCtx
|
||||
|
||||
export default {
|
||||
name: 'v-sign',
|
||||
props: {
|
||||
// canvas id
|
||||
cid: {
|
||||
type: String,
|
||||
default: `v-sign-${Date.now()}`
|
||||
// required: true
|
||||
},
|
||||
// canvas 宽度
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: '100%'
|
||||
},
|
||||
// canvas 高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 300
|
||||
},
|
||||
// 画笔大小,权重大于 v-sign-pen 组件设置的画笔大小
|
||||
lineWidth: {
|
||||
type: Number
|
||||
},
|
||||
// 线颜色
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#000'
|
||||
},
|
||||
// canvas自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
getInterface: this.provideInterface
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatSize,
|
||||
lineData: [],
|
||||
winWidth: 0,
|
||||
winHeight: 0,
|
||||
penLineWidth: null, // v-sign-pen 组件设置的画笔大小
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
canvasCtx = uni.createCanvasContext(this.cid, this)
|
||||
// 初始化完成,向外暴露接口
|
||||
this.$emit('init', this.provideInterface())
|
||||
// 获取窗口宽高
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
this.winWidth = res.windowWidth
|
||||
this.winHeight = res.windowHeight
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
onTouchStart(e) {
|
||||
const pos = e.touches[0]
|
||||
this.lineData.push({
|
||||
style: {
|
||||
color: this.lineColor,
|
||||
width: this.lineWidth || this.penLineWidth || 4
|
||||
},
|
||||
// 屏幕坐标
|
||||
coordinates: [{
|
||||
type: e.type,
|
||||
x: pos.x,
|
||||
y: pos.y
|
||||
}]
|
||||
})
|
||||
this.drawLine()
|
||||
},
|
||||
onTouchMove(e) {
|
||||
const pos = e.touches[0]
|
||||
this.lineData[this.lineData.length - 1].coordinates.push({
|
||||
type: e.type,
|
||||
x: pos.x,
|
||||
y: pos.y
|
||||
})
|
||||
this.drawLine()
|
||||
},
|
||||
onTouchEnd(e) {
|
||||
// console.log(e.type, e)
|
||||
},
|
||||
// 清空画布
|
||||
clear() {
|
||||
this.lineData = []
|
||||
canvasCtx.clearRect(0, 0, this.winWidth, this.winHeight)
|
||||
canvasCtx.draw()
|
||||
},
|
||||
// 撤销
|
||||
revoke() {
|
||||
this.lineData.pop()
|
||||
this.lineData.forEach((item, index) => {
|
||||
canvasCtx.beginPath()
|
||||
canvasCtx.setLineCap('round')
|
||||
canvasCtx.setStrokeStyle(item.style.color)
|
||||
canvasCtx.setLineWidth(item.style.width)
|
||||
item.coordinates.forEach(pos => {
|
||||
if (pos.type == 'touchstart') {
|
||||
canvasCtx.moveTo(pos.x, pos.y)
|
||||
} else {
|
||||
canvasCtx.lineTo(pos.x, pos.y)
|
||||
}
|
||||
})
|
||||
canvasCtx.stroke()
|
||||
})
|
||||
canvasCtx.draw()
|
||||
},
|
||||
// 绘制线条
|
||||
drawLine() {
|
||||
const lineDataLen = this.lineData.length
|
||||
if (!lineDataLen) return
|
||||
const currentLineData = this.lineData[lineDataLen - 1]
|
||||
const coordinates = currentLineData.coordinates
|
||||
const coordinatesLen = coordinates.length
|
||||
if (!coordinatesLen) return
|
||||
let startPos
|
||||
let endPos
|
||||
if (coordinatesLen < 2) {
|
||||
// only start, no move event
|
||||
startPos = coordinates[coordinatesLen - 1]
|
||||
endPos = {
|
||||
x: startPos.x + 1,
|
||||
y: startPos.y
|
||||
}
|
||||
} else {
|
||||
startPos = coordinates[coordinatesLen - 2]
|
||||
endPos = coordinates[coordinatesLen - 1]
|
||||
}
|
||||
|
||||
const style = currentLineData.style
|
||||
canvasCtx.beginPath()
|
||||
canvasCtx.setLineCap('round')
|
||||
canvasCtx.setStrokeStyle(style.color)
|
||||
canvasCtx.setLineWidth(style.width)
|
||||
canvasCtx.moveTo(startPos.x, startPos.y)
|
||||
canvasCtx.lineTo(endPos.x, endPos.y)
|
||||
// const P1 = this.caculateBezier(startPos, endPos, centerPos)
|
||||
// console.log(P1.x, P1.y)
|
||||
// canvasCtx.moveTo(startPos.x, startPos.y)
|
||||
// canvasCtx.quadraticCurveTo(P1.x, P1.y, endPos.x, endPos.y)
|
||||
canvasCtx.stroke()
|
||||
canvasCtx.draw(true)
|
||||
},
|
||||
canvasToTempFilePath(conf = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: this.cid,
|
||||
...conf,
|
||||
success: res => {
|
||||
resolve(res.tempFilePath)
|
||||
},
|
||||
fail: err => {
|
||||
console.log('fail', err)
|
||||
reject(err)
|
||||
}
|
||||
},
|
||||
this
|
||||
)
|
||||
})
|
||||
},
|
||||
setLineWidth(numberVal) {
|
||||
this.penLineWidth = numberVal
|
||||
},
|
||||
provideInterface() {
|
||||
return {
|
||||
cid: this.cid,
|
||||
ctx: canvasCtx,
|
||||
clear: this.clear,
|
||||
revoke: this.revoke,
|
||||
canvasToTempFilePath: this.canvasToTempFilePath,
|
||||
setLineWidth: this.setLineWidth
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 计算二次贝塞尔曲线 控制点 P1
|
||||
* 起点 P0(x0,y0)、控制点P1(x1, y1)、P2(x2, y2)、曲线上任意点B(x, y)
|
||||
* 二次贝塞尔公式:B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2
|
||||
* 代入坐标得:
|
||||
* x = (1-t)²*x0 + 2t(1-t)*x1 + t²*x2
|
||||
* y = (1-t)²*y0 + 2t(1-t)*y1 + t²*y2
|
||||
*/
|
||||
caculateBezier(P0, P2, B, t = 0.5) {
|
||||
const {
|
||||
x: x0,
|
||||
y: y0
|
||||
} = P0
|
||||
const {
|
||||
x: x2,
|
||||
y: y2
|
||||
} = P2
|
||||
const {
|
||||
x,
|
||||
y
|
||||
} = B
|
||||
let x1 = (x - (1 - t) * (1 - t) * x0 - t * t * x2) / (2 * t * (1 - t))
|
||||
let y1 = (y - (1 - t) * (1 - t) * y0 - t * t * y2) / (2 * t * (1 - t))
|
||||
return {
|
||||
x: x1,
|
||||
y: y1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.signature-wrap {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
15
pages.json
@ -9,7 +9,13 @@
|
||||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}, {
|
||||
},{
|
||||
"path": "pages/confirmCompletion/confirmCompletion",
|
||||
"style": {
|
||||
"navigationBarTitleText": "当前工单",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}, {
|
||||
"path": "pages/MyBenefits/MyBenefits",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的收益",
|
||||
@ -48,13 +54,6 @@
|
||||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/confirmCompletion/confirmCompletion",
|
||||
"style": {
|
||||
"navigationBarTitleText": "当前工单",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
}, {
|
||||
"path": "pages/taskReturn/taskReturn",
|
||||
"style": {
|
||||
|
||||
@ -101,15 +101,11 @@
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- <u-popup v-model="show" mode='bottom' :closeable='true' :safe-area-inset-bottom='true' @touchmove.stop.prevent
|
||||
height='900'>
|
||||
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop
|
||||
style='background-color: #F4F5F7;width: 100%;height: 800rpx;'>
|
||||
</signature>
|
||||
</u-popup> -->
|
||||
<u-mask :show="show" @click="show = false" @touchmove.stop.prevent>
|
||||
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop v-if='show'
|
||||
style='background-color: #F4F5F7;position:absolute;bottom:0%;width: 100%;height: 800rpx;'></signature>
|
||||
<u-mask :show="show" @click="show = false">
|
||||
<view style="position:absolute;bottom:0;height:800rpx;width:100%;background-color: #fff;" v-if='show'>
|
||||
<signature @userSignaturePictureUrl='userSignaturePicture' @click.native.stop
|
||||
style='background-color: #F4F5F7;width: 100%;height: 800rpx;'></signature>
|
||||
</view>
|
||||
</u-mask>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
@ -292,8 +288,10 @@
|
||||
.app {
|
||||
font-size: 36rpx;
|
||||
padding: 0;
|
||||
overflow: scroll;
|
||||
height: 100vh;
|
||||
|
||||
::v-deep .u-mask {
|
||||
height: 100vh !important;
|
||||
}
|
||||
|
||||
.finishmask {
|
||||
height: 300rpx;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="" style="background-color: #F4F5F7;">
|
||||
<Signature @init="onSignInit" style='background-color: #fff;'></Signature>
|
||||
<view class="signature" style="background-color: #F4F5F7;height: 400rpx;">
|
||||
<Signature @init="onSignInit" style='background-color: #fff;height: 700rpx;width: 100%;'></Signature>
|
||||
<view class="btns">
|
||||
<button @click="clear">清空</button>
|
||||
<button @click="revoke">撤回</button>
|
||||
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Signature from '@/components/v-sign/v-sign.vue'
|
||||
import Signature from '../v-sign/components/v-sign/v-sign.vue'
|
||||
export default {
|
||||
components: {
|
||||
Signature
|
||||
@ -37,6 +37,11 @@
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
.signature {
|
||||
height: 800rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btns {
|
||||
margin-top: 50rpx;
|
||||
display: flex;
|
||||
|
||||
48
pages/v-sign/changelog.md
Normal file
@ -0,0 +1,48 @@
|
||||
## 0.5.2(2022-06-10)
|
||||
- canvas 默认设置 disable-scroll 属性为true, 当在 canvas 中移动时且有绑定手势事件时,禁止屏幕滚动以及下拉刷新
|
||||
## 0.5.1(2022-05-27)
|
||||
- 取消属性 width, height 默认值
|
||||
## 0.5.0(2022-05-27)
|
||||
- 新增 v-sgin 组件获取坐标信息数组方法 `getLineData`
|
||||
- 新增 v-sign 组件事件触发 @clear, @revoke, @end
|
||||
- 修复撤销操作重设背景色导致线条消失问题
|
||||
## 0.4.0(2022-05-15)
|
||||
- 新增属性 bgColor 设置画布背景色,修复导出图片无背景色问题
|
||||
## 0.3.4(2022-03-30)
|
||||
修复 v-sign-action 子组件 svg 加载报错
|
||||
## 0.3.3(2022-03-30)
|
||||
修复 v-sign-action 子组件 svg 加载报错
|
||||
## 0.3.2(2022-01-12)
|
||||
- 修复多组件共存绘制问题
|
||||
## 0.3.1(2021-12-14)
|
||||
- 【新增】保存 png 图片方法 saveImage;
|
||||
- 【修改】 控件子组件保存按钮动作由保存为临时文件路径改为保存图片
|
||||
## 0.3.0(2021-12-11)
|
||||
- 添加颜色选择器组件
|
||||
## 0.2.0(2021-12-10)
|
||||
- 修改为 uni_modules 规范
|
||||
## 0.1.7 (2021-12-09)
|
||||
|
||||
- 画笔组件优化
|
||||
|
||||
## 0.1.6 (2021-12-08)
|
||||
|
||||
- 画笔样式优化
|
||||
|
||||
## 0.1.5 (2021-12-08)
|
||||
|
||||
- 画笔子组件样式修改,支持circle、line 两种样式
|
||||
|
||||
## 0.1.4 (2021-12-08)
|
||||
|
||||
- 添加画笔子组件
|
||||
|
||||
## 0.1.3 (2021-12-02)
|
||||
|
||||
- 添加按钮控件子组件
|
||||
|
||||
## 0.1.0 (2021-11-28)
|
||||
|
||||
- 支持线宽、颜色自定义,自定义画布样式
|
||||
- 支持画布清空、撤回、保存图片
|
||||
- 事件 init 暴露清空、撤回、保存图片方法
|
||||
@ -7,7 +7,7 @@
|
||||
:style="[{ 'margin-right': formatSize(space) }]"
|
||||
@click="onBtnClick(item)"
|
||||
>
|
||||
<image :class="['icon', 'icon-' + item.action]" :src="item.icon"></image>
|
||||
<image v-if="item.icon" :class="['icon', 'icon-' + item.action]" :src="item.icon"></image>
|
||||
<text class="text">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@ -25,7 +25,7 @@
|
||||
* @event {Function} 点击对应类型按钮触发对应事件, 例如点击 clear 则触发 clear 事件
|
||||
* @example 示例
|
||||
**/
|
||||
import { formatSize } from './utils'
|
||||
import { formatSize } from '../../utils'
|
||||
|
||||
// v-sign 父组件提供的接口
|
||||
let vSignInterface
|
||||
@ -41,22 +41,22 @@ const btnsConf = [
|
||||
{
|
||||
label: '清空',
|
||||
action: btn_type.CLEAR,
|
||||
icon: '/static/v-sign/clear.png'
|
||||
icon: require('../../static/image/clear.svg')
|
||||
},
|
||||
{
|
||||
label: '撤回',
|
||||
action: btn_type.PREV,
|
||||
icon: '/static/v-sign/prev.png'
|
||||
icon: require('../../static/image/prev.svg')
|
||||
},
|
||||
// {
|
||||
// label: '取消撤回',
|
||||
// action: btn_type.NEXT,
|
||||
// icon: '/static/v-sign/next.png'
|
||||
// icon: '/uni_modules/v-sign/static/image/next.svg'
|
||||
// },
|
||||
{
|
||||
label: '保存',
|
||||
action: btn_type.SAVE,
|
||||
icon: '/static/v-sign/save.png'
|
||||
icon: require('../../static/image/save.svg')
|
||||
}
|
||||
]
|
||||
export default {
|
||||
@ -83,7 +83,7 @@ export default {
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
inject: ['getInterface'],
|
||||
inject: ['getSignInterface'],
|
||||
data() {
|
||||
return {
|
||||
formatSize
|
||||
@ -95,11 +95,10 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
vSignInterface = this.getInterface()
|
||||
vSignInterface = this.getSignInterface()
|
||||
},
|
||||
methods: {
|
||||
async onBtnClick(btn) {
|
||||
// console.log(btn, btn.action)
|
||||
let emit_result
|
||||
switch (btn.action) {
|
||||
case btn_type.CLEAR:
|
||||
@ -112,7 +111,7 @@ export default {
|
||||
// console.log('next')
|
||||
// break
|
||||
case btn_type.SAVE:
|
||||
emit_result = await vSignInterface.canvasToTempFilePath()
|
||||
emit_result = await vSignInterface.saveImage()
|
||||
break
|
||||
default:
|
||||
break
|
||||
@ -138,8 +137,12 @@ export default {
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&:active {
|
||||
transition: transform 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
&.border {
|
||||
border: 2rpx solid #666;
|
||||
border: 2rpx solid #333;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.icon {
|
||||
@ -154,7 +157,7 @@ export default {
|
||||
}
|
||||
}
|
||||
.text {
|
||||
color: #666;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
212
pages/v-sign/components/v-sign-color/v-sign-color.vue
Normal file
@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<view class="v-sign-color">
|
||||
<view class="options">
|
||||
<view
|
||||
class="option-item"
|
||||
v-for="(color, index) in options"
|
||||
:key="color"
|
||||
:style="[optionStyle(color, index)]"
|
||||
@click="onSelect(color, index)"
|
||||
>
|
||||
<view class="option-inner" :style="[optionInnerStyle(color, index)]">
|
||||
{{ tick && currentIndex === index ? '✓' : '' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* v-sign-color 颜色选择器(v-sign 子组件)
|
||||
* @description 设置 v-sign 画笔的颜色
|
||||
* @tutorial
|
||||
* @property {String} type 选项样式,可选 SQUARE \ CIRCLE
|
||||
* @property {String} color 默认颜色
|
||||
* @property {Array} options 备选色
|
||||
* @property {Number/String} size 圆/方形大小
|
||||
* @property {Boolean} tick 是否选中打勾
|
||||
* @property {Number/String} tickSize 勾大小
|
||||
* @property {String} borderColor 边框颜色
|
||||
* @property {Boolean} border 是否有边框
|
||||
* @property {Number/String} space 选项间隙
|
||||
* @event {Function} change 改变颜色时触发
|
||||
* @example 示例
|
||||
**/
|
||||
import { formatSize } from '../../utils'
|
||||
// 选项样式
|
||||
const type_style = {
|
||||
CIRCLE: 'circle',
|
||||
SQUARE: 'square'
|
||||
}
|
||||
// v-sign 父组件提供的接口
|
||||
let vSignInterface
|
||||
|
||||
export default {
|
||||
name: 'v-sign-color',
|
||||
props: {
|
||||
// 选项样式
|
||||
type: {
|
||||
type: String,
|
||||
default: type_style.SQUARE
|
||||
},
|
||||
// 默认色,需在备选中
|
||||
color: {
|
||||
type: String
|
||||
},
|
||||
// 备选色
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => ['#333', '#f44236', '#3f51b5', '#2195f3', '#ffeb3b', '#ff9900']
|
||||
},
|
||||
// 圆/方形大小
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 44
|
||||
},
|
||||
// 是否选中打勾
|
||||
tick: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 勾大小
|
||||
tickSize: {
|
||||
type: [Number, String],
|
||||
default: 24
|
||||
},
|
||||
// 边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
// 是否有边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 选项间隙
|
||||
space: {
|
||||
type: [Number, String],
|
||||
default: 16
|
||||
}
|
||||
},
|
||||
inject: ['getSignInterface'],
|
||||
data() {
|
||||
return {
|
||||
formatSize,
|
||||
currentIndex: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
color: {
|
||||
handler(val) {
|
||||
if (!val) {
|
||||
this.currentIndex = 0
|
||||
return
|
||||
}
|
||||
const index = this.options.findIndex(item => item === this.color)
|
||||
if (index > 0) {
|
||||
this.currentIndex = index
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentColor() {
|
||||
return this.options[this.currentIndex]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
vSignInterface = this.getSignInterface()
|
||||
this.setLineColor()
|
||||
},
|
||||
methods: {
|
||||
onSelect(color, index) {
|
||||
this.currentIndex = index
|
||||
this.$emit('change', color)
|
||||
this.setLineColor()
|
||||
},
|
||||
setLineColor() {
|
||||
vSignInterface.setLineColor(this.currentColor)
|
||||
},
|
||||
optionStyle(color, index) {
|
||||
const size = formatSize(this.size)
|
||||
let style = {
|
||||
marginRight: formatSize(this.space),
|
||||
width: size,
|
||||
height: size,
|
||||
lineHeight: size,
|
||||
border:
|
||||
this.border && this.currentIndex === index
|
||||
? `4rpx solid ${this.borderColor}`
|
||||
: '',
|
||||
padding: this.currentIndex === index ? 0 : this.border ? '4rpx' : ''
|
||||
}
|
||||
switch (this.type) {
|
||||
case type_style.SQUARE:
|
||||
style = {
|
||||
...style,
|
||||
borderRadius: this.currentIndex === index ? '8rpx' : 0
|
||||
}
|
||||
break
|
||||
case type_style.CIRCLE:
|
||||
style = {
|
||||
...style,
|
||||
borderRadius: '50%'
|
||||
}
|
||||
break
|
||||
}
|
||||
return style
|
||||
},
|
||||
optionInnerStyle(color, index) {
|
||||
let style = {
|
||||
background: color,
|
||||
fontSize: formatSize(this.tickSize)
|
||||
}
|
||||
switch (this.type) {
|
||||
case type_style.SQUARE:
|
||||
style = {
|
||||
...style,
|
||||
borderRadius: this.currentIndex === index ? 0 : '8rpx'
|
||||
}
|
||||
break
|
||||
case type_style.CIRCLE:
|
||||
style = {
|
||||
...style,
|
||||
borderRadius: '50%'
|
||||
}
|
||||
break
|
||||
}
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.options {
|
||||
padding: 8rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.option-item {
|
||||
overflow: hidden;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&:active {
|
||||
transition: transform 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.option-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
@ -31,7 +31,7 @@
|
||||
<script>
|
||||
/**
|
||||
* v-sign-pen 画笔(v-sign 子组件)
|
||||
* @description 控制 v-sign 画笔的线宽
|
||||
* @description 设置 v-sign 画笔的线宽
|
||||
* @tutorial
|
||||
* @property {String} type 选项样式 line / circle
|
||||
* @property {String} label 标签
|
||||
@ -105,7 +105,7 @@ export default {
|
||||
default: 4
|
||||
}
|
||||
},
|
||||
inject: ['getInterface'],
|
||||
inject: ['getSignInterface'],
|
||||
data() {
|
||||
return {
|
||||
type_style,
|
||||
@ -142,7 +142,7 @@ export default {
|
||||
this.currentSelect = this.csizes[0]
|
||||
},
|
||||
mounted() {
|
||||
vSignInterface = this.getInterface()
|
||||
vSignInterface = this.getSignInterface()
|
||||
this.setLineWidth()
|
||||
},
|
||||
methods: {
|
||||
@ -195,6 +195,10 @@ export default {
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&:active {
|
||||
transition: transform 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
padding: 4rpx;
|
||||
314
pages/v-sign/components/v-sign/v-sign.vue
Normal file
@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<view class="signature-wrap">
|
||||
<canvas
|
||||
:canvas-id="cid"
|
||||
:id="cid"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
disable-scroll
|
||||
style="width: 100%;height: 600rpx;"
|
||||
></canvas>
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* sign canvas 手写签名
|
||||
* @description 设置线条宽度、颜色,撤回,清空
|
||||
* @tutorial
|
||||
* @property {String} cid canvas id 不设置则默认为 v-sign-时间戳
|
||||
* @property {String, Number} width canvas 宽度
|
||||
* @property {String, Number} height canvas 高度
|
||||
* @property {bgColor} bgColor 画布背景颜色
|
||||
* @property {Object} customStyle canvas 自定义样式
|
||||
* @property {String} lineWidth 画笔大小,权重小于 v-sign-pen 组件设置的画笔大小
|
||||
* @property {Number} lineColor 画笔颜色,权重小于 v-sign-pen 组件设置的画笔大小
|
||||
* @event {Function} init 当创建完 canvas 实例后触发,向外提供 canvas实例,撤回,清空方法
|
||||
* @example <v-sign @init="signInit"></v-sign>
|
||||
*/
|
||||
import { formatSize } from '../../utils'
|
||||
|
||||
export default {
|
||||
name: 'v-sign',
|
||||
props: {
|
||||
// canvas id
|
||||
cid: {
|
||||
type: String,
|
||||
default: `v-sign-${Date.now()}`
|
||||
// required: true
|
||||
},
|
||||
// canvas 宽度
|
||||
width: {
|
||||
type: [String, Number]
|
||||
},
|
||||
// canvas 高度
|
||||
height: {
|
||||
type: [String, Number]
|
||||
},
|
||||
// 画笔大小,权重小于 v-sign-pen 组件设置的画笔大小 penLineWidth
|
||||
lineWidth: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
// 线颜色,权重小于 v-sign-color 组件设置的画笔颜色 penLineColor
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
// 画布背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
// canvas自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
getSignInterface: this.provideSignInterface
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatSize,
|
||||
lineData: [],
|
||||
winWidth: 0,
|
||||
winHeight: 0,
|
||||
penLineWidth: null, // v-sign-pen 组件设置的画笔大小
|
||||
penLineColor: null // v-sign-color 组件设置的颜色
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 获取窗口宽高
|
||||
const { windowWidth, windowHeight } = uni.getSystemInfoSync()
|
||||
this.winWidth = windowWidth
|
||||
this.winHeight = windowHeight
|
||||
},
|
||||
mounted() {
|
||||
this.canvasCtx = uni.createCanvasContext(this.cid, this)
|
||||
// h5 需延迟绘制,否则绘制失败
|
||||
// #ifdef H5
|
||||
setTimeout(() => {
|
||||
// #endif
|
||||
this.setBackgroundColor(this.bgColor)
|
||||
// #ifdef H5
|
||||
}, 10)
|
||||
// #endif
|
||||
// 初始化完成,触发 init 事件
|
||||
this.$emit('init', this.provideSignInterface())
|
||||
},
|
||||
methods: {
|
||||
onTouchStart(e) {
|
||||
const pos = e.touches[0]
|
||||
this.lineData.push({
|
||||
style: {
|
||||
color: this.penLineColor || this.lineColor,
|
||||
width: this.penLineWidth || this.lineWidth
|
||||
},
|
||||
// 屏幕坐标
|
||||
coordinates: [
|
||||
{
|
||||
type: e.type,
|
||||
x: pos.x,
|
||||
y: pos.y
|
||||
}
|
||||
]
|
||||
})
|
||||
this.drawLine()
|
||||
},
|
||||
onTouchMove(e) {
|
||||
const pos = e.touches[0]
|
||||
this.lineData[this.lineData.length - 1].coordinates.push({
|
||||
type: e.type,
|
||||
x: pos.x,
|
||||
y: pos.y
|
||||
})
|
||||
this.drawLine()
|
||||
},
|
||||
onTouchEnd(e) {
|
||||
this.$emit('end', this.lineData)
|
||||
},
|
||||
// 清空画布
|
||||
clear() {
|
||||
this.lineData = []
|
||||
this.canvasCtx.clearRect(0, 0, this.winWidth, this.winHeight)
|
||||
this.canvasCtx.draw()
|
||||
this.setBackgroundColor(this.bgColor)
|
||||
this.$emit('clear')
|
||||
},
|
||||
// 撤销
|
||||
revoke() {
|
||||
this.setBackgroundColor(this.bgColor)
|
||||
this.lineData.pop()
|
||||
this.lineData.forEach((item, index) => {
|
||||
this.canvasCtx.beginPath()
|
||||
this.canvasCtx.setLineCap('round')
|
||||
this.canvasCtx.setStrokeStyle(item.style.color)
|
||||
this.canvasCtx.setLineWidth(item.style.width)
|
||||
if (item.coordinates.length < 2) {
|
||||
const pos = item.coordinates[0]
|
||||
this.canvasCtx.moveTo(pos.x, pos.y)
|
||||
this.canvasCtx.lineTo(pos.x + 1, pos.y)
|
||||
} else {
|
||||
item.coordinates.forEach(pos => {
|
||||
if (pos.type == 'touchstart') {
|
||||
this.canvasCtx.moveTo(pos.x, pos.y)
|
||||
} else {
|
||||
this.canvasCtx.lineTo(pos.x, pos.y)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.canvasCtx.stroke()
|
||||
})
|
||||
this.canvasCtx.draw(true)
|
||||
this.$emit('revoke', this.lineData)
|
||||
},
|
||||
// 绘制线条
|
||||
drawLine() {
|
||||
const lineDataLen = this.lineData.length
|
||||
if (!lineDataLen) return
|
||||
const currentLineData = this.lineData[lineDataLen - 1]
|
||||
const coordinates = currentLineData.coordinates
|
||||
const coordinatesLen = coordinates.length
|
||||
if (!coordinatesLen) return
|
||||
let startPos
|
||||
let endPos
|
||||
if (coordinatesLen < 2) {
|
||||
// only start, no move event
|
||||
startPos = coordinates[coordinatesLen - 1]
|
||||
endPos = {
|
||||
x: startPos.x + 1,
|
||||
y: startPos.y
|
||||
}
|
||||
} else {
|
||||
startPos = coordinates[coordinatesLen - 2]
|
||||
endPos = coordinates[coordinatesLen - 1]
|
||||
}
|
||||
|
||||
const style = currentLineData.style
|
||||
this.canvasCtx.beginPath()
|
||||
this.canvasCtx.setLineCap('round')
|
||||
this.canvasCtx.setStrokeStyle(style.color)
|
||||
this.canvasCtx.setLineWidth(style.width)
|
||||
this.canvasCtx.moveTo(startPos.x, startPos.y)
|
||||
this.canvasCtx.lineTo(endPos.x, endPos.y)
|
||||
// const P1 = this.caculateBezier(startPos, endPos, centerPos)
|
||||
// console.log(P1.x, P1.y)
|
||||
// this.canvasCtx.moveTo(startPos.x, startPos.y)
|
||||
// this.canvasCtx.quadraticCurveTo(P1.x, P1.y, endPos.x, endPos.y)
|
||||
this.canvasCtx.stroke()
|
||||
this.canvasCtx.draw(true)
|
||||
},
|
||||
// 保存png图片,文件名配置 filename 仅支持 h5
|
||||
async saveImage(filename = '签名') {
|
||||
const tempFilePath = await this.canvasToTempFilePath()
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
try {
|
||||
const a = document.createElement('a')
|
||||
a.href = tempFilePath
|
||||
a.download = filename
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
resolve({
|
||||
errMsg: 'saveImageH5:ok'
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
reject(e)
|
||||
}
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: tempFilePath,
|
||||
success(resObj) {
|
||||
resolve(resObj)
|
||||
},
|
||||
fail(err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
// canvas 保存为临时图片路径,h5返回 base64
|
||||
canvasToTempFilePath(conf = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.canvasToTempFilePath(
|
||||
{
|
||||
canvasId: this.cid,
|
||||
...conf,
|
||||
success: res => {
|
||||
resolve(res.tempFilePath)
|
||||
},
|
||||
fail: err => {
|
||||
console.log('fail', err)
|
||||
reject(err)
|
||||
}
|
||||
},
|
||||
this
|
||||
)
|
||||
})
|
||||
},
|
||||
setBackgroundColor(color = '#fff') {
|
||||
this.canvasCtx.beginPath()
|
||||
this.canvasCtx.setFillStyle(color)
|
||||
this.canvasCtx.fillRect(0, 0, this.winWidth, this.winHeight)
|
||||
this.canvasCtx.fill()
|
||||
this.canvasCtx.draw(true)
|
||||
},
|
||||
setLineWidth(numberVal) {
|
||||
this.penLineWidth = numberVal
|
||||
},
|
||||
setLineColor(strValue) {
|
||||
this.penLineColor = strValue
|
||||
},
|
||||
// 向外暴露内部方法
|
||||
provideSignInterface() {
|
||||
return {
|
||||
cid: this.cid,
|
||||
ctx: this.canvasCtx,
|
||||
clear: this.clear,
|
||||
revoke: this.revoke,
|
||||
saveImage: this.saveImage,
|
||||
canvasToTempFilePath: this.canvasToTempFilePath,
|
||||
setLineWidth: this.setLineWidth,
|
||||
setLineColor: this.setLineColor,
|
||||
setBackgroundColor: this.setBackgroundColor,
|
||||
getLineData: () => this.lineData
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 计算二次贝塞尔曲线 控制点 P1
|
||||
* 起点 P0(x0,y0)、控制点P1(x1, y1)、P2(x2, y2)、曲线上任意点B(x, y)
|
||||
* 二次贝塞尔公式:B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2
|
||||
* 代入坐标得:
|
||||
* x = (1-t)²*x0 + 2t(1-t)*x1 + t²*x2
|
||||
* y = (1-t)²*y0 + 2t(1-t)*y1 + t²*y2
|
||||
*/
|
||||
caculateBezier(P0, P2, B, t = 0.5) {
|
||||
const { x: x0, y: y0 } = P0
|
||||
const { x: x2, y: y2 } = P2
|
||||
const { x, y } = B
|
||||
let x1 = (x - (1 - t) * (1 - t) * x0 - t * t * x2) / (2 * t * (1 - t))
|
||||
let y1 = (y - (1 - t) * (1 - t) * y0 - t * t * y2) / (2 * t * (1 - t))
|
||||
return {
|
||||
x: x1,
|
||||
y: y1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.signature-wrap {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
83
pages/v-sign/package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "v-sign",
|
||||
"displayName": "canvas 手写 签名 签字 画板组件",
|
||||
"version": "0.5.2",
|
||||
"description": "基于 canvas 实现;1. 支持线条、背景色自定义样式;2. 支持撤回、清空、导出图片等功能;3. 内置按钮、画笔等子组件。",
|
||||
"keywords": [
|
||||
"canvas",
|
||||
"签名",
|
||||
"签字",
|
||||
"电子签名",
|
||||
"signature"
|
||||
],
|
||||
"repository": "https://github.com/jizai1125/v-sign",
|
||||
"engines": {
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "n"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "u",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
245
pages/v-sign/readme.md
Normal file
@ -0,0 +1,245 @@
|
||||
# v-sign 手写签名
|
||||
|
||||
**如有问题或者建议,欢迎留言或加群联系我(群号:736123963)!!!将保持维护!!!**
|
||||
|
||||
<img src="https://i.loli.net/2021/12/02/bgsfnDmCzXGq8ct.png" alt="uniapp 交流群群聊二维码" style="zoom: 80%;" />
|
||||
|
||||
## 快速使用
|
||||
|
||||
基础示例,具体说明见下方 API,**若需要使用内置子组件,见下方子组件说明。**
|
||||
|
||||
```html
|
||||
<template>
|
||||
<v-sign :width="winWidth + 'px'" @init="onSignInit"></v-sign>
|
||||
<button @click="clear">清空<button>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
winWidth: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 宽度设为可使用窗口宽度
|
||||
const { windowWidth } = uni.getSystemInfoSync()
|
||||
this.winWidth = windowWidth
|
||||
},
|
||||
methods: {
|
||||
onSignInit(signCtx) {
|
||||
this.signCtx = signCtx
|
||||
},
|
||||
// 清空
|
||||
clear() {
|
||||
this.signCtx.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### 属性 (Props)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| :---------: | :-----------: | :-----------: | :--------------------------: |
|
||||
| cid | String | v-sign-时间戳 | canvas id |
|
||||
| width | String/Number | - | canvas 宽度,Number 单位 rpx |
|
||||
| height | String/Number | - | canvas 高度,Number 单位 rpx |
|
||||
| customStyle | Object | - | canvas 自定义样式 |
|
||||
| lineWidth | Number | 4 | 线宽,单位 px |
|
||||
| lineColor | String | #333 | 线颜色 |
|
||||
| bgColor | String | #fff | 画布背景颜色 |
|
||||
|
||||
### 事件(Events)
|
||||
|
||||
| 事件称名 | 说明 | 返回值 |
|
||||
| :------: | :------------------------------------------------------------: | :--------------------------------: |
|
||||
| @init | 创建完 canvas 实例后触发,向外提供 canvas 实例,撤回,清空方法 | Object:具体见下方事件回调参数说明 |
|
||||
| @clear | 清空画布后触发 | - |
|
||||
| @revoke | 撤销操作后触发 | 坐标信息数组 |
|
||||
| @end | 每次绘制结束后触发 | 坐标信息数组 |
|
||||
|
||||
### 事件回调参数说明
|
||||
|
||||
#### **`init(ctx: SignContext)`**
|
||||
|
||||
可以通过该事件回调暴露的 clear、revoke 等方法操作画布。
|
||||
|
||||
```java
|
||||
interface SignContext {
|
||||
// canvas 实例
|
||||
ctx: object;
|
||||
// 清空画布
|
||||
clear(): void;
|
||||
// 撤回
|
||||
revoke(): void;
|
||||
// 保存 png 图片,文件名 filename 配置仅支持 h5
|
||||
saveImage(filename: string): Promise<object>;
|
||||
// 返回图片临时文件路径,config 参数同 uni.canvasToTempFilePath方法,内部只是做了 Promise 化处理而已
|
||||
canvasToTempFilePath(config: object): Promise<object>;
|
||||
// 设置画布背景色
|
||||
setBackgroundColor(color: string): void;
|
||||
setLineWidth(value: number): void;
|
||||
setLineColor(value: string): void;
|
||||
// 获取坐标信息数组
|
||||
getLineData(): Array<object>;
|
||||
}
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<v-sign @init="onSignInit"></v-sign>
|
||||
<button @click="clear">清空<button>
|
||||
<button @click="revoke">撤回<button>
|
||||
<button @click="saveTempFilePath">保存临时图片路径<button>
|
||||
<button @click="saveImage">保存图片<button>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
onSignInit(signCtx) {
|
||||
this.signCtx = signCtx
|
||||
},
|
||||
// 清空
|
||||
clear() {
|
||||
this.signCtx.clear()
|
||||
},
|
||||
// 撤回
|
||||
revoke() {
|
||||
this.signCtx.revoke()
|
||||
},
|
||||
// 保存为临时图片路径,h5返回 base64
|
||||
saveTempFilePath() {
|
||||
this.signCtx.canvasToTempFilePath()
|
||||
},
|
||||
// 保存 png 图片
|
||||
saveImage() {
|
||||
this.signCtx.saveImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
# 子组件
|
||||
|
||||
子组件需要包裹在 `v-sign` 组件内使用!!!
|
||||
|
||||
## 按钮控件(v-sign-action)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<template>
|
||||
<v-sign>
|
||||
<v-sign-action @save="save" @clear="clear" @prev="revoke"></v-sign-action>
|
||||
</v-sign>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
save(tempFilePath) {
|
||||
console.log(tempFilePath);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
### 属性 (Props)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| :---------: | :-----------: | :-----------------------: | :----------------------------------------------------: |
|
||||
| actions | Array | ["clear", "prev", "save"] | 按钮配置;清空(clear), 撤回(prev) 保存图片(save) |
|
||||
| border | Boolean | true | 按钮是否有边框 |
|
||||
| space | String/Number | 12 | 按钮间隔,Number 单位 rpx |
|
||||
| customStyle | Object | - | 根元素自定义样式 |
|
||||
|
||||
### 事件(Events)
|
||||
|
||||
点击对应类型按钮触发对应事件, 例如:配置了清空(clear)按钮,点击则触发 clear 事件。
|
||||
|
||||
---
|
||||
|
||||
## 画笔组件(v-sign-pen)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<template>
|
||||
<v-sign>
|
||||
<v-sign-pen></v-sign-pen>
|
||||
</v-sign>
|
||||
</template>
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
### 属性 (Props)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| :---------: | :-----: | :--------------: | :--------------------------: |
|
||||
| type | String | circle | 选项样式,可选 circle \ line |
|
||||
| label | String | - | 标签 |
|
||||
| sizes | Array | [2, 4, 6, 8, 10] | 画笔尺寸数组,单位 px |
|
||||
| color | String | #333 | 选项颜色 |
|
||||
| activeColor | String | #333 | 选中项颜色 |
|
||||
| border | Boolean | true | 选中项是否有边框 |
|
||||
| borderWidth | Number | 4 | 边框大小,单位 rpx |
|
||||
| space | Number | 20 | 选项间隙,单位 rpx |
|
||||
| bigger | Number | 2 | 圆点变大变粗倍数 |
|
||||
| minSize | Number | 4 | 圆点最小尺寸,单位 px |
|
||||
|
||||
### 事件(Events)
|
||||
|
||||
| 事件称名 | 说明 | 返回值 |
|
||||
| :------: | :----------------: | :----------------: |
|
||||
| @change | 选择画笔大小时触发 | size:画笔尺寸大小 |
|
||||
|
||||
---
|
||||
|
||||
## 颜色选择器组件(v-sign-color)
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
<template>
|
||||
<v-sign>
|
||||
<v-sign-color></v-sign-color>
|
||||
</v-sign>
|
||||
</template>
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
### 属性 (Props)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| :---------: | :-----------: | :-------------------------------------------------------------: | :-------------------: |
|
||||
| type | String | square | 选项样式,可选 circle |
|
||||
| color | String | - | 默认颜色 |
|
||||
| options | Array | ['#333', '#f44236', '#3f51b5', '#2195f3', '#ffeb3b', '#ff9900'] | 备选色 |
|
||||
| size | Number/String | 44 | 圆/方形大小,单位 rpx |
|
||||
| tick | Boolean | true | 是否选中打勾 |
|
||||
| tickSize | Number/String | 24 | 勾大小 |
|
||||
| borderColor | String | #fff | 边框颜色 |
|
||||
| border | Boolean | false | 是否有边框 |
|
||||
| space | Number/String | 16 | 选项间隙 |
|
||||
|
||||
### 事件(Events)
|
||||
|
||||
| 事件称名 | 说明 | 返回值 |
|
||||
| :------: | :------------: | :----: |
|
||||
| @change | 选择颜色时触发 | color |
|
||||
BIN
pages/v-sign/static/image/clear.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
1
pages/v-sign/static/image/clear.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214313546" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="827" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M274.56 798.997333l19.434667-25.130666-33.792 68.565333a18.133333 18.133333 0 0 0 11.562666 25.536l59.733334 16a18.133333 18.133333 0 0 0 17.28-4.48c20.522667-19.818667 35.626667-35.989333 45.290666-48.469333l19.456-25.130667-33.813333 68.565333a18.133333 18.133333 0 0 0 11.562667 25.536l84.48 22.634667a18.133333 18.133333 0 0 0 17.28-4.48c20.522667-19.84 35.626667-35.989333 45.269333-48.469333l19.456-25.130667-33.813333 68.565333A18.133333 18.133333 0 0 0 535.530667 938.666667l72.106666 19.328a18.133333 18.133333 0 0 0 17.28-4.48c20.522667-19.84 35.626667-36.010667 45.269334-48.490667l19.456-25.130667-33.813334 68.586667a18.133333 18.133333 0 0 0 11.584 25.514667l86.421334 23.338666 3.84-0.213333c13.269333-0.704 29.056-5.034667 43.84-12.8 29.781333-15.701333 48.170667-43.2 52.181333-78.250667 2.133333-18.517333 4.778667-38.549333 8.405333-63.530666 1.642667-11.221333 2.944-20.010667 6.229334-41.834667 11.050667-73.322667 14.634667-101.034667 17.130666-133.674667l0.938667-12.373333 2.837333-2.922667 12.330667-1.344a41.813333 41.813333 0 0 0 24.810667-11.221333c10.730667-10.24 14.805333-25.386667 11.093333-42.197333l-37.546667-171.584c-3.029333-13.696-11.264-27.946667-23.146666-39.829334-11.648-11.626667-25.92-20.138667-39.893334-23.893333L723.626667 331.306667l-2.261334-3.925334L774.250667 130.133333c8.32-31.061333-11.754667-63.744-44.970667-72.64l-79.509333-21.312c-33.194667-8.896-66.922667 9.365333-75.264 40.426667l-52.842667 197.269333-3.925333 2.261334-118.101334-31.637334c-13.994667-3.754667-30.634667-3.498667-46.506666 0.746667-16.256 4.352-30.506667 12.586667-39.957334 22.933333l-118.314666 129.792c-11.605333 12.714667-15.658667 27.84-11.52 42.090667 4.16 14.229333 15.850667 25.194667 32.896 30.528l13.610666 4.266667 2.133334 3.882666-3.626667 13.802667c-21.12 79.850667-52.885333 136.917333-85.717333 150.890667-47.530667 20.202667-72.938667 49.429333-78.421334 85.034666-5.034667 32.682667 9.28 67.114667 37.589334 91.541334l22.037333 8.341333 74.666667 20.010667a42.666667 42.666667 0 0 0 41.216-11.050667c15.274667-15.274667 26.88-28.032 34.837333-38.293333z m551.381333-396.565333c14.144 3.797333 29.952 19.2 32.768 32l34.56 157.781333a10.666667 10.666667 0 0 1-13.184 12.586667L240.64 433.493333a10.666667 10.666667 0 0 1-5.12-17.493333l108.8-119.36c8.832-9.685333 30.229333-15.146667 44.373333-11.349333l141.333334 37.866666a21.333333 21.333333 0 0 0 26.133333-15.082666l58.304-217.642667a21.333333 21.333333 0 0 1 26.133333-15.082667l77.056 20.650667a21.333333 21.333333 0 0 1 15.082667 26.133333l-58.325333 217.642667a21.333333 21.333333 0 0 0 15.082666 26.112l136.448 36.565333zM315.456 701.568c-33.664 45.141333-64.597333 79.082667-92.8 101.802667l-5.909333 4.778666-2.837334 0.597334-88.106666-24.106667-2.922667-3.2c-13.034667-14.165333-19.370667-31.04-16.981333-46.592 3.285333-21.333333 22.058667-39.338667 53.205333-52.586667 31.722667-13.482667 59.818667-47.104 82.922667-99.904 10.026667-22.954667 18.88-48.725333 26.389333-76.586666l3.882667-14.4 3.904-2.261334 566.165333 151.701334 2.346667 3.306666-0.789334 12.224c-1.984 30.592-30.336 229.397333-32.128 244.906667-2.346667 20.416-11.306667 34.986667-27.605333 44.394667a73.237333 73.237333 0 0 1-21.397333 8.106666l-5.013334 0.725334-60.373333-16.170667 11.242667-20.288c8.277333-14.976 22.656-43.84 43.093333-86.613333a21.12 21.12 0 0 0-9.962667-28.16l-3.136-1.493334a21.333333 21.333333 0 0 0-26.261333 6.485334c-33.642667 45.056-64.533333 78.912-92.672 101.546666l-5.909333 4.757334-2.837334 0.597333-52.544-14.08 11.114667-20.266667c3.562667-6.485333 7.04-13.013333 10.453333-19.626666 7.04-13.504 17.898667-35.797333 32.597334-66.816a21.290667 21.290667 0 0 0-9.984-28.309334l-3.029334-1.450666a21.333333 21.333333 0 0 0-26.368 6.442666c-33.6 45.013333-64.469333 78.826667-92.608 101.482667l-5.909333 4.757333-2.837333 0.597334-52.138667-13.973334 11.114667-20.266666c3.242667-5.888 6.72-12.416 10.453333-19.626667 6.997333-13.461333 17.962667-35.946667 32.896-67.434667a20.970667 20.970667 0 0 0-10.112-28.010666l-3.328-1.536a21.333333 21.333333 0 0 0-26.069333 6.613333c-33.642667 45.056-64.554667 78.976-92.778667 101.696l-5.909333 4.757333-2.837334 0.597334-32.64-8.746667 11.093334-20.245333c3.541333-6.506667 7.04-13.034667 10.453333-19.626667 6.976-13.482667 17.941333-35.968 32.874667-67.456a21.056 21.056 0 0 0-10.069334-28.074667l-3.242666-1.514666a21.333333 21.333333 0 0 0-26.154667 6.549333z" fill="#333333" p-id="828"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
BIN
pages/v-sign/static/image/next.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
1
pages/v-sign/static/image/next.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214327673" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1119" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M588.8 934.4c-17.066667-4.266667-25.6-21.333333-25.6-38.4v-204.8c-68.266667-8.533333-128-4.266667-192 12.8-76.8 21.333333-170.666667 93.866667-273.066667 213.333333-12.8 17.066667-34.133333 21.333333-51.2 12.8-17.066667-8.533333-29.866667-25.6-25.6-42.666666 17.066667-162.133333 76.8-298.666667 187.733334-405.333334 98.133333-98.133333 213.333333-153.6 349.866666-166.4V128c0-17.066667 8.533333-34.133333 25.6-38.4 17.066667-8.533333 34.133333-4.266667 46.933334 8.533333l358.4 375.466667c17.066667 17.066667 17.066667 42.666667 0 59.733333l-358.4 392.533334c-8.533333 8.533333-21.333333 12.8-29.866667 12.8-4.266667 0-8.533333 0-12.8-4.266667z m55.466667-699.733333v123.733333c0 21.333333-17.066667 42.666667-42.666667 42.666667-132.266667 0-243.2 46.933333-337.066667 140.8-59.733333 59.733333-102.4 128-128 204.8 72.533333-68.266667 145.066667-110.933333 209.066667-128 85.333333-21.333333 166.4-25.6 260.266667-8.533334 21.333333 4.266667 34.133333 21.333333 34.133333 42.666667v132.266667l256-281.6-251.733333-268.8z" fill="#333333" p-id="1120"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
pages/v-sign/static/image/prev.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
1
pages/v-sign/static/image/prev.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214320974" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="973" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M422.4 938.666667c-12.8 0-21.333333-4.266667-29.866667-12.8L34.133333 533.333333c-17.066667-17.066667-12.8-42.666667 0-59.733333l358.4-375.466667c8.533333-12.8 25.6-17.066667 42.666667-8.533333s25.6 21.333333 25.6 38.4v187.733333c136.533333 8.533333 256 64 354.133333 166.4 106.666667 106.666667 170.666667 243.2 187.733334 405.333334 0 17.066667-8.533333 38.4-25.6 42.666666-17.066667 8.533333-38.4 4.266667-51.2-12.8-98.133333-119.466667-192-196.266667-273.066667-213.333333-64-17.066667-123.733333-21.333333-192-12.8V896c0 17.066667-12.8 34.133333-25.6 38.4-4.266667 4.266667-8.533333 4.266667-12.8 4.266667z m-298.666667-435.2l256 281.6v-132.266667c0-21.333333 17.066667-38.4 34.133334-42.666667 93.866667-17.066667 174.933333-12.8 260.266666 8.533334 64 17.066667 136.533333 59.733333 209.066667 128-25.6-76.8-72.533333-149.333333-128-204.8-93.866667-93.866667-204.8-140.8-337.066667-140.8-21.333333 0-42.666667-21.333333-42.666666-42.666667V234.666667l-251.733334 268.8z" fill="#333333" p-id="974"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
pages/v-sign/static/image/save.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
1
pages/v-sign/static/image/save.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639214153477" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="861" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M62.848 60.992h704v64h-704zM62.848 640.832h543.936v64H62.848z" fill="#333333" p-id="862"></path><path d="M702.848 60.992h64v320h-64zM62.848 113.536h64v527.296h-64z" fill="#333333" p-id="863"></path><path d="M70.528 498.304l218.24-117.312 30.4 56.32-218.304 117.376z" fill="#333333" p-id="864"></path><path d="M298.432 375.04l144.896 274.176-56.576 29.888L241.92 404.928zM512 194.176h126.592v127.168H512zM663.296 448.448h128v320h-128z" fill="#333333" p-id="865"></path><path d="M599.36 764.992l127.936 128 128.064-128z" fill="#333333" p-id="866"></path></svg>
|
||||
|
After Width: | Height: | Size: 934 B |
@ -10,6 +10,6 @@ export function isNumber(val) {
|
||||
* 处理大小单位
|
||||
* @param {Object} val
|
||||
*/
|
||||
export function formatSize(val) {
|
||||
return isNumber(val) ? `${val}rpx` : val
|
||||
export function formatSize(val, unit = 'rpx') {
|
||||
return isNumber(val) ? `${val}${unit}` : val
|
||||
}
|
||||