Browse Source

更新canvas

zhengnaiwen_citu 3 months ago
parent
commit
d2f0c386d4
1 changed files with 168 additions and 66 deletions
  1. 168 66
      src/views/components/MTry.vue

+ 168 - 66
src/views/components/MTry.vue

@@ -4,19 +4,10 @@
     <div class="d-flex black align-center justify-center" style="height: 100%; position: relative; overflow: hidden;">
       <div class="contentBox">
         <v-card :width="size.width" :height="size.height" class="content" ref="content">
-          <!-- <div class="point" :style="`left: ${coordinate.left.x}px;top: ${coordinate.left.y}px`"></div>
-          <div class="point" :style="`left: ${coordinate.right.x}px;top: ${coordinate.right.y}px`"></div>
-          <div class="point" :style="`left: ${coordinate.bridge.x}px;top: ${coordinate.bridge.y}px`"></div> -->
-          <img
-            v-if="chooseItem"
-            :src="chooseItem.pic_url"
-            alt=""
-            class="cover noMove"
-            :class="{ 'cursor-pointer': canMove}"
+          <canvas :width="size.width" :height="size.height"
             ref="cover"
-            :width="tryGlassesSize.width * scale"
-            :style="`--deg: ${tryGlassesSize.deg}deg;`"
-          >
+            class="cover noMove"
+          ></canvas>
           <img class="noMove" :src="src" alt="" :width="size.width" :height="size.height">
         </v-card>
 
@@ -110,7 +101,6 @@ export default {
       hidePanel: false,
       loading: false,
       isMobile: isMobile(),
-      scale: 1,
       clear: false,
       active: 0,
       canMove: false,
@@ -132,14 +122,21 @@ export default {
           modifier: 1,
           slideShadows: false
         }
+      },
+      canvasCtx: null,
+      imgEntity: new Image(),
+      imgProps: {
+        x: 0,
+        y: 0,
+        width: 10,
+        height: 10,
+        angle: 0,
+        scale: 1
       }
     }
   },
   inject: ['size'],
   computed: {
-    // faceType () {
-    //   return programType.find(e => e.value === this.chooseItem.facetype)?.face ?? ''
-    // },
     chooseItem () {
       const obj = this.item.items[this.active] ?? {}
       obj.faceType = programType.find(e => e.value === obj.facetype)?.face ?? ''
@@ -171,12 +168,12 @@ export default {
           handle: this.onMinus
         },
         {
-          icon: 'mdi-swap-horizontal',
-          handle: this.onChange
+          icon: 'mdi-reload',
+          handle: this.onReset
         },
         {
-          icon: 'mdi-reload',
-          handle: this.onReload
+          icon: 'mdi-swap-horizontal',
+          handle: this.onChange
         },
         {
           icon: 'mdi-camera-flip-outline',
@@ -193,46 +190,108 @@ export default {
     }
   },
   mounted () {
-    this.$nextTick(() => {
-      this.onReload()
+    this.$nextTick(async () => {
       if (this.isMobile) {
-        this.$refs.content.$el.addEventListener('touchmove', this.onTouchMove)
-        this.$refs.content.$el.addEventListener('touchend', this.onTouchLeave)
         this.$refs.cover.addEventListener('touchstart', this.onTouchStart)
         this.$refs.cover.addEventListener('touchend', this.onTouchEnd)
-        return
+        this.$refs.cover.addEventListener('touchmove', this.onTouchMove)
+        this.$refs.cover.addEventListener('touchleave', this.onTouchEnd)
+      } else {
+        this.$refs.cover.addEventListener('mousedown', this.onTouchStart)
+        this.$refs.cover.addEventListener('mouseup', this.onTouchEnd)
+        this.$refs.cover.addEventListener('mousemove', this.onTouchMove)
+        this.$refs.cover.addEventListener('mouseleave', this.onTouchEnd)
+      }
+      this.canvasCtx = this.$refs.cover.getContext('2d')
+      try {
+        await this.loadingImg()
+        this.onReset()
+      } catch (error) {
+        this.loading = false
+        this.$emit('error', '加载失败')
       }
-      this.$refs.content.$el.addEventListener('mousemove', this.onTouchMove)
-      this.$refs.content.$el.addEventListener('mouseleave', this.onTouchLeave)
-      this.$refs.cover.addEventListener('mousedown', this.onTouchStart)
-      this.$refs.cover.addEventListener('mouseup', this.onTouchEnd)
     })
   },
   beforeDestroy () {
     if (this.isMobile) {
-      this.$refs.content.$el.removeEventListener('touchmove', this.onTouchMove)
-      this.$refs.content.$el.removeEventListener('touchend', this.onTouchLeave)
       this.$refs.cover.removeEventListener('touchstart', this.onTouchStart)
       this.$refs.cover.removeEventListener('touchend', this.onTouchEnd)
+      this.$refs.cover.removeEventListener('touchmove', this.onTouchMove)
+      this.$refs.cover.removeEventListener('touchleave', this.onTouchEnd)
       return
     }
-    this.$refs.content.$el.removeEventListener('mousemove', this.onTouchMove)
-    this.$refs.content.$el.removeEventListener('mouseleave', this.onTouchLeave)
-    this.$refs.cover.addEventListener('touchstart', this.onTouchMove)
-    this.$refs.cover.addEventListener('touchend', this.onTouchLeave)
+    this.$refs.cover.removeEventListener('mousedown', this.onTouchStart)
+    this.$refs.cover.removeEventListener('mouseup', this.onTouchEnd)
+    this.$refs.cover.removeEventListener('mousemove', this.onTouchMove)
+    this.$refs.cover.removeEventListener('mouseleave', this.onTouchEnd)
   },
   methods: {
+    loadingImg () {
+      return new Promise((resolve, reject) => {
+        this.imgEntity.src = this.chooseItem.pic_url // 替换为您的 PNG 图片路径
+        this.imgEntity.onload = () => {
+          resolve()
+        }
+        this.imgEntity.onerror = (error) => {
+          reject(error)
+        }
+      })
+    },
+    draw () {
+      const ctx = this.canvasCtx
+      const img = this.imgEntity
+      const canvas = this.$refs.cover
+      const { x, y, width, height, angle } = this.imgProps
+      ctx.clearRect(0, 0, canvas.width, canvas.height)
+      ctx.save()
+      ctx.translate(x + width / 2, y + height / 2) // 旋转中心点
+      ctx.rotate(angle * Math.PI / 180)
+      ctx.drawImage(
+        img,
+        -width / 2, // 左上角 x 坐标
+        -height / 2, // 左上角 y 坐标
+        width,
+        height
+      )
+
+      // 绘制描边
+      // ctx.strokeStyle = 'red' // 可以根据需要更改颜色
+      // ctx.lineWidth = 5 // 设置描边的宽度
+      // ctx.strokeRect(-this.imgProps.width / 2, // 左上角 x 坐标
+      //   -this.imgProps.height / 2, // 左上角 y 坐标
+      //   this.imgProps.width,
+      //   this.imgProps.height)
+
+      ctx.restore()
+      this.loading = false
+    },
     onPlus () {
-      if (this.scale > 1.4) {
+      if (this.imgProps.scale > 1.4) {
         return
       }
-      this.scale += 0.05
+      const RATE = 0.05
+      Object.assign(this.imgProps, {
+        x: this.imgProps.x - this.imgProps.width * RATE / 2,
+        y: this.imgProps.y - this.imgProps.height * RATE / 2,
+        scale: this.imgProps.scale + RATE,
+        width: this.imgProps.width * (1 + RATE),
+        height: this.imgProps.height * (1 + RATE)
+      })
+      this.draw()
     },
     onMinus () {
-      if (this.scale < 0.6) {
+      if (this.imgProps.scale < 0.6) {
         return
       }
-      this.scale -= 0.05
+      const RATE = 0.05
+      Object.assign(this.imgProps, {
+        x: this.imgProps.x + this.imgProps.width * RATE / 2,
+        y: this.imgProps.y + this.imgProps.height * RATE / 2,
+        scale: this.imgProps.scale - RATE,
+        width: this.imgProps.width * (1 - RATE),
+        height: this.imgProps.height * (1 - RATE)
+      })
+      this.draw()
     },
     // 清屏
     onClear () {
@@ -247,10 +306,15 @@ export default {
       this.$emit('retake')
     },
     // 重置眼镜
-    onReload () {
-      this.scale = 1
-      this.$refs.cover.style.left = this.tryGlassesSize.center.x + 'px'
-      this.$refs.cover.style.top = this.tryGlassesSize.center.y + 'px'
+    onReset () {
+      this.imgProps.scale = 1
+      this.imgProps.angle = this.tryGlassesSize.deg
+      this.imgProps.width = this.tryGlassesSize.width
+      const { height, width } = this.imgEntity
+      this.imgProps.height = this.imgProps.width / width * height
+      this.imgProps.x = this.tryGlassesSize.center.x - this.imgProps.width / 2
+      this.imgProps.y = this.tryGlassesSize.center.y - this.imgProps.height / 2
+      this.draw()
     },
     calculateAngle (x1, y1, x2, y2) {
     // 计算斜率
@@ -264,17 +328,14 @@ export default {
 
       return degrees
     },
-    onSlideChange () {
+    async onSlideChange () {
+      if (this.loading) {
+        return
+      }
       this.loading = true
       this.active = this.$refs.mySwiper.swiper.activeIndex
-      const timer = setTimeout(() => {
-        this.loading = false
-        this.$emit('error', '加载失败')
-      }, 3000)
-      this.$nextTick(() => {
-        this.loading = false
-        clearTimeout(timer)
-      })
+      await this.loadingImg()
+      this.draw()
     },
     onTouchMove (v) {
       if (!this.canMove) {
@@ -289,23 +350,38 @@ export default {
       const left = this.moveObj.left + moveX
       const top = this.moveObj.top + moveY
       // 更新点位位置
-      this.$refs.cover.style.left = left + 'px'
-      this.$refs.cover.style.top = top + 'px'
-    },
-    onTouchLeave () {
-      this.onTouchEnd()
+      this.imgProps.x = left
+      this.imgProps.y = top
+      this.draw()
+      // this.$refs.cover.style.left = left + 'px'
+      // this.$refs.cover.style.top = top + 'px'
     },
     onTouchStart (e) {
+      // 获取 Canvas 元素的边界信息
+      const rect = this.$refs.cover.getBoundingClientRect()
+      let mouseX, mouseY
+      // 根据设备类型计算坐标
+      if (this.isMobile) {
+        mouseX = e.touches[0].clientX - rect.left
+        mouseY = e.touches[0].clientY - rect.top
+      } else {
+        mouseX = e.offsetX
+        mouseY = e.offsetY
+      }
+      if (!this.isMouseOnImage(mouseX, mouseY)) {
+        return
+      }
+
       this.canMove = true
-      const { left, top } = this.$refs.content.$el.getBoundingClientRect()
-      const { left: glassLeft, top: glassTop, height: glassHeight, width: glassWidth } = this.$refs.cover.getBoundingClientRect()
+      // const { left, top } = this.$refs.content.$el.getBoundingClientRect()
       const startX = this.isMobile ? e.touches[0].clientX : e.clientX
       const startY = this.isMobile ? e.touches[0].clientY : e.clientY
+
       this.moveObj = {
         startX,
         startY,
-        left: glassLeft - left + glassWidth / 2,
-        top: glassTop - top + glassHeight / 2
+        left: this.imgProps.x,
+        top: this.imgProps.y
       }
     },
     onTouchEnd (e) {
@@ -316,6 +392,36 @@ export default {
         left: null,
         top: null
       })
+    },
+    // 计算点位是否在图片上
+    isMouseOnImage (mouseX, mouseY) {
+      const { x, y, width, height, angle } = this.imgProps
+      // 计算图片中心点坐标
+      const centerX = x + width / 2
+      const centerY = y + height / 2
+
+      // 将坐标转换为相对于中心点的坐标
+      const localX = mouseX - centerX
+      const localY = mouseY - centerY
+
+      // 将旋转角度转换为弧度,并计算反向旋转(用于坐标转换)
+      const radians = angle * Math.PI / 180
+      const cos = Math.cos(-radians) // 反向旋转
+      const sin = Math.sin(-radians)
+
+      // 应用旋转矩阵,将坐标转换到未旋转的坐标系
+      const rotatedX = localX * cos - localY * sin
+      const rotatedY = localX * sin + localY * cos
+
+      // 计算图片未旋转时的边界范围(半宽和半高)
+      const halfWidth = width / 2
+      const halfHeight = height / 2
+
+      // 判断转换后的坐标是否在图片范围内
+      return (
+        Math.abs(rotatedX) <= halfWidth &&
+        Math.abs(rotatedY) <= halfHeight
+      )
     }
   }
 }
@@ -395,11 +501,7 @@ export default {
     border-radius: 50%;
     background-color: red;
   }
-  .cover {
-    transform-origin: 50% 50%;
-    transform: translate(-50%, -50%) rotate(var(--deg));
-    cursor: pointer;
-  }
+
 }
 .cursor-pointer {
   cursor: pointer;