1
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
export default {
|
||||
props: {
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: () => uni.$u.$t('uSignature.title'),
|
||||
},
|
||||
// 是否显示标题
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.showTitle,
|
||||
},
|
||||
// 是否显示工具栏
|
||||
showToolbar: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.showToolbar,
|
||||
},
|
||||
// 是否显示颜色列表
|
||||
showColorList: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.showColorList,
|
||||
},
|
||||
// 是否显示清空按钮
|
||||
showClear: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.showClear,
|
||||
},
|
||||
// 是否显示撤销按钮
|
||||
showUndo: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.showUndo,
|
||||
},
|
||||
// 清空按钮的文本
|
||||
clearText: {
|
||||
type: String,
|
||||
default: () => uni.$u.$t('uSignature.clearText'),
|
||||
},
|
||||
// 撤销按钮的文本
|
||||
undoText: {
|
||||
type: String,
|
||||
default: () => uni.$u.$t('uSignature.undoText'),
|
||||
},
|
||||
// 完成按钮的文本
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: () => uni.$u.$t('uSignature.confirmText'),
|
||||
},
|
||||
// 工具栏对齐方式
|
||||
toolbarStyle: {
|
||||
type: Object,
|
||||
default: () => uni.$u.props.signature.toolbarStyle,
|
||||
},
|
||||
// 是否固定标题栏和工具栏
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.fixed,
|
||||
},
|
||||
// 线条最小宽度
|
||||
minLineWidth: {
|
||||
type: [String, Number],
|
||||
default: () => uni.$u.props.signature.minLineWidth,
|
||||
},
|
||||
// 线条最大宽度
|
||||
maxLineWidth: {
|
||||
type: [String, Number],
|
||||
default: () => uni.$u.props.signature.maxLineWidth,
|
||||
},
|
||||
// 画笔颜色
|
||||
penColor: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.signature.penColor,
|
||||
},
|
||||
// 画笔颜色列表
|
||||
penColorList: {
|
||||
type: Array,
|
||||
default: () => uni.$u.props.signature.penColorList,
|
||||
},
|
||||
// 画笔大小
|
||||
penSize: {
|
||||
type: [String, Number],
|
||||
default: () => uni.$u.props.signature.penSize,
|
||||
},
|
||||
// 背景颜色
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.signature.backgroundColor,
|
||||
},
|
||||
// canvas类型
|
||||
type: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.signature.type,
|
||||
},
|
||||
// 是否开启压感
|
||||
openSmooth: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.openSmooth,
|
||||
},
|
||||
// 最大历史记录数
|
||||
maxHistoryLength: {
|
||||
type: [String, Number],
|
||||
default: () => uni.$u.props.signature.maxHistoryLength,
|
||||
},
|
||||
// 是否横屏
|
||||
landscape: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.landscape,
|
||||
},
|
||||
// 是否禁用滚动
|
||||
disableScroll: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.disableScroll,
|
||||
},
|
||||
// 是否禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.disabled,
|
||||
},
|
||||
// 只生成内容区域
|
||||
boundingBox: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.boundingBox,
|
||||
},
|
||||
|
||||
// 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可
|
||||
fileType: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.signature.fileType,
|
||||
},
|
||||
// 图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理
|
||||
quality: {
|
||||
type: Number,
|
||||
default: () => uni.$u.props.signature.quality,
|
||||
},
|
||||
// 是否显示水印
|
||||
showWatermark: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.signature.showWatermark,
|
||||
},
|
||||
// 水印配置对象
|
||||
watermark: {
|
||||
type: Object,
|
||||
default: () => uni.$u.props.signature.watermark,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,771 @@
|
||||
<template>
|
||||
<view
|
||||
class="u-signature"
|
||||
:class="[landscape ? 'u-signature-landscape' : '']"
|
||||
:style="[containerStyle, $u.addStyle(customStyle)]"
|
||||
@touchmove.prevent.stop
|
||||
@wheel.prevent.stop
|
||||
>
|
||||
<view v-if="showTitle" class="u-signature__title" :class="[{'u-signature__title-fixed': fixed}]">
|
||||
<slot name="title">
|
||||
<text class="u-signature__title-text">{{ title }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<view class="u-signature__canvas">
|
||||
<canvas
|
||||
:type="type"
|
||||
:canvas-id="canvasId"
|
||||
:id="canvasId"
|
||||
:style="[canvasStyle]"
|
||||
:class="['u-signature__canvas']"
|
||||
:disable-scroll="disableScroll"
|
||||
@touchstart="onCanvasTouchStart"
|
||||
@touchmove="onCanvasTouchMove"
|
||||
@touchend="onCanvasTouchEnd"
|
||||
@touchcancel="onCanvasTouchCancel" >
|
||||
</canvas>
|
||||
</view>
|
||||
|
||||
<view v-if="showToolbar"
|
||||
class="u-signature__toolbar"
|
||||
:class="[{'u-signature__toolbar-fixed': fixed}]"
|
||||
:style="[$u.addStyle(toolbarStyle)]"
|
||||
>
|
||||
<slot name="toolbar">
|
||||
<view class="u-signature__toolbar-left">
|
||||
<view v-if="showColorList" class="u-signature__toolbar-item">
|
||||
<view class="u-signature__toolbar-color-list">
|
||||
<view
|
||||
v-for="(item, index) in penColorList"
|
||||
:key="index"
|
||||
class="u-signature__toolbar-color"
|
||||
:style="[
|
||||
{
|
||||
backgroundColor: penColorInner === item ? 'transparent' : item,
|
||||
borderColor: item,
|
||||
},
|
||||
]"
|
||||
@click="handlePenColor(item)"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-signature__toolbar-right">
|
||||
<view class="u-signature__toolbar-item">
|
||||
<u-button
|
||||
type="info"
|
||||
icon="trash"
|
||||
@click="handleClear"
|
||||
>
|
||||
{{ clearText }}
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="u-signature__toolbar-item">
|
||||
<u-button
|
||||
type="info"
|
||||
icon="back"
|
||||
@click="handleUndo"
|
||||
>
|
||||
{{ undoText }}
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="u-signature__toolbar-item">
|
||||
<u-button
|
||||
type="primary"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
{{ confirmText }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin';
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
|
||||
/**
|
||||
* Signature 签名组件
|
||||
* @description 可用于业务签名等场景
|
||||
* @tutorial https://uview.d3u.cn/components/signature.html
|
||||
*
|
||||
* @property {Number} penSize 画笔大小 (默认 2 )
|
||||
* @property {Number} minLineWidth 线条最小宽度 (默认 2 )
|
||||
* @property {Number} maxLineWidth 线条最大宽度 (默认 6 )
|
||||
* @property {String} penColor 画笔颜色 (默认 'black' )
|
||||
* @property {String} backgroundColor 背景颜色 (默认 '' )
|
||||
* @property {String} type canvas类型 (默认 '2d' )
|
||||
* @property {Boolean} openSmooth 是否开启压感 (默认 false )
|
||||
* @property {Number} maxHistoryLength 最大历史记录数 (默认 20 )
|
||||
* @property {Boolean} landscape 是否横屏 (默认 false )
|
||||
* @property {Boolean} disableScroll 是否禁用滚动 (默认 true )
|
||||
* @property {Boolean} disabled 是否禁用 (默认 false )
|
||||
* @property {Boolean} boundingBox 只生成内容区域 (默认 false )
|
||||
* @property {Object} customStyle 自定义样式
|
||||
* @event {Function} undo 撤销方法
|
||||
* @event {Function} clear 清空方法
|
||||
* @event {Function} getImage 保存方法
|
||||
* @example <u-signature :penColor="penColor" :penSize="penSize" ref="signatureRef"></u-signature>
|
||||
*/
|
||||
|
||||
let canvasObj = {};
|
||||
|
||||
export default {
|
||||
name: 'u-signature',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
canvasId: 'signatureId_' + uni.$u.guid(),
|
||||
ctx: null,
|
||||
canvas: null,
|
||||
canvasWidth: 0,
|
||||
canvasHeight: 0,
|
||||
isDrawing: false,
|
||||
lastPoint: null,
|
||||
currentStroke: [],
|
||||
history: [],
|
||||
isEmpty: true,
|
||||
velocityFilterWeight: 0.7,
|
||||
minVelocity: 0.25,
|
||||
currentVelocity: 0,
|
||||
lastTimestamp: 0,
|
||||
penColorInner: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
is2d() {
|
||||
// #ifdef MP-WEIXIN
|
||||
return this.type == '2d';
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
return false;
|
||||
// #endif
|
||||
},
|
||||
containerStyle() {
|
||||
const style = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
return style;
|
||||
},
|
||||
canvasStyle() {
|
||||
const style = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'block',
|
||||
};
|
||||
|
||||
if (
|
||||
this.backgroundColor &&
|
||||
this.backgroundColor !== 'transparent'
|
||||
) {
|
||||
style.backgroundColor = this.backgroundColor;
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
penColor: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.penColorInner = newVal;
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ['clear', 'undo', 'confirm'],
|
||||
// #endif
|
||||
methods: {
|
||||
async init() {
|
||||
await this.$nextTick();
|
||||
|
||||
const query = uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(`#${this.canvasId}`);
|
||||
|
||||
if (this.is2d) {
|
||||
let canvas = await new Promise((resolve) => {
|
||||
query
|
||||
.fields({
|
||||
node: true,
|
||||
size: true,
|
||||
})
|
||||
.exec((res) => {
|
||||
|
||||
this.canvasWidth = parseInt(res[0].width);
|
||||
this.canvasHeight = parseInt(res[0].height);
|
||||
resolve(res[0].node);
|
||||
});
|
||||
});
|
||||
|
||||
canvas.width = this.canvasWidth;
|
||||
canvas.height = this.canvasHeight;
|
||||
this.ctx = canvas.getContext('2d');
|
||||
canvasObj[this.canvasId] = canvas;
|
||||
} else {
|
||||
// 传统 canvas 模式
|
||||
// #ifdef MP-ALIPAY
|
||||
this.ctx = uni.createCanvasContext(this.canvasId);
|
||||
// #endif
|
||||
// #ifndef MP-ALIPAY
|
||||
this.ctx = uni.createCanvasContext(this.canvasId, this);
|
||||
// #endif
|
||||
|
||||
await new Promise((resolve) => {
|
||||
query
|
||||
.boundingClientRect((data) => {
|
||||
this.canvasWidth = parseInt(data.width);
|
||||
this.canvasHeight = parseInt(data.height);
|
||||
resolve();
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
}
|
||||
|
||||
// 设置画笔样式
|
||||
this.ctx.lineCap = 'round';
|
||||
this.ctx.lineJoin = 'round';
|
||||
this.ctx.strokeStyle = this.penColorInner;
|
||||
this.ctx.lineWidth = this.penSize;
|
||||
|
||||
// 绘制背景和水印
|
||||
this.drawBackgroundAndWatermark();
|
||||
},
|
||||
|
||||
// 开始绘制
|
||||
onCanvasTouchStart(e) {
|
||||
if (this.disabled) return;
|
||||
const touch = e.touches[0];
|
||||
const point = this.getTouchPoint(touch);
|
||||
|
||||
this.isDrawing = true;
|
||||
this.lastPoint = point;
|
||||
this.currentStroke = [point];
|
||||
this.lastTimestamp = Date.now();
|
||||
this.currentVelocity = 0;
|
||||
},
|
||||
|
||||
// 绘制中
|
||||
onCanvasTouchMove(e) {
|
||||
if (this.disabled || !this.isDrawing) return;
|
||||
|
||||
const touch = e.touches[0];
|
||||
const point = this.getTouchPoint(touch);
|
||||
const now = Date.now();
|
||||
|
||||
// 计算速度(用于压感)
|
||||
if (this.openSmooth && this.lastPoint) {
|
||||
const distance = this.getDistance(this.lastPoint, point);
|
||||
const timeDelta = now - this.lastTimestamp;
|
||||
const velocity = distance / timeDelta;
|
||||
|
||||
this.currentVelocity =
|
||||
this.velocityFilterWeight * velocity +
|
||||
(1 - this.velocityFilterWeight) * this.currentVelocity;
|
||||
}
|
||||
|
||||
this.drawLine(this.lastPoint, point);
|
||||
this.lastPoint = point;
|
||||
this.currentStroke.push(point);
|
||||
this.lastTimestamp = now;
|
||||
this.isEmpty = false;
|
||||
},
|
||||
|
||||
// 结束绘制
|
||||
onCanvasTouchEnd(e) {
|
||||
if (this.disabled || !this.isDrawing) return;
|
||||
|
||||
this.isDrawing = false;
|
||||
|
||||
// 保存到历史记录
|
||||
if (this.currentStroke.length > 0) {
|
||||
this.saveToHistory();
|
||||
}
|
||||
},
|
||||
|
||||
// 取消绘制
|
||||
onCanvasTouchCancel(e) {
|
||||
this.onCanvasTouchEnd(e);
|
||||
},
|
||||
|
||||
// 获取触摸点坐标
|
||||
getTouchPoint(touch) {
|
||||
return {
|
||||
x: touch.x,
|
||||
y: touch.y,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
},
|
||||
|
||||
// 绘制线条
|
||||
drawLine(from, to) {
|
||||
if (!this.ctx || !from || !to) return;
|
||||
|
||||
let lineWidth = this.penSize;
|
||||
|
||||
// 压感效果
|
||||
if (this.openSmooth) {
|
||||
const velocity = Math.max(
|
||||
this.currentVelocity,
|
||||
this.minVelocity,
|
||||
);
|
||||
lineWidth = Math.max(
|
||||
this.minLineWidth,
|
||||
Math.min(this.maxLineWidth, this.penSize / velocity),
|
||||
);
|
||||
}
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(from.x, from.y);
|
||||
this.ctx.lineTo(to.x, to.y);
|
||||
this.ctx.strokeStyle = this.penColorInner;
|
||||
this.ctx.lineWidth = lineWidth;
|
||||
this.ctx.stroke();
|
||||
|
||||
if (!this.is2d) {
|
||||
this.ctx.draw(true);
|
||||
}
|
||||
},
|
||||
|
||||
// 计算两点距离
|
||||
getDistance(p1, p2) {
|
||||
return Math.sqrt(
|
||||
Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2),
|
||||
);
|
||||
},
|
||||
|
||||
// 绘制水印
|
||||
drawWatermark() {
|
||||
if (!this.ctx || !this.watermark.text) return;
|
||||
|
||||
const ctx = this.ctx;
|
||||
const text = this.watermark.text;
|
||||
const fontSize = parseInt(this.watermark.fontSize);
|
||||
const fontFamily = this.watermark.fontFamily;
|
||||
const color = this.watermark.color;
|
||||
const rotate = this.watermark.rotate;
|
||||
const spacing = this.watermark.spacing;
|
||||
|
||||
// 设置水印样式
|
||||
ctx.font = `${fontSize}px ${fontFamily}`;
|
||||
ctx.fillStyle = color;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
// 计算文本尺寸
|
||||
const textMetrics = ctx.measureText(text);
|
||||
const textWidth = textMetrics.width;
|
||||
const textHeight = fontSize;
|
||||
|
||||
if (this.watermark.single) {
|
||||
// 绘制单个居中水印
|
||||
const centerX = this.canvasWidth / 2;
|
||||
const centerY = this.canvasHeight / 2;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(centerX, centerY);
|
||||
ctx.rotate((rotate * Math.PI) / 180);
|
||||
ctx.fillText(text, 0, 0);
|
||||
ctx.restore();
|
||||
} else {
|
||||
// 绘制网格水印
|
||||
const cols = Math.ceil(this.canvasWidth / spacing) + 1;
|
||||
const rows = Math.ceil(this.canvasHeight / spacing) + 1;
|
||||
|
||||
// 绘制水印网格
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
const x = col * spacing;
|
||||
const y = row * spacing;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate((rotate * Math.PI) / 180);
|
||||
ctx.fillText(text, 0, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于非2d canvas,需要调用draw方法
|
||||
if (!this.is2d) {
|
||||
ctx.draw(true);
|
||||
}
|
||||
},
|
||||
|
||||
// 重绘历史记录
|
||||
redrawHistory() {
|
||||
if (!this.ctx || !this.history.length) return;
|
||||
|
||||
this.history.forEach((item) => {
|
||||
if (item.stroke && item.stroke.length > 1) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(item.stroke[0].x, item.stroke[0].y);
|
||||
|
||||
for (let i = 1; i < item.stroke.length; i++) {
|
||||
this.ctx.lineTo(item.stroke[i].x, item.stroke[i].y);
|
||||
}
|
||||
|
||||
this.ctx.strokeStyle = this.penColorInner;
|
||||
this.ctx.lineWidth = this.penSize;
|
||||
this.ctx.stroke();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 清空画布并设置背景
|
||||
clearCanvas() {
|
||||
if (!this.ctx) return;
|
||||
|
||||
// 清空画布
|
||||
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||||
|
||||
// 重新设置背景色
|
||||
if (
|
||||
this.backgroundColor &&
|
||||
this.backgroundColor !== 'transparent'
|
||||
) {
|
||||
this.ctx.fillStyle = this.backgroundColor;
|
||||
this.ctx.fillRect(
|
||||
0,
|
||||
0,
|
||||
this.canvasWidth,
|
||||
this.canvasHeight,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// 绘制背景和水印
|
||||
drawBackgroundAndWatermark() {
|
||||
this.clearCanvas();
|
||||
|
||||
// 重新绘制水印
|
||||
if (this.showWatermark && this.watermark.text) {
|
||||
this.drawWatermark();
|
||||
}
|
||||
},
|
||||
|
||||
// 恢复水印
|
||||
restoreWatermark() {
|
||||
if (!this.ctx) return;
|
||||
|
||||
// 绘制背景和水印
|
||||
this.drawBackgroundAndWatermark();
|
||||
|
||||
// 重绘所有笔画
|
||||
this.redrawHistory();
|
||||
|
||||
// 对于非2d canvas,需要调用draw方法
|
||||
if (!this.is2d) {
|
||||
this.ctx.draw(true);
|
||||
}
|
||||
},
|
||||
|
||||
// 保存到历史记录
|
||||
saveToHistory() {
|
||||
if (this.maxHistoryLength <= 0) return;
|
||||
|
||||
const imageData = {
|
||||
stroke: [...this.currentStroke],
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
this.history.push(imageData);
|
||||
|
||||
// 限制历史记录数量
|
||||
if (this.history.length > this.maxHistoryLength) {
|
||||
this.history.shift();
|
||||
}
|
||||
|
||||
this.currentStroke = [];
|
||||
},
|
||||
|
||||
handlePenColor(color){
|
||||
this.penColorInner = color;
|
||||
this.ctx.strokeStyle = color;
|
||||
},
|
||||
handleClear(){
|
||||
this.clear();
|
||||
this.$emit('clear');
|
||||
},
|
||||
handleUndo(){
|
||||
this.undo();
|
||||
this.$emit('undo');
|
||||
},
|
||||
handleConfirm(){
|
||||
this.getImage().then(res => {
|
||||
this.$emit('confirm', res);
|
||||
});
|
||||
},
|
||||
// 撤销
|
||||
undo() {
|
||||
if (this.history.length === 0) return;
|
||||
this.history.pop();
|
||||
this.redrawFromHistory();
|
||||
},
|
||||
|
||||
// 重做(从历史记录重绘)
|
||||
redrawFromHistory() {
|
||||
if (!this.ctx) return;
|
||||
|
||||
// 绘制背景和水印
|
||||
this.drawBackgroundAndWatermark();
|
||||
|
||||
// 重绘所有笔画
|
||||
this.redrawHistory();
|
||||
|
||||
// 对于非2d canvas,需要调用draw方法
|
||||
if (!this.is2d) {
|
||||
this.ctx.draw(true);
|
||||
}
|
||||
|
||||
this.isEmpty = this.history.length === 0;
|
||||
},
|
||||
|
||||
// 清空
|
||||
clear() {
|
||||
if (!this.ctx) return;
|
||||
|
||||
// 绘制背景和水印
|
||||
this.drawBackgroundAndWatermark();
|
||||
|
||||
//对于非2d canvas,需要调用draw方法
|
||||
if (!this.is2d) {
|
||||
this.ctx.draw(true);
|
||||
}
|
||||
|
||||
this.history = [];
|
||||
this.currentStroke = [];
|
||||
this.isEmpty = true;
|
||||
|
||||
// 触发清空事件
|
||||
this.$emit('clear');
|
||||
},
|
||||
|
||||
|
||||
// 导出图片
|
||||
getImage() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.isEmpty) {
|
||||
uni.showToast({
|
||||
title: '签名板为空',
|
||||
icon: 'none',
|
||||
});
|
||||
reject('签名板为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果保存时不显示水印,需要临时隐藏水印
|
||||
let needRestoreWatermark = false;
|
||||
if (this.showWatermark && !this.watermark.showOnSave) {
|
||||
needRestoreWatermark = true;
|
||||
// 临时隐藏水印,只绘制背景和笔画
|
||||
this.clearCanvas();
|
||||
this.redrawHistory();
|
||||
|
||||
// 对于非2d canvas,需要调用draw方法
|
||||
if (!this.is2d) {
|
||||
this.ctx.draw(true);
|
||||
}
|
||||
}
|
||||
|
||||
let params = {
|
||||
canvas: canvasObj[this.canvasId],
|
||||
canvasId: this.canvasId,
|
||||
width: this.canvasWidth,
|
||||
height: this.canvasHeight,
|
||||
fileType: this.fileType,
|
||||
quality: this.quality,
|
||||
success: (res) => {
|
||||
if (needRestoreWatermark) {
|
||||
this.restoreWatermark();
|
||||
}
|
||||
resolve(res.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
if (needRestoreWatermark) {
|
||||
this.restoreWatermark();
|
||||
}
|
||||
reject(err);
|
||||
},
|
||||
};
|
||||
|
||||
// 处理boundingBox
|
||||
if (this.boundingBox) {
|
||||
params.x = 0;
|
||||
params.y = 0;
|
||||
}
|
||||
|
||||
// #ifdef MP-ALIPAY
|
||||
uni.canvasToTempFilePath(params);
|
||||
// #endif
|
||||
// #ifndef MP-ALIPAY
|
||||
uni.canvasToTempFilePath(params, this);
|
||||
// #endif
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-signature {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// 横屏模式
|
||||
&-landscape {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
&__title{
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&-fixed{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9999;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
// 横屏模式标题样式
|
||||
.u-signature-landscape & {
|
||||
width: 26px;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&-text {
|
||||
width: 100vh;
|
||||
transform: rotate(90deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__canvas {
|
||||
width: 100%;
|
||||
display: block;
|
||||
touch-action: none;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__toolbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 10px 20px 10px;
|
||||
|
||||
&-fixed{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9999;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
// 横屏模式工具栏样式
|
||||
.u-signature-landscape & {
|
||||
padding: 10px;
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&-color-list{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
// 横屏模式颜色列表样式
|
||||
.u-signature-landscape & {
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-color{
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 100px;
|
||||
margin: 0 3px;
|
||||
border: 6px solid #fff;
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1);
|
||||
// 横屏模式下的颜色选择器样式
|
||||
.u-signature-landscape & {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
// 横屏模式左侧工具栏样式
|
||||
.u-signature-landscape & {
|
||||
flex-direction: column;
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
&-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
//align-items: center;
|
||||
|
||||
// 横屏模式右侧工具栏样式
|
||||
.u-signature-landscape & {
|
||||
flex-direction: column;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.u-signature__toolbar-item {
|
||||
margin-right: 0;
|
||||
margin-left: -40px;
|
||||
height: 100px;
|
||||
transform: rotate(90deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
margin-right: 10px;
|
||||
|
||||
}
|
||||
|
||||
&-item:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user