wlzboy
2025-10-26 2c86a8bd60deed0dd0e044bad6fb83f75d19a332
app/pages/mine/avatar/index.vue
New file
@@ -0,0 +1,632 @@
<template>
   <view class="container">
      <view class="page-body uni-content-info">
         <view class='cropper-content'>
            <view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
               <view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
                  <image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
                  <view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd"
                      :style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
                     <view class="uni-cropper-view-box">
                        <view class="uni-cropper-dashed-h"></view>
                        <view class="uni-cropper-dashed-v"></view>
                        <view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-point point-tr" data-drag="topTight"></view>
                        <view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove" @touchend.stop="dragEnd"></view>
                        <view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
                        <view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
                        <view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
                     </view>
                  </view>
               </view>
            </view>
         </view>
         <view class='cropper-config'>
            <button type="primary reverse" @click="getImage" style='margin-top: 30rpx;'> 选择头像 </button>
            <button type="warn" @click="getImageInfo" style='margin-top: 30rpx;'> 提交 </button>
         </view>
         <canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
      </view>
   </view>
</template>
<script>
  import config from '@/config'
  import store from "@/store"
  import { uploadAvatar } from "@/api/system/user"
  import { getSystemInfo } from '@/utils/common'
  const baseUrl = config.baseUrl
   const sysInfo = getSystemInfo()
   let SCREEN_WIDTH = sysInfo.screenWidth
   let PAGE_X, // 手按下的x位置
      PAGE_Y, // 手按下y的位置
      PR = sysInfo.pixelRatio, // dpi
      T_PAGE_X, // 手移动的时候x的位置
      T_PAGE_Y, // 手移动的时候Y的位置
      CUT_L, // 初始化拖拽元素的left值
      CUT_T, // 初始化拖拽元素的top值
      CUT_R, // 初始化拖拽元素的
      CUT_B, // 初始化拖拽元素的
      CUT_W, // 初始化拖拽元素的宽度
      CUT_H, //  初始化拖拽元素的高度
      IMG_RATIO, // 图片比例
      IMG_REAL_W, // 图片实际的宽度
      IMG_REAL_H, // 图片实际的高度
      DRAFG_MOVE_RATIO = 1, //移动时候的比例,
      INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
      DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度
   export default {
      /**
       * 页面的初始数据
       */
      data() {
         return {
            imageSrc: store.getters.avatar,
            isShowImg: false,
            // 初始化的宽高
            cropperInitW: SCREEN_WIDTH,
            cropperInitH: SCREEN_WIDTH,
            // 动态的宽高
            cropperW: SCREEN_WIDTH,
            cropperH: SCREEN_WIDTH,
            // 动态的left top值
            cropperL: 0,
            cropperT: 0,
            transL: 0,
            transT: 0,
            // 图片缩放值
            scaleP: 0,
            imageW: 0,
            imageH: 0,
            // 裁剪框 宽高
            cutL: 0,
            cutT: 0,
            cutB: SCREEN_WIDTH,
            cutR: '100%',
            qualityWidth: DRAW_IMAGE_W,
            innerAspectRadio: DRAFG_MOVE_RATIO
         }
      },
      /**
       * 生命周期函数--监听页面初次渲染完成
       */
      onReady: function () {
         this.loadImage()
      },
      methods: {
         setData: function (obj) {
            let that = this
            Object.keys(obj).forEach(function (key) {
               that.$set(that.$data, key, obj[key])
            })
         },
         getImage: function () {
            var _this = this
            uni.chooseImage({
               success: function (res) {
                  _this.setData({
                     imageSrc: res.tempFilePaths[0],
                  })
                  _this.loadImage()
               },
            })
         },
         loadImage: function () {
            var _this = this
            uni.getImageInfo({
               src: _this.imageSrc,
               success: function success(res) {
                  IMG_RATIO = 1 / 1
                  if (IMG_RATIO >= 1) {
                     IMG_REAL_W = SCREEN_WIDTH
                     IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO
                  } else {
                     IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO
                     IMG_REAL_H = SCREEN_WIDTH
                  }
                  let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
                  INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange
                  // 根据图片的宽高显示不同的效果   保证图片可以正常显示
                  if (IMG_RATIO >= 1) {
                     let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2)
                     let cutB = cutT
                     let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2)
                     let cutR = cutL
                     _this.setData({
                        cropperW: SCREEN_WIDTH,
                        cropperH: SCREEN_WIDTH / IMG_RATIO,
                        // 初始化left right
                        cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
                        cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2),
                        cutL: cutL,
                        cutT: cutT,
                        cutR: cutR,
                        cutB: cutB,
                        // 图片缩放值
                        imageW: IMG_REAL_W,
                        imageH: IMG_REAL_H,
                        scaleP: IMG_REAL_W / SCREEN_WIDTH,
                        qualityWidth: DRAW_IMAGE_W,
                        innerAspectRadio: IMG_RATIO
                     })
                  } else {
                     let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2)
                     let cutR = cutL
                     let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2)
                     let cutB = cutT
                     _this.setData({
                        cropperW: SCREEN_WIDTH * IMG_RATIO,
                        cropperH: SCREEN_WIDTH,
                        // 初始化left right
                        cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
                        cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
                        cutL: cutL,
                        cutT: cutT,
                        cutR: cutR,
                        cutB: cutB,
                        // 图片缩放值
                        imageW: IMG_REAL_W,
                        imageH: IMG_REAL_H,
                        scaleP: IMG_REAL_W / SCREEN_WIDTH,
                        qualityWidth: DRAW_IMAGE_W,
                        innerAspectRadio: IMG_RATIO
                     })
                  }
                  _this.setData({
                     isShowImg: true
                  })
                  uni.hideLoading()
               }
            })
         },
         // 拖动时候触发的touchStart事件
         contentStartMove(e) {
            PAGE_X = e.touches[0].pageX
            PAGE_Y = e.touches[0].pageY
         },
         // 拖动时候触发的touchMove事件
         contentMoveing(e) {
            var _this = this
            var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
            var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
            // 左移
            if (dragLengthX > 0) {
               if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
            } else {
               if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
            }
            if (dragLengthY > 0) {
               if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
            } else {
               if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
            }
            this.setData({
               cutL: this.cutL - dragLengthX,
               cutT: this.cutT - dragLengthY,
               cutR: this.cutR + dragLengthX,
               cutB: this.cutB + dragLengthY
            })
            PAGE_X = e.touches[0].pageX
            PAGE_Y = e.touches[0].pageY
         },
         contentTouchEnd() {
         },
         // 获取图片
         getImageInfo() {
            var _this = this
            uni.showLoading({
               title: '图片生成中...',
            })
            // 将图片写入画布
            const ctx = uni.createCanvasContext('myCanvas')
            ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H)
            ctx.draw(true, () => {
               // 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
               var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W
               var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H
               var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W
               var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H
               uni.canvasToTempFilePath({
                  x: canvasL,
                  y: canvasT,
                  width: canvasW,
                  height: canvasH,
                  destWidth: canvasW,
                  destHeight: canvasH,
                  quality: 0.5,
                  canvasId: 'myCanvas',
                  success: function (res) {
                     uni.hideLoading()
                     let data = {name: 'avatarfile', filePath: res.tempFilePath}
                     uploadAvatar(data).then(response => {
                        store.commit('SET_AVATAR', baseUrl + response.imgUrl)
                        uni.showToast({ title: "修改成功", icon: 'success' })
                        uni.navigateBack()
                     })
                  }
               })
            })
         },
         // 设置大小的时候触发的touchStart事件
         dragStart(e) {
            T_PAGE_X = e.touches[0].pageX
            T_PAGE_Y = e.touches[0].pageY
            CUT_L = this.cutL
            CUT_R = this.cutR
            CUT_B = this.cutB
            CUT_T = this.cutT
         },
         // 设置大小的时候触发的touchMove事件
         dragMove(e) {
            var _this = this
            var dragType = e.target.dataset.drag
            switch (dragType) {
               case 'right':
                  var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
                  if (CUT_R + dragLength < 0) dragLength = -CUT_R
                  this.setData({
                     cutR: CUT_R + dragLength
                  })
                  break
               case 'left':
                  var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
                  if (CUT_L - dragLength < 0) dragLength = CUT_L
                  if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
                  this.setData({
                     cutL: CUT_L - dragLength
                  })
                  break
               case 'top':
                  var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
                  if (CUT_T - dragLength < 0) dragLength = CUT_T
                  if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
                  this.setData({
                     cutT: CUT_T - dragLength
                  })
                  break
               case 'bottom':
                  var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
                  if (CUT_B + dragLength < 0) dragLength = -CUT_B
                  this.setData({
                     cutB: CUT_B + dragLength
                  })
                  break
               case 'rightBottom':
                  var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
                  var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
                  if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
                  if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
                  let cutB = CUT_B + dragLengthY
                  let cutR = CUT_R + dragLengthX
                  this.setData({
                     cutB: cutB,
                     cutR: cutR
                  })
                  break
               default:
                  break
            }
         }
      }
   }
</script>
<style>
   /* pages/uni-cropper/index.wxss */
   .uni-content-info {
      /* position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      display: block;
      align-items: center;
      flex-direction: column; */
   }
   .cropper-config {
      padding: 20rpx 40rpx;
   }
   .cropper-content {
      min-height: 750rpx;
      width: 100%;
   }
   .uni-corpper {
      position: relative;
      overflow: hidden;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
      -webkit-tap-highlight-color: transparent;
      -webkit-touch-callout: none;
      box-sizing: border-box;
   }
   .uni-corpper-content {
      position: relative;
   }
   .uni-corpper-content image {
      display: block;
      width: 100%;
      min-width: 0 !important;
      max-width: none !important;
      height: 100%;
      min-height: 0 !important;
      max-height: none !important;
      image-orientation: 0deg !important;
      margin: 0 auto;
   }
   /* 移动图片效果 */
   .uni-cropper-drag-box {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      cursor: move;
      background: rgba(0, 0, 0, 0.6);
      z-index: 1;
   }
   /* 内部的信息 */
   .uni-corpper-crop-box {
      position: absolute;
      background: rgba(255, 255, 255, 0.3);
      z-index: 2;
   }
   .uni-corpper-crop-box .uni-cropper-view-box {
      position: relative;
      display: block;
      width: 100%;
      height: 100%;
      overflow: visible;
      outline: 1rpx solid #69f;
      outline-color: rgba(102, 153, 255, .75)
   }
   /* 横向虚线 */
   .uni-cropper-dashed-h {
      position: absolute;
      top: 33.33333333%;
      left: 0;
      width: 100%;
      height: 33.33333333%;
      border-top: 1rpx dashed rgba(255, 255, 255, 0.5);
      border-bottom: 1rpx dashed rgba(255, 255, 255, 0.5);
   }
   /* 纵向虚线 */
   .uni-cropper-dashed-v {
      position: absolute;
      left: 33.33333333%;
      top: 0;
      width: 33.33333333%;
      height: 100%;
      border-left: 1rpx dashed rgba(255, 255, 255, 0.5);
      border-right: 1rpx dashed rgba(255, 255, 255, 0.5);
   }
   /* 四个方向的线  为了之后的拖动事件*/
   .uni-cropper-line-t {
      position: absolute;
      display: block;
      width: 100%;
      background-color: #69f;
      top: 0;
      left: 0;
      height: 1rpx;
      opacity: 0.1;
      cursor: n-resize;
   }
   .uni-cropper-line-t::before {
      content: '';
      position: absolute;
      top: 50%;
      right: 0rpx;
      width: 100%;
      -webkit-transform: translate3d(0, -50%, 0);
      transform: translate3d(0, -50%, 0);
      bottom: 0;
      height: 41rpx;
      background: transparent;
      z-index: 11;
   }
   .uni-cropper-line-r {
      position: absolute;
      display: block;
      background-color: #69f;
      top: 0;
      right: 0rpx;
      width: 1rpx;
      opacity: 0.1;
      height: 100%;
      cursor: e-resize;
   }
   .uni-cropper-line-r::before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      width: 41rpx;
      -webkit-transform: translate3d(-50%, 0, 0);
      transform: translate3d(-50%, 0, 0);
      bottom: 0;
      height: 100%;
      background: transparent;
      z-index: 11;
   }
   .uni-cropper-line-b {
      position: absolute;
      display: block;
      width: 100%;
      background-color: #69f;
      bottom: 0;
      left: 0;
      height: 1rpx;
      opacity: 0.1;
      cursor: s-resize;
   }
   .uni-cropper-line-b::before {
      content: '';
      position: absolute;
      top: 50%;
      right: 0rpx;
      width: 100%;
      -webkit-transform: translate3d(0, -50%, 0);
      transform: translate3d(0, -50%, 0);
      bottom: 0;
      height: 41rpx;
      background: transparent;
      z-index: 11;
   }
   .uni-cropper-line-l {
      position: absolute;
      display: block;
      background-color: #69f;
      top: 0;
      left: 0;
      width: 1rpx;
      opacity: 0.1;
      height: 100%;
      cursor: w-resize;
   }
   .uni-cropper-line-l::before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      width: 41rpx;
      -webkit-transform: translate3d(-50%, 0, 0);
      transform: translate3d(-50%, 0, 0);
      bottom: 0;
      height: 100%;
      background: transparent;
      z-index: 11;
   }
   .uni-cropper-point {
      width: 5rpx;
      height: 5rpx;
      background-color: #69f;
      opacity: .75;
      position: absolute;
      z-index: 3;
   }
   .point-t {
      top: -3rpx;
      left: 50%;
      margin-left: -3rpx;
      cursor: n-resize;
   }
   .point-tr {
      top: -3rpx;
      left: 100%;
      margin-left: -3rpx;
      cursor: n-resize;
   }
   .point-r {
      top: 50%;
      left: 100%;
      margin-left: -3rpx;
      margin-top: -3rpx;
      cursor: n-resize;
   }
   .point-rb {
      left: 100%;
      top: 100%;
      -webkit-transform: translate3d(-50%, -50%, 0);
      transform: translate3d(-50%, -50%, 0);
      cursor: n-resize;
      width: 36rpx;
      height: 36rpx;
      background-color: #69f;
      position: absolute;
      z-index: 1112;
      opacity: 1;
   }
   .point-b {
      left: 50%;
      top: 100%;
      margin-left: -3rpx;
      margin-top: -3rpx;
      cursor: n-resize;
   }
   .point-bl {
      left: 0%;
      top: 100%;
      margin-left: -3rpx;
      margin-top: -3rpx;
      cursor: n-resize;
   }
   .point-l {
      left: 0%;
      top: 50%;
      margin-left: -3rpx;
      margin-top: -3rpx;
      cursor: n-resize;
   }
   .point-lt {
      left: 0%;
      top: 0%;
      margin-left: -3rpx;
      margin-top: -3rpx;
      cursor: n-resize;
   }
   /* 裁剪框预览内容 */
   .uni-cropper-viewer {
      position: relative;
      width: 100%;
      height: 100%;
      overflow: hidden;
   }
   .uni-cropper-viewer image {
      position: absolute;
      z-index: 2;
   }
</style>