Heim  >  Artikel  >  Web-Frontend  >  Vue-Übung: Verwenden Sie benutzerdefinierte Anweisungen, um den Effekt des Ziehens von Elementen mit der Maus zu erzielen

Vue-Übung: Verwenden Sie benutzerdefinierte Anweisungen, um den Effekt des Ziehens von Elementen mit der Maus zu erzielen

青灯夜游
青灯夜游nach vorne
2022-09-13 19:34:292551Durchsuche

Dieser Artikel beschreibt einen tatsächlichen Kampf von Vue und stellt die Verwendung der benutzerdefinierten Anweisungen von Vue vor, um den Effekt des Ziehens von Elementen mit der Maus zu erzielen und das Problem der Anpassung an mobile Endgeräte zu lösen.

Vue-Übung: Verwenden Sie benutzerdefinierte Anweisungen, um den Effekt des Ziehens von Elementen mit der Maus zu erzielen

Kernattribute

  • Element.clientWidthElement.clientWidth:元素可视宽度。
  • Element.clientHeight:元素可视高度。
  • MouseEvent.clientX:鼠标相对于浏览器左上顶点的水平坐标。
  • MouseEvent.clientY:鼠标相对于浏览器左上顶点的垂直坐标。
  • Touch.clientX:触点相对于浏览器左上顶点的水平坐标(移动端属性)。
  • Touch.clientY:触点相对于浏览器左上顶点的垂直坐标(移动端属性)。
  • HTMLElement.offsetLeft:当前元素左上角相对于父节点(HTMLElement.offsetParent)的左边偏移的距离。当元素脱离文档流时(position: fixed)则相对于原点(浏览器左上顶点)偏移。【相关推荐:vuejs视频教程
  • HTMLElement.offsetTop:当前元素左上角相对于父节点(HTMLElement.offsetParent)的顶部偏移的距离。当元素脱离文档流时(position: fixed)则相对于原点(浏览器左上顶点)偏移。
  • Element.style.top:可读可写,值为 offsetTop
  • Element.style.left:可读可写,值为 offsetLeft

Vue-Übung: Verwenden Sie benutzerdefinierte Anweisungen, um den Effekt des Ziehens von Elementen mit der Maus zu erzielen

实现思路

待滑动元素必须设置 position: fixed or absolute

元素滑动需要依赖于鼠标的移动,鼠标的移动位置决定了元素滑动的位置,元素的位置是通过调整左上顶点坐标来的,所以我们要知道元素滑动后的左上顶点坐标,这样才能将元素移动到指定位置(鼠标悬停的位置)。

首先要计算出鼠标在移动元素前相对元素的位置 (x, y)

// 鼠标当前的位置减去元素当前的位置
(x, y) = (e.clientX - el.offsetLeft, e.clientY - el.offsetTop)

鼠标相对元素位置是指相对于元素左上顶点的位置。

e 指鼠标事件,el 指滑动的元素。

知道了鼠标的相对位置,后续的鼠标移动,只要知道移动后的鼠标坐标,就能很容易的把元素的左上顶点坐标算出来。

计算元素移动后的左上顶点坐标 (x', y')

// 鼠标当前的位置减去滑动前的相对位置
(x‘, y’) = (e.clientX - x, e.clientY - y)

(x', y') 就是要移动的最终坐标,然后调整元素位置即可

el.style.left = x' + 'px'
el.style.top = y' + 'px'

代码

<template>
	<div>
	  <!-- 省略... -->
	</div>
</template>

<script>
export default {
  data() {
    return {
      isDrag: false
  },
  methods: {
    click() {
      if (this.isDrag) {
        return
      }

      // 省略...
    }
  },
  directives: {
    drag(el, binding, vnode) {
      /**
       * 获取客户端可见内容的高度
       *
       * @returns {number}
       */
      const getClientHeight = () => {
        return window.innerHeight || Math.min(document.documentElement.clientHeight, document.body.clientHeight)
      }

      /**
       * 获取客户端可见内容的宽度
       *
       * @returns {number}
       */
      const getClientWidth = () => {
        return window.innerWidth || Math.min(document.documentElement.clientWidth, document.body.clientWidth)
      }

      /**
       * startX = null:获取鼠标相对于元素(左上顶点)的x轴坐标(移动前坐标)
       * startX != null:获取移动后的左上顶点x轴坐标
       *
       * e.clientX:鼠标相对客户端(客户端左上顶点)的x轴坐标
       * el.offsetLeft:元素顶点(左上顶点)相对客户端(客户端左上顶点)的x轴坐标(元素必须脱离文档流,position: fixed or absolute)
       * el.clientWidth:元素宽度
       *
       * @param el
       * @param e
       * @param startX
       * @returns {number}
       */
      const getX = (el, e, startX) => {
        if (startX === null) {
          // 返回鼠标相对于元素(左上顶点)的x轴坐标
          return e.clientX - el.offsetLeft
        }

        // 客户端可视宽度
        const clientWidth = getClientWidth()
        // 元素自身宽度
        const elWidth = el.clientWidth

        // 移动到x轴位置
        let x = e.clientX - startX
        // 水平方向边界处理
        if (x <= 0) {
          // x轴最小为0
          x = 0
        } else if (x + elWidth > clientWidth) {
          // x是左上顶点的坐标,是否触碰到右边边界(超出可视宽度)要通过右顶点判断,所以需要加上元素自身宽度
          x = clientWidth - elWidth
        }

        return x
      }

      /**
       * startY = null:获取鼠标相对于元素(左上顶点)的y轴坐标(移动前坐标)
       * startY != null:获取移动后的左上顶点y轴坐标
       *
       * e.clientY:鼠标相对客户端(客户端左上顶点)的y轴坐标
       * el.offsetTop:元素顶点(左上顶点)相对客户端(客户端左上顶点)的y轴坐标(元素必须脱离文档流,position: fixed or absolute)
       * el.clientHeight:元素高度
       *
       * @param el
       * @param e
       * @param startY
       * @returns {number}
       */
      const getY = (el, e, startY) => {
        if (startY === null) {
          // 返回鼠标相对于元素(左上顶点)的y轴坐标
          return e.clientY - el.offsetTop
        }

        // 客户端可视高度
        const clientHeight = getClientHeight()
        // 元素自身高度
        const elHeight = el.clientHeight

        // 移动到y轴位置
        let y = e.clientY - startY
        // 垂直方向边界处理
        if (y <= 0) {
          // y轴最小为0
          y = 0
        } else if (y + elHeight > clientHeight) {
          // 同理,判断是否超出可视高度要加上自身高度
          y = clientHeight - elHeight
        }

        return y
      }

      /**
       * 监听鼠标按下事件(PC端拖动)
       *
       * @param e
       */
      el.onmousedown = (e) => {
        vnode.context.isDrag = false

        // 获取当前位置信息 (startX,startY)
        const startX = getX(el, e, null)
        const startY = getY(el, e, null)

        /**
         * 监听鼠标移动事件
         *
         * @param e
         */
        document.onmousemove = (e) => {
          // 标记正在移动,解决元素移动后点击事件被触发的问题
          vnode.context.isDrag = true

          // 更新元素位置(移动元素)
          el.style.left = getX(el, e, startX) + &#39;px&#39;
          el.style.top = getY(el, e, startY) + &#39;px&#39;
        }

        /**
         * 监听鼠标松开事件
         */
        document.onmouseup = () => {
          // 移除鼠标相关事件,防止元素无法脱离鼠标
          document.onmousemove = document.onmouseup = null
        }
      }

      /**
       * 监听手指按下事件(移动端拖动)
       * @param e
       */
      el.ontouchstart = (e) => {
        // 获取被触摸的元素
        const touch = e.targetTouches[0]
        // 获取当前位置信息 (startX,startY)
        const startX = getX(el, touch, null)
        const startY = getY(el, touch, null)

        /**
         * 监听手指移动事件
         * @param e
         */
        document.ontouchmove = (e) => {
          // 获取被触摸的元素
          const touch = e.targetTouches[0]
          // 更新元素位置(移动元素)
          el.style.left = getX(el, touch, startX) + &#39;px&#39;
          el.style.top = getY(el, touch, startY) + &#39;px&#39;
        }

        /**
         * 监听手指移开事件
         */
        document.ontouchend = () => {
          // 移除touch相关事件,防止元素无法脱离手指
          document.ontouchmove = document.ontouchend = null
        }
      }
    }
  }
}
</script>

<style>
    .ball-wrap {
	position: fixed;
    }
</style>

drag 是我们自定义的指令,在需要滑动的元素上绑定 v-drag 即可。

注意

自定义指令this指向问题

在自定义指令 directives 内不能访问 this,如果需要修改 data 里的值,需要通过 vnode.context.字段名 = 值 修改。

滑动后点击事件被触发

鼠标事件触发顺序:

mouseover - mousedown - mouseup - click - mouseout

滑动的前提是鼠标必须按下再滑动,所以在我们滑动完毕松开鼠标时,click 事件会被触发。

解决方法:定义一个标志变量,表示是否是滑动,点击事件执行时,将此变量作为前置条件,如果是在滑动则不执行

// ...

data() 
  return {
    isDrag: false
  }
}

// ...

el.onmousedown = (e) => {
	// ...
	vnode.context.isDrag = false
	document.onmousemove = (e) => {
    	// 标记正在移动,解决元素移动后点击事件被触发的问题
       	vnode.context.isDrag = true
       	// ...
    }
}

// ...

methods: {
	click() {
	  if (this.isDrag) {
	    return
	  }

	  // ...
	}
}

移动端滑动问题

移动端滑动时会触发默认事件,导致滑动卡顿。

在要触发滑动的元素上加上 @touchmove.prevent: Sichtbare Breite des Elements.

Element.clientHeight: Die sichtbare Höhe des Elements.

MouseEvent.clientX
: Die horizontale Koordinate der Maus relativ zum oberen linken Scheitelpunkt des Browsers.

MouseEvent.clientY
: Die vertikale Koordinate der Maus relativ zum oberen linken Scheitelpunkt des Browsers.

Touch.clientX: Die horizontale Koordinate des Berührungspunkts relativ zum oberen linken Scheitelpunkt des Browsers (mobile Eigenschaft). Touch.clientY

: Die vertikale Koordinate des Berührungspunkts relativ zum oberen linken Scheitelpunkt des Browsers (mobile Eigenschaft). 🎜🎜HTMLElement.offsetLeft🎜: Der Abstand der oberen linken Ecke des aktuellen Elements relativ zum linken Versatz des übergeordneten Knotens (HTMLElement.offsetParent) . Wenn ein Element den Dokumentfluss verlässt (position: behoben), wird es relativ zum Ursprung (dem oberen linken Scheitelpunkt des Browsers) versetzt. [Verwandte Empfehlungen: vuejs Video-Tutorial🎜]🎜 🎜HTMLElement.offsetTop🎜: Der Versatzabstand der oberen linken Ecke des aktuellen Elements relativ zur Oberseite des übergeordneten Knotens (HTMLElement.offsetParent). Wenn ein Element den Dokumentfluss verlässt (position: behoben), wird es relativ zum Ursprung (dem oberen linken Scheitelpunkt des Browsers) versetzt. 🎜🎜Element.style.top🎜: Lesbar und beschreibbar, der Wert ist offsetTop. 🎜🎜Element.style.left🎜: Lesbar und beschreibbar, der Wert ist offsetLeft. 🎜2. png🎜

🎜Implementierungsidee🎜🎜🎜🎜Das zu verschiebende Element muss festgelegt werden Position: fest oder absolut🎜🎜🎜Element, das verschoben werden muss sich auf die Maus verlassen Die Bewegung der Maus bestimmt die Gleitposition des Elements. Die Position des Elements wird durch Anpassen der Koordinaten des oberen linken Scheitelpunkts bestimmt. Daher müssen wir die Koordinaten des oberen linken Scheitelpunkts des Elements kennen nach dem Verschieben, sodass das Element an die angegebene Position verschoben werden kann (Mouseover-Position). 🎜🎜Berechnen Sie zunächst die Position der Maus relativ zum Element, bevor Sie das Element (x, y) verschieben: 🎜rrreee🎜Die Position der Maus relativ zum Element bezieht sich auf die Position relativ zum oberer linker Scheitelpunkt des Elements. 🎜🎜e bezieht sich auf das Mausereignis und el bezieht sich auf das Schiebeelement. 🎜🎜Wenn Sie die relative Position der Maus und die nachfolgenden Mausbewegungen kennen und die Koordinaten der Maus nach der Bewegung kennen, können Sie die Koordinaten des oberen linken Scheitelpunkts des Elements leicht berechnen. 🎜🎜Berechnen Sie die Koordinaten des oberen linken Scheitelpunkts (x', y') des Elements nach der Bewegung: 🎜rrreee🎜(x', y') ist das Finale Koordinaten, die verschoben werden sollen, und passen Sie dann einfach die Position des Elements an zu dem Element, das verschoben werden muss, genügt v-drag. 🎜

🎜Achtung🎜🎜

🎜Benutzerdefinierte Direktive weist auf das Problem hin🎜

🎜In der benutzerdefinierten Direktive Auf die Anweisungen <code>this kann innerhalb von nicht zugegriffen werden. Wenn Sie den Wert in data ändern müssen, müssen Sie ihn über den Namen vnode.context.Field ändern = Wert. 🎜

🎜Das Klickereignis wird nach dem Schieben ausgelöst🎜

🎜Mausereignis-Auslösesequenz: 🎜🎜mouseover - Mousedown - Mouseup - Click - Mouseout 🎜 🎜Voraussetzung für das Schieben ist, dass die Maus gedrückt und dann gerutscht werden muss. Wenn wir also die Maus nach dem Schieben loslassen, wird das Ereignis click ausgelöst. 🎜🎜Lösung: 🎜Definieren Sie eine Flag-Variable, um anzugeben, ob das Klickereignis ausgeführt wird. Wenn es gleitet, wird es nicht ausgeführt. 🎜rrreee

🎜Gleitproblem auf der mobilen Seite🎜

🎜Das Standardereignis wird ausgelöst, wenn die mobile Seite gleitet, wodurch das Gleiten einfriert. 🎜🎜Fügen Sie @touchmove.prevent zum Element hinzu, um das Verschieben auszulösen und das Eintreten des Standardereignisses zu verhindern. 🎜🎜🎜Quellcode🎜🎜🎜🎜https://github.com/anlingyi/xeblog-vue/blob/master/src/components/xe-pokeball/index.vue🎜🎜🎜(Lernvideo-Sharing: 🎜web front- Ende der Entwicklung🎜,🎜Grundlegendes Programmiervideo🎜)🎜

Das obige ist der detaillierte Inhalt vonVue-Übung: Verwenden Sie benutzerdefinierte Anweisungen, um den Effekt des Ziehens von Elementen mit der Maus zu erzielen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen