vue 프로젝트에서 레이블 요소의 끌기, 확대/축소, 회전 및 기타 작업 구현

1. 마우스 이벤트

  • 클릭: 마우스를 눌렀을 때 발생합니다(보통 메인 버튼을 눌렀을 때).
  • dblclick: 동일한 요소에서 마우스를 두 번 클릭하면 트리거됩니다.
  • mousedown : 마우스 버튼을 눌렀을 때 발생합니다.
  • mouseup : 눌려진 마우스 버튼을 놓을 때 발생합니다.
  • mousemove : 마우스가 노드 내에서 움직일 때 트리거됩니다. 이 이벤트는 마우스가 계속 움직일 때 계속 발생합니다. 성능 문제를 방지하려면 이 이벤트의 수신 기능에 몇 가지 제한을 두는 것이 좋습니다. 예를 들어 일정 시간 내에 한 번만 실행할 수 있습니다.
  • mouseenter: 마우스가 노드에 들어갈 때 트리거되며, 이 이벤트는 마우스가 하위 노드에 들어갈 때 트리거되지 않습니다(자세한 내용은 아래 참조).
  • mouseover: 마우스가 노드에 진입할 때 트리거되며 이 이벤트는 마우스가 하위 노드에 진입할 때 다시 트리거됩니다(자세한 내용은 아래 참조).
  • mouseout: 마우스가 노드를 떠날 때 트리거되며, 이 이벤트는 마우스가 부모 노드를 떠날 때도 트리거됩니다(자세한 내용은 아래 참조).
  • mouseleave : 마우스가 노드를 떠날 때 트리거되며 부모 노드를 떠나는 것은 이 이벤트를 트리거하지 않습니다(자세한 내용은 아래 참조).
  • contextmenu: 마우스 오른쪽 버튼을 누르거나(컨텍스트 메뉴가 나타나기 전) "컨텍스트 메뉴 키"를 누를 때 트리거됩니다.
  • wheel: 마우스 휠을 굴릴 때 트리거되며 이 이벤트는 WheelEvent 인터페이스에서 상속됩니다.

클릭 이벤트: 사용자가 동일한 위치에서 먼저 mousedown 동작을 완료한 다음 mouseup 동작을 완료합니다. 따라서 트리거링 순서는 mousedown이 먼저 트리거되고 mouseup이 다음으로 트리거되고 click이 마지막으로 트리거됩니다.

dblclick 이벤트: mousedown, mouseup, click 후에 트리거됩니다.

mouseover 이벤트 및 mouseenter 이벤트: 둘 다 마우스가 노드에 들어갈 때 트리거됩니다. 둘 사이의 차이점은 mouseenter 이벤트는 한 번만 트리거되며 마우스가 노드 내부에서 이동하는 한 자식 노드에서 mouseover 이벤트가 여러 번 트리거된다는 것입니다.

mouseout 및 mouseleave 이벤트: 둘 다 마우스가 노드에 들어갈 때 트리거됩니다. 둘 사이의 차이점은 mouseleave 이벤트는 노드를 떠날 때 한 번만 발생하지만 여러 번 발생하지 않는 반면, mouseout 이벤트는 자식 요소를 떠나지 않고 부모 요소를 떠나면 여러 번 발생할 수 있다는 것입니다.

MouseEvent 속성

MouseEvent 인터페이스는 Event 인터페이스를 상속하므로 Event의 모든 속성과 메서드를 가집니다. 또한 고유한 속성과 메서드가 있습니다.

  • pageX, pageY: 페이지 스크롤의 영향을 받지 않는 페이지의 왼쪽 위 모서리를 기준으로 한거리
  • clientX, clientY: 페이지 스크롤링의 영향을 받는 페이지의 왼쪽 상단 모서리로부터의 거리, 기본값은 0, 이 속성을 설정하면 마우스가 움직이지 않습니다.
  • offsetX, offsetY: 자신 의 왼쪽 위 모서리 좌표 는 패딩부터 시작하여 요소를 기준으로 왼쪽 위 모서리를 위치 지정 문제와 상관없이 계산하고 내부 교차점을 계산합니다. IE 브라우저의 고유한 속성입니다.
  • layerX, layerY: 포지셔닝 속성이 있는 상위 요소의 왼쪽 위 모서리를 찾습니다 요소의 위치에 영향을 받아 이 요소에서 첫 번째로 배치된 요소 의 왼쪽 위 모서리를 찾습니다 (포지셔닝 속성이 있는 경우, 자신을 기준으로 합니다 ), 없는 경우 본체의 왼쪽 상단 모서리를 기준으로 합니다.
  • offsetWidth, offsetHeight: 콘텐츠 영역, 패딩 및 테두리를 포함하여 요소의 전체 너비와 높이를 가져옵니다.
  • clientWidth, clientHeight: 이 두 속성은 요소의 보이는 너비와 높이를 가져올 수 있으며 콘텐츠 영역과 내부 여백을 포함하여 요소의 너비와 높이를 가져옵니다.
  • offsetParent: 현재 요소의 포지셔닝 상위 요소(포지셔닝이 활성화된 상위 요소)의 포지셔닝을 가져오는 데 사용할 수 있으며 포지셔닝이 활성화되지 않은 경우 본문을 반환합니다.
  • offsetLeft, offsetTop: 배치된 부모 요소를 기준으로 현재 요소의 가로 및 세로 오프셋입니다.
  • scrollWidth, scrollHeight: 요소의 전체 스크롤 영역의 너비와 높이를 얻을 수 있습니다.
  • scrollLeft, scrollTop: 가로 및 세로 스크롤 막대의 스크롤 거리를 얻을 수 있습니다.
  • screenX, screenY: 값, 화면을 기준으로 마우스의 왼쪽 위 모서리 좌표(단위 픽셀), 기본값은 0이며 이 속성을 설정하면 마우스가 움직이지 않습니다.
  • ctrlKey : 부울 값, Ctrl 키를 동시에 누르는지 여부, 기본값은 false입니다.
  • shiftKey: 부울 값, Shift 키를 동시에 누르는지 여부, 기본값은 false입니다.
  • altKey : 부울 값, Alt 키를 동시에 누를지 여부, 기본값은 false입니다.
  • metaKey: 부울 값, Meta 키를 동시에 누를지 여부, 기본값은 false입니다.
  • 버튼: 어떤 마우스 버튼이 눌렸는지 나타내는 값, 기본값은 0, 기본 버튼(일반적으로 마우스의 왼쪽 버튼)이 눌렸거나 현재 이벤트가 이 속성을 정의하지 않음을 나타냅니다. 눌렸음(일반적으로 마우스 키의 가운데), 2는 보조 키가 눌렸음을 의미합니다(일반적으로 마우스 오른쪽 버튼).
  • buttons: 값, 마우스의 어떤 버튼이 눌렸는지 나타내는 3비트 이진 값이며 기본값은 0(버튼이 눌리지 않음)입니다. 1(이진수 001)은 기본 키를 눌렀음을 의미하고(일반적으로 왼쪽 버튼) 2(이진수 010)는 보조 키를 눌렀음을 의미하고(일반적으로 오른쪽 버튼) 4(이진수 100)는 보조 키를 눌렀음을 의미합니다(일반적으로 이진수 100). 중간 키). 따라서 3(이진수 011)을 반환하면 왼쪽 및 오른쪽 버튼이 동시에 눌렸다는 의미입니다.
  • relatedTarget: 이벤트의 관련 노드를 나타내는 노드 개체, 기본값은 null입니다. mouseenter 및 mouseover 이벤트의 경우 마우스가 방금 떠난 요소 노드를 나타내고 mouseout 및 mouseleave 이벤트의 경우 마우스가 들어가는 요소 노드를 나타냅니다.

2. 논리 실현

레이블의 마우스 선택 및 마우스를 누른 상태에서 드래그하는 효과를 얻으려면 먼저 레이블을 선택하고 mousedown 이벤트를 사용한 다음 마우스를 누르고 있으면 다음과 같이 mousedown 이벤트가 트리거될 때 드래그 가능한 표시를 설정할 수 있습니다. moveable= true, 그런 다음 mousemove 이벤트에서 moveable이 true인지 판단하고 true이면 드래그할 수 있음을 의미하며 획득한 좌표에 따라 레이블 요소의 상단과 왼쪽을 변경할 수 있으므로 움직임을 실현하기 위해 false이면 아무것도 실행되지 않고 마우스를 놓으면 mouseup 이벤트가 발생하며 이 이벤트에서 moveable은 false로 설정됩니다.

1. URL 이미지의 너비와 높이 얻기

    // 创建实例对象
	let img = new Image();
	// 图片地址
	img.src = "图片url链接";
	
	img.onload = function () {
 		let res = {
			width: img.width,
			height: img.height
		}
		console.log(res); // 获取到图片的宽高
 	}

2. 가능한 문제

1. PC 쪽에서 드래그 시 mouseup 이벤트가 사라지는 문제

설명: PC측에서 좌우 요소를 드래그&드래그할 때 mousedown+mousemove+mouseup을 사용하여 좌우 드래그&이동을 구현하는 동작을 여러 번 반복하면 항상 입력되지 않는 mouseup 이벤트가 발생하며, 결과적으로 mousemove 이벤트를 지울 수 없습니다.

해결 방법: 브라우저 마우스 이벤트에는 이벤트 버블링 및 기타 동작과 같은 기본 동작이 있습니다. mousedown 및 mousemove 이벤트에서 이러한 기본 동작을 비활성화해야 합니다. 동시에 css 스타일을 수신 요소에 추가합니다. user-select: none; 해당 텍스트가 선택되고 이동하여 mouseup 이벤트에 영향을 미치지 않도록 합니다.

2. mousemove 이벤트가 발생할 수 있는 영역이 너무 작음

설명: mousemove 이벤트를 모바일 레이블 요소에 바인딩하면 레이블 자체의 크기가 충분히 크지 않아 이동할 때 마우스가 레이블 영역을 벗어나 이벤트 손실 및 부드럽지 않은 이동 경험이 발생할 수 있습니다. .

해결 방법: 이동할 수 있는 가장 큰 영역의 부모 레이블에 mousemove 이벤트를 바인딩합니다.

3. 사건번호

<template>
  <!-- 视频编辑 -->
  <div class="img-canvas" 
    id="img-canvas" 
    :style="{
      width: width+'px',
      height: height+'px'
    }"
    @mousedown.stop.prevent="wrapStart($event)" 
    @mousemove.stop.prevent="wrapMove($event)"
    @mouseup.stop.prevent="wrapUp($event)"
    @mouseleave.stop.prevent="wrapUp($event)">

    <template v-for="(item, key) in items">
      <img
        :key="key"
        v-if="item.type == 'bg'"
        class="img"
        :src="item.content"
        :style="{
          width: item.width+'px', 
          height:item.height+'px', 
          zIndex: item.zIndex
        }"
        @click.stop.prevent="wraptouchStart($event, item.id)" />
        
      <div
        :key="key"
        v-else-if="item.show"
        :class="item.active ? 'img-wrap touch-active' : 'img-wrap'" 
        :style="{
          transform: 'rotate('+(item.angle ? item.angle : 0)+'deg)', 
          top: item.top+'px', 
          left: item.left+'px', 
          zIndex: item.zIndex
        }">
          <img
            v-if="item.type == 'ai' || item.type == 'tt'"
            class="img"
            :src="item.content"
            :style="{
              width: item.width+'px', 
              height:item.height+'px'
            }"
            @mousedown.prevent="wraptouchStart($event, item.id)" 
          />

          <div
            v-if="item.type == 'bt' || item.type == 'zm'"
            :id="'txt'+item.id"
            :class="item.type == 'zm' ? 'slh txt' : 'txt'"
            :style="{
              width: item.width ? item.width +'px' : 'auto', 
              height: item.height ? item.height + 'px' : 'auto', 
              fontSize: item.fontSize+'px', 
              color: item.fontColor, 
              textStroke: item.strokeShow ? item.strokeSize + 'px ' + item.strokeColor : 'none', 
              textAlign: item.align, 
              background: item.fontBgColor, 
              fontFamily: item.fontFamily
            }"
            @mousedown.prevent="wraptouchStart($event, item.id)" 
            :data-content="item.type == 'zm' ? '此处是字幕' : item.content"
            :data-color="item.fontColor"
            v-html="item.type == 'zm' ? ('此处是字幕') : item.content"></div>

        <!-- 删除按钮 -->
        <div 
          class="x" 
          v-if="item.active"
          @click.stop="deleteItem(item.id)">
          <img src="@/assets/x.png" />
        </div>

        <!-- 缩放按钮 -->
        <div
          class="s"
          v-if="item.active" 
          style="transform-origin:center;"
          @mousedown.prevent="oTouchStart($event, item.id)" 
        >
        </div>

        <div
          class="s s2"
          v-if="item.active" 
          style="transform-origin:center;"
          @mousedown.prevent="oTouchStart($event, item.id)" 
          >
        </div>

        <div
          class="s s3"
          v-if="item.active" 
          style="transform-origin:center;"
          @mousedown.prevent="oTouchStart($event, item.id)" 
        >
        </div>

        <!-- 旋转按钮 -->
        <div 
          class="o"
          v-if="item.active && item.type == 'tt'" 
          style="transform-origin:center;"
          @mousedown.prevent="oScaleStart($event, item.id)" 
        >
          <img src="@/assets/o.png"/>
        </div>

        <!-- 拉宽按钮 -->
        <div 
          class="lw"
          v-if="item.active && (item.type == 'bt')" 
          style="transform-origin:center;"
          @mousedown.prevent="oLwhStart($event, item.id, 'w')" 
        >
        </div>

        <!-- 拉高按钮 -->
        <div 
          class="lh"
          v-if="item.active && (item.type == 'bt')" 
          style="transform-origin:center;"
          @mousedown.prevent="oLwhStart($event, item.id, 'h')" 
        >
        </div>

      </div>

    </template>
  </div>
</template>
<style lang="less" scoped>
  .img-canvas {
    user-select: none;
    position: relative;
    background: yellowgreen;
    display: block;
    margin: 0 auto;
    width: 360px;
    height: 640px;

    .img-wrap {
      position: absolute;
      top: 20px;
      left: 20px;
      transform-origin: center;
      padding: 10px;
      box-sizing: border-box;

      &.touch-active {
        &::after {
          position: absolute;
          top: 0;
          left: 0;
          content: '';
          width: 100%;
          height: 100%;
          border: 6px solid #0054D1;
          box-sizing: border-box;
          pointer-events: none;
        }
      }

      .img {
        display: block;
      }

      .txt {
        display: block;
        /* 通过属性选择器结合伪元素before 实现文字外描边效果 */
        &[data-content]::before {
          /* attr()是用来获取被选中元素的某属性值,并且在样式文件中使用 */
          content: attr(data-content);
          position: absolute;
          /* 实现元素外描边的关键 */
          -webkit-text-stroke: 0;
          /* 文本颜色 */
          color: attr(data-color);
        }

        &.slh {
          display: block;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          &[data-content]::before {
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }
        }
        
      }

      .x {
        z-index: 2;
        width: 50px;
        height: 50px;
        position: absolute;
        top: 0;
        left: 0;
        transform: translate(-50%, -50%);
        background: white;
        border-radius: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        box-shadow:0 5px 5px 5px rgba(0, 0, 0, 0.2);
        image {
          width: 100%;
          height: 100%;
        }
      }

      .o {
        width: 50px;
        height: 50px;
        position: absolute;
        bottom: -20px;
        left: 50%;
        transform: translate(-50%, 100%);
        background: white;
        border-radius: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        box-shadow:0 5px 5px 5px rgba(0, 0, 0, 0.2);
        image {
          width: 100%;
          height: 100%;
      
        }
      }

      .s {
        width: 30px;
        height: 30px;
        position: absolute;
        top: 0;
        right: 0;
        transform: translate(50%, -50%);
        background: #0054D1;
        border-radius: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        box-shadow:0 5px 5px 5px rgba(0, 0, 0, 0.2);

        &.s2 {
          top: auto;
          bottom: 0;
          right: 0;
          transform: translate(50%, 50%);
        }

        &.s3 {
          top: auto;
          bottom: 0;
          left: 0;
          right: auto;
          transform: translate(-50%, 50%);
        }
      }

      .lw {
        z-index: 2;
        position: absolute;
        top: 50%;
        right: 0;
        transform: translate(50%, -50%);
        width: 15px;
        height: 40px;
        background: white;
        border-radius: 5px;
      }

      .lh {
        z-index: 2;
        position: absolute;
        left: 50%;
        bottom: 0;
        transform: translate(-50%, 50%);
        width: 40px;
        height: 15px;
        background: white;
        border-radius: 5px;
      }

    }
  }
</style>
<script>

import { getUUID, algorithm } from '@/utils/utils'


export default {
  props: {
    width: {
      type: Number,
      default: 360
    },
    height: {
      type: Number,
      default: 640
    },
    templateId: {
      type: Number,
      default: 304
    }
  },

  data() {
    return {
      timeFlag: '',
      toucheWrap: {}, // 图层容器
      index: 0, // 当前点击图片的index
      items: [],
      firstCopyItems: [],
      spItems: [], // 视频对象数组信息
      fmItems:[], // 封面对象数组信息
      spPart: [], // 视频片段列表
      spPartIndex: 0, // 当前选中的视频片段的index
      sDpi: 1, // 模版尺寸 720*1280


    }
  },

  created() {
    this.sDpi = 720/this.width;
    console.log(this.$parent)
  },

  mounted() {

    this.toucheWrap = {
      width: this.width,
      height: this.height,
      top: document.getElementById('img-canvas').offsetTop,
      left: document.getElementById('img-canvas').offsetLeft
    }


  },

  onUnmounted() {
    if(this.timeFlag) {
      clearTimeout(this.timeFlag)
    }
  },

  methods: {

    // 设置图层对象的信息
    setDropItem(obj, call) {
      console.log('setDropItem', obj)

      return new Promise((resolve, reject)=> {
        let data = {}; // 存储拖拽对象信息

        let type = obj.type;
        let content = obj.content;

        // 背景、AI数字人、贴图
        if(type == 'bg' || type == 'ai' || type == 'tt') {
          // 获取图片信息
          var img = new Image();
          // 图片地址
          img.src = content;

          img.onload = () => {

            let res = {
              width: img.width,
              height: img.height
            }

            // 初始化数据
            let maxWidth = 150, maxHeight = 150; // 设置最大宽高
            if(type == 'bg') {
              maxWidth = 360
              maxHeight = 640
            }

            if (res.width > maxWidth || res.height > maxHeight) { // 原图宽或高大于最大值就执行
              if (res.width / res.height > maxWidth / maxHeight) { // 判断比例使用最大值的宽或高作为基数计算
                data.width = maxWidth;
                data.height = Math.round(maxWidth * (res.height / res.width));
              } else {
                data.height = maxHeight;
                data.width = Math.round(maxHeight * (res.width / res.height));
              }
            } else {
              data.width = res.width;
              data.height = res.height;
            }
          
            data.iobsKey = '';
            data.show = true; // 显示
            data.type = type; // 对象类型 type - [bg, ai, tt, zm]
            data.content = content; // 显示地址
            data.id = algorithm(); // id
            data.top = 0; // top定位
            data.left = 0; // left定位
            // 圆心坐标
            data.x = data.left + data.width / 2;
            data.y = data.top + data.height / 2;
            data.scale = 1; // scale缩放
            data.rotate = 0; // 旋转角度
            data.active = false; // 选中状态
            
            console.log(this.items)
            if(type == 'bg') {
              data.zIndex = 0; // 层级
            } else if(this.items.find(it => it.type == 'bg')) {
              data.zIndex = this.items.length; // 层级
            } else {
              data.zIndex = this.items.length+1; // 层级
            }

            // 覆盖原数据
            data = {
              ...data,
              ...obj
            }
            
            // 封面
            if(this.isCoverEdit || obj.isFm) {
              
              this.fmItems.push(data); // 每增加一张图片数据增加一条信息
              this.items = this.fmItems;
            } 
            // 视频
            else {
              if(this.spPart[this.spPartIndex]) {
                this.spItems = this.spPart[this.spPartIndex]
              } else {
                this.spItems = []
              }
              
              this.spItems.push(data); // 每增加一张图片数据增加一条信息
              this.spPart[this.spPartIndex] = this.spItems
              this.items = this.spPart[this.spPartIndex]
            }

            resolve();
            call && call()
          }
        } 
        // 标题
        else if (type == 'bt' || type == 'zm') {
          // 初始化数据
          data.width = 0;
          data.height = 0;

          data.show = true; // 显示
          data.fontFamily = 'simhei'; // 字体
          data.strokeShow = true; // 显示描边
          data.align = 'left'; // 文本对齐方式
          data.fontColor = '#000'; // 字体颜色
          data.fontSize = 12; // 字号大小
          data.fontBgColor = ''; // 文本背景颜色
          data.strokeSize = 0; // 描边粗细
          data.strokeColor = '#000'; // 描边颜色
          data.type = type; // 对象类型 type - [bg, ai, tt, zm]
          data.content = content; // 显示内容
          data.id = algorithm(); // id
          
          data.scale = 1; // scale缩放
          data.rotate = 0; // 旋转角度
          data.active = false; // 选中状态

          if(type == 'bg') {
            data.zIndex = 0; // 层级
          } else if(this.items.find(it => it.type == 'bg')) {
            data.zIndex = this.items.length; // 层级
          } else {
            data.zIndex = this.items.length+1; // 层级
          }

          data.top = 0; // top定位
          data.left = 0; // left定位

          // 圆心坐标
          data.x = data.left + data.width / 2;
          data.y = data.top + data.height / 2;

          // 字幕
          if(type == 'zm') {

            // 圆心坐标
            data.x = this.toucheWrap.width/2;
            data.y = this.toucheWrap.height - 20;
            
            data.ys = obj.ys
            data.yl = obj.yl
            data.yd = obj.yd
            data.ysu = obj.ysu
      
          }

          // 覆盖原数据
          data = {
            ...data,
            ...obj
          }

          // 封面
          if(this.isCoverEdit || obj.isFm) {
            this.fmItems.push(data);
            this.items = this.fmItems; // 每增加一张图片数据增加一条信息
          } 
          // 视频
          else {
            if(this.spPart[this.spPartIndex]) {
              this.spItems = this.spPart[this.spPartIndex]
            } else {
              this.spItems = []
            }

            this.spItems.push(data); // 每增加一张图片数据增加一条信息
            this.spPart[this.spPartIndex] = this.spItems;
            this.items = this.spPart[this.spPartIndex];
          }

          let _i = this.items.length - 1;

          this.items = JSON.parse(JSON.stringify(this.items))
       
          setTimeout(() => {
            let doc = document.getElementById('txt' + this.items[_i].id)
            console.log('doc',this.items[_i].id, doc)

            this.items[_i].width = doc.offsetWidth;
            this.items[_i].height = doc.offsetHeight;

            if(type == 'zm') {
              this.items[_i].left = this.items[_i].x - this.items[_i].width / 2;
              this.items[_i].top = this.items[_i].y - this.items[_i].height / 2
            } else {
              this.items[_i].x = this.items[_i].left + this.items[_i].width / 2;
              this.items[_i].y = this.items[_i].top + this.items[_i].height / 2;
            }

            // 覆盖原数据
            this.items[_i] = {
              ...this.items[_i],
              ...obj
            }
    
            if(this.isCoverEdit || obj.isFm) {
              // 封面编辑
              this.fmItems = this.items
            } else {
              // 视频编辑
              this.spPart[this.spPartIndex] = this.items
            }

            resolve()
            call && call() 
          }, 500)
                     
          
        } 
      })
    
    },

    // 改变图片层级
    changeZIndex(isAdd) {

      let _zIndex = this.items[this.index].zIndex
      isAdd ? _zIndex += 1 : _zIndex -= 1

      // 循环图片数组获取点击的图片信息
      for (let i = 0; i < this.items.length; i++) {  
        if (_zIndex == this.items[i].zIndex) {
          isAdd ? this.items[i].zIndex -= 1 : this.items[i].zIndex += 1
          break;
        }
      }

      // 上移一层 | 下移一层
      isAdd ? this.items[this.index].zIndex += 1 : this.items[this.index].zIndex -= 1

      if(this.items[this.index].zIndex > this.items.length-1) {
        this.items[this.index].zIndex = this.items.length-1;

        this.$message('已是最顶层');

      }

      if(this.items[this.index].zIndex < 1) {
        this.items[this.index].zIndex = 1;

        this.$message('已是最底层');
      
      }

      if(this.isCoverEdit) {
        this.fmItems = this.items
      } else {
        this.spPart[this.spPartIndex] = this.items
      }

    },

    wrapStart(e) {
      console.log('wrapStart')
      this.drag = true;
    },

    wrapMove(e) {
      if(this.drag) {
        console.log('wrapMove')
        if(this.items[this.index].active){
          if(this.items[this.index].sMoveabled) {
            console.log('wrapMove-sMoveabled')
            this.oTouchMove(e);
          } else if (this.items[this.index].moveabled) {
            console.log('wrapMove-moveabled')
            this.wraptouchMove(e);
          } else if (this.items[this.index].lMoveabled) {
            console.log('wrapMove-lMoveabled')
            this.oLwhMove(e, this.items[this.index].id, this.items[this.index].lMoveType);
          }
        }
      }
    },

    wrapUp(e) {
      console.log('wrapUp')
      this.drag = false;
      this.oTouchUp();
    },

    // 点击图层
    wraptouchStart (e, id) {
      console.log('点击图层',e)

      // 循环图片数组获取点击的图片信息
      for (let i = 0; i < this.items.length; i++) {
        this.items[i].active = false;
        this.items[i].moveabled = false;
        this.items[i].sMoveabled = false;


        if (id == this.items[i].id) {
          console.log(id)
          this.index = i;
          this.items[this.index].active = true;
          this.items[this.index].moveabled = true;
        }
      }

      let editType = '';

      if(this.items[this.index].type == 'bg') {
        editType = 'bg'
      } 
      else if(this.items[this.index].type == 'ai') {
        editType = 'ai'
      }
      else if(this.items[this.index].type == 'tt') {
        editType = 'tt'
      }
      else if(this.items[this.index].type == 'bt') {
        editType = 'bt'

        this.btAttribute = {
          color: this.items[this.index].fontColor,
          align: this.items[this.index].align,
          size: this.items[this.index].fontSize,
          family: this.items[this.index].fontFamily,
          bgColor: this.items[this.index].fontBgColor,
          showStroke:this.items[this.index].strokeShow,
          strokeSize: this.items[this.index].strokeSize,
          strokeColor: this.items[this.index].strokeColor
        }
        
      }
      else if(this.items[this.index].type == 'zm') {
        editType = 'zm'

        this.zmAttribute = {
          color: this.items[this.index].fontColor,
          align: this.items[this.index].align,
          size: this.items[this.index].fontSize,
          family: this.items[this.index].fontFamily,
          bgColor: this.items[this.index].fontBgColor,
          showStroke: this.items[this.index].strokeShow,
          strokeSize: this.items[this.index].strokeSize,
          strokeColor: this.items[this.index].strokeColor,
          show: this.items[this.index].show
        }
        
      }

      if(this.isCoverEdit) {
        this.fmItems = this.items
      } else {
        this.spPart[this.spPartIndex] = this.items
      }

      this.editType = editType
      this.isShowEditInput = false
      this.isInitEdit = false
      
      if(this.items[this.index].type == 'bg') {
        return
      }
    
      // 获取点击的坐标值
      this.items[this.index].lx = e.pageX;
      this.items[this.index].ly = e.pageY;

      this.items = JSON.parse(JSON.stringify(this.items))

    },

    // 拖动图层
    wraptouchMove (e, id) {

      let { items, index } = this;

      if(!items[index].moveabled) {
        return
      }

      console.log('拖动图层', e, id)

      items[index]._lx = e.pageX;
      items[index]._ly = e.pageY;
  
      // if(items[index].type == 'zm') {

      //   items[index].top += items[index]._ly - items[index].ly;

      //   items[index].y += items[index]._ly - items[index].ly;
      // } else {

        items[index].left += items[index]._lx - items[index].lx;
        items[index].top += items[index]._ly - items[index].ly;
        items[index].x += items[index]._lx - items[index].lx;
        items[index].y += items[index]._ly - items[index].ly;
      // }
  
      items[index].lx = e.pageX;
      items[index].ly = e.pageY;

      this.items = items
      if(this.isCoverEdit) {
        this.fmItems = this.items
      } else {
        this.spPart[this.spPartIndex] = this.items
      }
      
    },

    // 点击旋转图标
    oScaleStart (e, id) {
      // 找到点击的那个图片对象,并记录
      for (let i = 0; i < this.items.length; i++) {
        this.items[i].active = false;
        if (id == this.items[i].id) {
          this.index = i;
          this.items[this.index].active = true;
        }
      }

      // 获取作为移动前角度的坐标
      this.items[this.index].tx = e.pageX - this.toucheWrap.left;
      this.items[this.index].ty = e.pageY - this.toucheWrap.top;

      // 移动前的角度
      this.items[this.index].anglePre = this.countDeg(
        this.items[this.index].x, 
        this.items[this.index].y, 
        this.items[this.index].tx, 
        this.items[this.index].ty
      );
      
    },

    // 移动旋转图标
    oScaleMove (e, id) {

      let { items, index } = this;
      
      // 记录移动后的位置
      items[index]._tx = e.pageX - this.toucheWrap.left;
      items[index]._ty = e.pageY - this.toucheWrap.top;

      // 移动的点到圆心的距离
      items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10)
  
      // 移动后位置的角度
      items[index].angleNext = this.countDeg(items[index].x, items[index].y, items[index]._tx, items[index]._ty)
      // 角度差
      items[index].new_rotate = items[index].angleNext - items[index].anglePre;
  
      //叠加的角度差
      items[index].rotate += items[index].new_rotate;
      items[index].angle = items[index].type == 'tt' ? items[index].rotate : 0; //赋值
  
      //用过移动后的坐标赋值为移动前坐标
      items[index].tx = e.pageX - this.toucheWrap.left;
      items[index].ty = e.pageY - this.toucheWrap.top;

      // 下次移动前的角度
      items[index].anglePre = this.countDeg(items[index].x, items[index].y, items[index].tx, items[index].ty)

      this.items = items;
      if(this.isCoverEdit) {
        this.fmItems = items
      } else {
        this.spPart[this.spPartIndex] = items
      }
  
    },

    // 点击伸缩图标
    oTouchStart (e, id) {
      console.log('点击伸缩图标')

      // 找到点击的那个图片对象,并记录
      for (let i = 0; i < this.items.length; i++) {
        this.items[i].active = false;
        this.items[i].moveabled = false;
        this.items[i].sMoveabled = false;

        if (id == this.items[i].id) {
          this.index = i;
          this.items[this.index].active = true;
          this.items[this.index].sMoveabled = true;
        }
      }

      // 获取作为移动前的坐标
      this.items[this.index].tx = e.pageX - this.toucheWrap.left;
      this.items[this.index].ty = e.pageY - this.toucheWrap.top;

      // 获取图片半径
      this.items[this.index].r = this.getDistancs(
        this.items[this.index].x, 
        this.items[this.index].y, 
        this.items[this.index].tx, 
        this.items[this.index].ty
      );
      
    },

    // 移动伸缩图标
    oTouchMove (e, id) {
      console.log('移动伸缩图标')

      let { items, index, toucheWrap, sDpi } = this;

      if(!items[index].sMoveabled) {
        return
      }
      
      // 记录移动后的位置
      items[index]._tx = e.pageX - toucheWrap.left;
      items[index]._ty = e.pageY - toucheWrap.top;

      // 移动的点到圆心的距离
      items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10);

      let _s = items[index].disPtoO / items[index].r;

      if(items[index].type == 'bt' || items[index].type == 'zm') {
        let _oldFontSize = items[index].fontSize;

        items[index].fontSize = items[index].fontSize * _s;

        let maxFontSize = items[index].type == 'zm' ? 40/sDpi : 100/sDpi;

        if(items[index].fontSize > maxFontSize) {
          items[index].fontSize = maxFontSize;
          _s = 100/_oldFontSize
        } else if(items[index].fontSize < 12/sDpi) {
          items[index].fontSize = 12/sDpi;
          _s = 12/(sDpi*_oldFontSize)
        }

        if(items[index].type == 'bt') {
          this.btAttribute = {
            color: items[index].fontColor,
            align: items[index].align,
            size: items[index].fontSize,
            family: items[index].fontFamily,
            bgColor: items[index].fontBgColor,
            showStroke: items[index].strokeShow,
            strokeSize: items[index].strokeSize,
            strokeColor: items[index].strokeColor
          }
          
        } else {
          this.zmAttribute = {
            color: items[index].fontColor,
            align: items[index].align,
            size: items[index].fontSize,
            family: items[index].fontFamily,
            bgColor: items[index].fontBgColor,
            showStroke: items[index].strokeShow,
            strokeSize: items[index].strokeSize,
            strokeColor: items[index].strokeColor,
            show: items[index].show
          }
          
        }
        
      }

      // 使用缩放
      // items[index].scale = items[index].disPtoO / items[index].r;
  
      // 不使用缩放
      items[index].width =  _s * items[index].width
      items[index].height =  _s * items[index].height
      items[index].top = items[index].y - items[index].height/2
      items[index].left = items[index].x - items[index].width/2
      

      // 获取图片半径
      items[index].r = items[index].disPtoO;

      this.items = items;
      if(this.isCoverEdit) {
        this.fmItems = items
      } else {
        this.spPart[this.spPartIndex] = items
      }

    },

    // 鼠标取消
    oTouchUp (e, id) {
      console.log('oTouchUp')
      this.items[this.index].lMoveabled = false;
      this.items[this.index].sMoveabled = false;
      this.items[this.index].moveabled = false;
    },

    // 点击文字拉宽高图标
    oLwhStart (e, id, _type) {
      // 找到点击的那个图片对象,并记录
      for (let i = 0; i < this.items.length; i++) {
        this.items[i].active = false;
        this.items[i].moveabled = false;
        this.items[i].sMoveabled = false;
        this.items[i].lMoveabled = false;

        if (id == this.items[i].id) {
          this.index = i;
          this.items[this.index].active = true;
          this.items[this.index].lMoveabled = true;
          this.items[this.index].lMoveType = _type;
        }
      }

      // 获取作为移动前的坐标
      this.items[this.index].tx = e.pageX - this.toucheWrap.left;
      this.items[this.index].ty = e.pageY - this.toucheWrap.top;

      // 获取触摸点到圆心距离
      this.items[this.index].r = this.getDistancs(
        this.items[this.index].x, 
        this.items[this.index].y, 
        this.items[this.index].tx, 
        this.items[this.index].ty
      );
      
    },

    // 移动文字拉宽高图标
    oLwhMove (e, id, _type) {

      let { items, index, toucheWrap } = this;

      if(!items[index].lMoveabled) {
        return
      }
      
      // 记录移动后的位置
      items[index]._tx = e.pageX - toucheWrap.left;
      items[index]._ty = e.pageY - toucheWrap.top;

      // 移动的点到圆心的距离
      items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10)
      let _s = items[index].disPtoO / items[index].r;
  
      // 不使用缩放
      if(_type == 'w') {
        items[index].width =  _s * items[index].width
        items[index].left = items[index].x - items[index].width/2
      
      } else {
        items[index].height =  _s * items[index].height
        items[index].top = items[index].y - items[index].height/2 
      }
      

      // 获取触摸点到圆心距离
      items[index].r = items[index].disPtoO;

      this.items = items;
      if(this.isCoverEdit) {
        this.fmItems = items
      } else {
        this.spPart[this.spPartIndex] = items
      }

    },

    // 删除图层对象
    deleteItem (id) {
      let newList = [];
      for (let i = 0; i < this.items.length; i++) {
        // 更新层级
        if(this.items[i].zIndex > this.items[this.index].zIndex) {
          this.items[i].zIndex -= 1
        }
        if (id != this.items[i].id) {
          newList.push(this.items[i])
        }

      }

      if (newList.length > 0) {
        newList[newList.length - 1].active = true; // 剩下图片组最后一个选中
        this.index = newList.length - 1
      } else {
        this.index = 0
      }
      
      this.items = newList;

      if(this.isCoverEdit) {
        this.fmItems = this.items
      } else {
        this.spPart[this.spPartIndex] = this.items
      }

    },

    // 计算坐标点到圆心的距离
    getDistancs (cx, cy, pointer_x, pointer_y) {
      var ox = pointer_x - cx;
      var oy = pointer_y - cy;
      return Math.sqrt(
        ox * ox + oy * oy
      );
    },

    /*
    * 参数cx和cy为图片圆心坐标
    * 参数pointer_x和pointer_y为手点击的坐标
    * 返回值为手点击的坐标到圆心的角度
    */
    countDeg (cx, cy, pointer_x, pointer_y) {
      var ox = pointer_x - cx;
      var oy = pointer_y - cy;
      var to = Math.abs(ox / oy);
      var angle = Math.atan(to) / (2 * Math.PI) * 360;

      // 相对在左上角,第四象限,js中坐标系是从左上角开始的,这里的象限是正常坐标系 
      if (ox < 0 && oy < 0) {
        angle = -angle;
      } 
      // 左下角,3象限 
      else if (ox <= 0 && oy >= 0) {
        angle = -(180 - angle)
      } 
      // 右上角,1象限 
      else if (ox > 0 && oy < 0) {
        angle = angle;
      } 
      // 右下角,2象限 
      else if (ox > 0 && oy > 0) {
        angle = 180 - angle;
      }

      return angle;
    },


    fetchError (msg) {

      this.loading.close();

      if(msg) {
        this.$message.error(msg);
      }
    }
  }
}
</script>

Supongo que te gusta

Origin blog.csdn.net/qq_31851435/article/details/131676138
Recomendado
Clasificación