WXL
3 天以前 2cc85c64f1c64a2dbaeae276a3e2ca8420de76b7
node_modules/uview-plus/components/u-signature/u-signature.vue
@@ -1,19 +1,22 @@
<template>
   <view class="u-signature">
      <view class="u-signature__canvas-wrap">
         <canvas
            class="u-signature__canvas"
            :canvas-id="canvasId"
            :disable-scroll="true"
            @touchstart="touchStart"
            @touchmove="touchMove"
      <view class="u-signature__canvas-wrap" :style="{background: bgColor}">
         <up-canvas
            ref="signatureCanvas"
            :canvas-id="canvasId"
            :width="canvasWidth"
            :height="canvasHeight"
            :bg-color="bgColor"
            @touchstart="touchStart"
            @touchmove="touchMove"
            @touchend="touchEnd"
            :disable-scroll="true"
            class="u-signature__canvas"
            :style="{
               width: canvasWidth + 'px',
               height: canvasHeight + 'px',
               background: bgColor
            }"
         ></canvas>
            }">
         </up-canvas>
      </view>
      
      <view v-if="showToolbar" class="u-signature__toolbar">
@@ -116,7 +119,6 @@
            isDrawing: false,
            pathStack: [], // 存储绘制路径用于回退
            currentPath: [], // 当前绘制路径
            ctx: null,
            isEmpty: true,
            presetColors: [
               '#000000', // 黑色
@@ -130,11 +132,16 @@
            ],
            showBrushSettings: false,
            showColorSettings: false,
            lastPoint: null // 保存上一个点的坐标
            lastPoint: null, // 保存上一个点的坐标
            canvasInstance: null // 缓存canvas实例
         }
      },
      mounted() {
         this.initCanvas()
         // 初始化时获取canvas实例
         this.$nextTick(() => {
            this.getCanvasInstance();
            this.clearCanvas();
         });
      },
      watch: {
         width: {
@@ -163,32 +170,41 @@
         }
      },
      methods: {
         initCanvas() {
            // #ifndef APP-NVUE
            const ctx = uni.createCanvasContext(this.canvasId, this)
            this.ctx = ctx
            this.clearCanvas()
            // #endif
         t,
         // 获取签名画布实例
         getCanvasInstance() {
            if (this.canvasInstance) {
               return this.canvasInstance;
            }
            
            // #ifdef APP-NVUE
            // NVUE环境下的处理
            // #endif
            const canvasRef = this.$refs.signatureCanvas;
            if (canvasRef) {
               this.canvasInstance = canvasRef;
               return canvasRef;
            }
            return null;
         },
         
         touchStart(e) {
            if (!this.ctx) return
            if (!this.canvasInstance || !this.canvasInstance.ctx) {
               this.getCanvasInstance();
            }
            
            this.isDrawing = true
            this.isEmpty = false
            this.currentPath = []
            if (!this.canvasInstance || !this.canvasInstance.ctx) return;
            
            const { x, y } = this.getCanvasPoint(e)
            this.ctx.beginPath()
            this.ctx.moveTo(x, y)
            this.ctx.setLineCap('round')
            this.ctx.setLineJoin('round')
            this.ctx.setStrokeStyle(this.lineColor)
            this.ctx.setLineWidth(this.lineWidth)
            this.isDrawing = true;
            this.isEmpty = false;
            this.currentPath = [];
            const { x, y } = this.getCanvasPoint(e);
            // 设置线条样式
            this.canvasInstance.setLineStyle(this.lineColor, this.lineWidth);
            // 开始路径
            this.canvasInstance.beginPath();
            this.canvasInstance.moveTo(x, y);
            
            // 记录起始点
            this.currentPath.push({
@@ -197,83 +213,69 @@
               type: 'start',
               color: this.lineColor,
               width: this.lineWidth
            })
            });
            
            // 保存上一个点
            this.lastPoint = { x, y }
            this.lastPoint = { x, y };
            
            // 阻止默认事件以提高性能
            e.preventDefault()
            e.preventDefault();
         },
         
         touchMove(e) {
            if (!this.isDrawing || !this.ctx) return
            if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
            
            // 阻止默认事件以提高性能
            e.preventDefault()
            e.preventDefault();
            
            const { x, y } = this.getCanvasPoint(e)
            const { x, y } = this.getCanvasPoint(e);
            
            // 使用更密集的点采样确保线条连贯性
            if (this.lastPoint) {
               // 计算两点间距离
               const distance = Math.sqrt(Math.pow(x - this.lastPoint.x, 2) + Math.pow(y - this.lastPoint.y, 2))
               // 根据距离确定插值点数量,确保点间距不超过1像素以获得更平滑的线条
               const steps = Math.max(1, Math.floor(distance / 1))
               // 在两点间插入插值点
               for (let i = 1; i <= steps; i++) {
                  const t = i / steps
                  const midX = this.lastPoint.x + (x - this.lastPoint.x) * t
                  const midY = this.lastPoint.y + (y - this.lastPoint.y) * t
                  this.ctx.lineTo(midX, midY)
                  this.ctx.stroke()
                  this.currentPath.push({
                     x: midX,
                     y: midY,
                     type: 'move'
                  })
               }
            } else {
               this.ctx.lineTo(x, y)
               this.ctx.stroke()
               this.currentPath.push({
                  x,
                  y,
                  type: 'move'
               })
            }
            this.ctx.draw(true)
            // 从上一个点画线到当前点
            this.canvasInstance.lineTo(x, y);
            this.canvasInstance.stroke(); // 实时绘制当前线段
            this.currentPath.push({
               x,
               y,
               type: 'move'
            });
            this.canvasInstance.draw(false);
            
            // 更新上一个点
            this.lastPoint = { x, y }
            this.lastPoint = { x, y };
         },
         
         touchEnd(e) {
            if (!this.isDrawing || !this.ctx) return
            if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
            
            this.isDrawing = false
            this.ctx.closePath()
            this.lastPoint = null
            this.isDrawing = false;
            this.canvasInstance.closePath();
            this.lastPoint = null;
            
            // 将当前路径加入栈中用于回退
            if (this.currentPath.length > 0) {
               this.pathStack.push([...this.currentPath])
               this.pathStack.push([...this.currentPath]);
            }
            // 最后统一执行一次绘制
            this.canvasInstance.draw(true);
         },
         
         // 同步获取canvas坐标点(兼容处理)
         getCanvasPoint(e) {
            const touch = e.touches[0]
            const canvas = uni.createSelectorQuery().in(this).select('.u-signature__canvas')
            // #ifdef MP-WEIXIN
            const touch = e.touches && e.touches[0] ? e.touches[0] : e.mp.touches[0];
            // #endif
            // #ifndef MP-WEIXIN
            const touch = e.touches[0];
            // #endif
            
            // 返回一个包含坐标的对象
            // 计算相对于canvas的坐标
            // 由于无法直接获取canvas位置,这里简化处理
            // 实际应用中可能需要通过uni.createSelectorQuery获取canvas位置
            return {
               x: touch.x,
               y: touch.y
            }
            };
         },
         
         // 选择颜色
@@ -294,84 +296,83 @@
         
         // 重新绘制所有路径
         redraw() {
            this.clearCanvas()
            if (this.pathStack.length === 0) {
               this.isEmpty = true
               return
            if (!this.canvasInstance) {
               this.getCanvasInstance();
            }
            
            this.isEmpty = false
            if (!this.canvasInstance) return;
            
            // #ifndef APP-NVUE
            // 先清空画布
            this.canvasInstance.clearCanvas();
            if (this.pathStack.length === 0) {
               this.isEmpty = true;
               return;
            }
            this.isEmpty = false;
            // 逐个绘制路径
            this.pathStack.forEach(path => {
               if (path.length === 0) return
               if (path.length === 0) return;
               
               this.ctx.beginPath()
               this.ctx.setLineCap('round')
               this.ctx.setLineJoin('round')
               this.canvasInstance.beginPath();
               
               let lastPoint = null
               let lastPoint = null;
               path.forEach((point, index) => {
                  if (index === 0 && point.type === 'start') {
                     // 设置起始点样式
                     this.ctx.setStrokeStyle(point.color)
                     this.ctx.setLineWidth(point.width)
                     this.ctx.moveTo(point.x, point.y)
                     lastPoint = { x: point.x, y: point.y }
                     // 设置线条样式
                     this.canvasInstance.setLineStyle(point.color, point.width);
                     this.canvasInstance.moveTo(point.x, point.y);
                     lastPoint = { x: point.x, y: point.y };
                  } else if (point.type === 'move') {
                     this.ctx.lineTo(point.x, point.y)
                     lastPoint = { x: point.x, y: point.y }
                     this.canvasInstance.lineTo(point.x, point.y);
                     lastPoint = { x: point.x, y: point.y };
                  }
               })
               this.ctx.stroke()
               this.ctx.draw(true)
            })
            // #endif
         },
         // 清空画布
         clear() {
            this.pathStack = []
            this.currentPath = []
            this.isEmpty = true
            this.lastPoint = null
            this.clearCanvas()
               });
               this.canvasInstance.stroke();
               this.canvasInstance.draw(true);
            });
         },
         
         // 清空画布内容
         clearCanvas() {
            if (!this.ctx) return
            if (!this.canvasInstance) {
               this.getCanvasInstance();
            }
            
            // #ifndef APP-NVUE
            this.ctx.setFillStyle(this.bgColor)
            this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
            this.ctx.draw()
            // #endif
            if (!this.canvasInstance) return;
            this.canvasInstance.clearCanvas();
         },
         
         // 导出签名图片
         exportSignature() {
            if (this.isEmpty) return
         async exportSignature() {
            if (this.isEmpty) {
               console.warn('签名为空,无法导出');
               return;
            }
            
            // #ifndef APP-NVUE
            uni.canvasToTempFilePath({
               canvasId: this.canvasId,
               fileType: 'png',
               quality: 1,
               success: (res) => {
                  this.$emit('confirm', res.tempFilePath)
               },
               fail: (err) => {
                  this.$emit('error', err)
               }
            }, this)
            // #endif
            if (!this.canvasInstance) {
               this.getCanvasInstance();
            }
            
            // #ifdef APP-NVUE
            // NVUE环境下可能需要特殊处理
            // #endif
            if (!this.canvasInstance) {
               console.error('无法获取画布实例');
               return;
            }
            try {
               // 先重绘整个签名内容
               this.redraw();
               // 导出图片
               const imagePath = await this.canvasInstance.exportImage('png', 1);
               this.$emit('confirm', imagePath);
            } catch (error) {
               console.error('导出签名图片失败:', error);
               this.$emit('error', error);
            }
         },
         
         // 切换笔画设置显示