vue项目中实现标签元素的拖动,缩放,旋转等操作

33 篇文章 0 订阅
29 篇文章 2 订阅

一、鼠标事件

  • click:按下鼠标(通常是按下主按钮)时触发。
  • dblclick:在同一个元素上双击鼠标时触发。
  • mousedown按下鼠标键时触发。
  • mouseup释放按下的鼠标键时触发。
  • mousemove当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。
  • mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件(详见后文)。
  • mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件(详见后文)。
  • mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件(详见后文)。
  • mouseleave鼠标离开一个节点时触发,离开父节点不会触发这个事件(详见后文)。
  • contextmenu:按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文菜单键”时触发。
  • wheel:滚动鼠标的滚轮时触发,该事件继承的是WheelEvent接口。

click事件:用户在同一个位置先完成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:针对自己的左上角坐标,从padding开始,计算相对于本元素的左上角,不在乎定位问题,计算的是内交点。是IE浏览器的特有属性。
  • layerX,layerY:往上找有定位属性的父元素的左上角,受元素的定位的影响,会从本元素往上找到第一个定位的元素的左上角(自身有定位属性的话就是相对于自身),都没有的话,就是相对于body的左上角。
  • offsetWidth,offsetHeight:获取元素的整个的宽度和高度,包括内容区、内边距和边框。
  • clientWidth,clientHeight:这两个属性可以获取元素的可见宽度和高度,会获取元素宽度和高度,包括内容区和内边距。
  • offsetParent:可以用来获取当前元素的定位父元素的定位(开启了定位的祖先元素),没有开启定位,则返回body。
  • offsetLeft,offsetTop:当前元素相对于其定位父元素的水平和垂直偏移量。
  • scrollWidth,scrollHeight:可以获取元素整个滚动区域的宽度和高度。
  • scrollLeft,scrollTop:可以获取水平和垂直滚动条滚动的距离。
  • screenX,screenY:数值,鼠标相对于屏幕的左上角坐标(单位像素),默认值为0,设置该属性不会移动鼠标。
  • ctrlKey:布尔值,是否同时按下了 Ctrl 键,默认值为false。
  • shiftKey:布尔值,是否同时按下了 Shift 键,默认值为false。
  • altKey:布尔值,是否同时按下 Alt 键,默认值为false。
  • metaKey:布尔值,是否同时按下 Meta 键,默认值为false。
  • button:数值,表示按下了哪一个鼠标按键,默认值为0,表示按下主键(通常是鼠标的左键)或者当前事件没有定义这个属性;1表示按下辅助键(通常是鼠标的中间键),2表示按下次要键(通常是鼠标的右键)。
  • buttons:数值,表示按下了鼠标的哪些键,是一个三个比特位的二进制值,默认为0(没有按下任何键)。1(二进制001)表示按下主键(通常是左键),2(二进制010)表示按下次要键(通常是右键),4(二进制100)表示按下辅助键(通常是中间键)。因此,如果返回3(二进制011)就表示同时按下了左键和右键。
  • relatedTarget:节点对象,表示事件的相关节点,默认为null。mouseenter和mouseover事件时,表示鼠标刚刚离开的那个元素节点;mouseout和mouseleave事件时,表示鼠标正在进入的那个元素节点。

二、实现逻辑

要实现标签的鼠标选中以及按住鼠标后拖动的效果,首先选中标签可以用mousedown事件,然后按住鼠标的话,就可以在mousedown事件触发的时候,设置一个可以拖动的标示,比如moveabled=true,然后在mousemove事件里,判断这个moveabled是否为true,为true则表示可以拖动,然后就可以根据获取到的坐标更改标签元素的top和left,从而实现移动,为false则什么都不执行,然后如果鼠标松开的话,则会触发mouseup事件,在这个事件里把moveabled设置为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; 防止其text选中移动影响mouseup事件。

2. 可以触发mousemove事件的区域过小

描述:如果我们把mousemove事件绑定在移动标签元素上,有可能因为标签本身大小不够大,导致在移动的时候,鼠标跑到标签区域外,从而造成事件丢失,移动不顺畅的体验。

解决方案:把mousemove事件绑定在可以移动的最大区域的父标签上。

三、案例代码

<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>

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值