Vue實現拖拽穿梭框功能四種方式

来源:https://www.cnblogs.com/mochenxiya/archive/2022/09/28/16739918.html
-Advertisement-
Play Games

一、使用原生js實現拖拽 點擊打開視頻講解更加詳細 <html lang="en"> <head> <meta charset="UTF-8" /> <title>Lazyload</title> <style> .drag { background-color: skyblue; position ...


一、使用原生js實現拖拽

點擊打開視頻講解更加詳細

<html lang="en">
    <head>
      <meta charset="UTF-8" />
      <title>Lazyload</title>
      <style>
        .drag {
          background-color: skyblue;
          position: absolute;
          line-height: 100px;
          text-align: center;
          width: 100px;
          height: 100px;
        }
      </style>
    </head>
    <body>
      <!-- left和top要寫在行內樣式裡面 -->
      <div class="drag" style="left: 0; top: 0">按住拖動</div>
      <script src="./jquery-3.6.0.min.js"></script>
      <script>
        // 獲取DOM元素
        let dragDiv = document.getElementsByClassName('drag')[0]
        // 滑鼠按下事件 處理程式
        let putDown = function (event) {
          dragDiv.style.cursor = 'pointer'
          let offsetX = parseInt(dragDiv.style.left) // 獲取當前的x軸距離
          let offsetY = parseInt(dragDiv.style.top) // 獲取當前的y軸距離
          let innerX = event.clientX - offsetX // 獲取滑鼠在方塊內的x軸距
          let innerY = event.clientY - offsetY // 獲取滑鼠在方塊內的y軸距
          // 按住滑鼠時為div添加一個border
          dragDiv.style.borderStyle = 'solid'
          dragDiv.style.borderColor = 'red'
          dragDiv.style.borderWidth = '3px'
          // 滑鼠移動的時候不停的修改div的left和top值
          document.onmousemove = function (event) {
            dragDiv.style.left = event.clientX - innerX + 'px'
            dragDiv.style.top = event.clientY - innerY + 'px'
            // 邊界判斷
            if (parseInt(dragDiv.style.left) <= 0) {
              dragDiv.style.left = '0px'
            }
            if (parseInt(dragDiv.style.top) <= 0) {
              dragDiv.style.top = '0px'
            }
            if (
              parseInt(dragDiv.style.left) >=
              window.innerWidth - parseInt(dragDiv.style.width)
            ) {
              dragDiv.style.left =
                window.innerWidth - parseInt(dragDiv.style.width) + 'px'
            }
            if (
              parseInt(dragDiv.style.top) >=
              window.innerHeight - parseInt(dragDiv.style.height)
            ) {
              dragDiv.style.top =
                window.innerHeight - parseInt(dragDiv.style.height) + 'px'
            }
          }
          // 滑鼠抬起時,清除綁定在文檔上的mousemove和mouseup事件
          // 否則滑鼠抬起後還可以繼續拖拽方塊
          document.onmouseup = function () {
            document.onmousemove = null
            document.onmouseup = null
            // 清除border
            dragDiv.style.borderStyle = ''
            dragDiv.style.borderColor = ''
            dragDiv.style.borderWidth = ''
          }
        }
        // 綁定滑鼠按下事件
        dragDiv.addEventListener('mousedown', putDown, false)
      </script>
    </body>
  </html>

二、VUe使用js實現拖拽穿梭框

<template>
  <div>
    <h3 style="text-align: center">拖拽穿梭框</h3>
    <div id="home" @mousemove="mousemove($event)">
      <div class="tree-select-content">
        <span
          class="select-content"
          :id="'mouse' + index"
          v-for="(item, index) in leftData"
          :key="item.id"
          @mousedown="mousedown(index, 1)"
          @mouseup="mouseup(item, 1, index)"
        >
          <span class="select-text">{{ item.label }}</span>
          <span class="select-text-X" @click="handerClickX(item, index, 1)"
            >X</span
          >
        </span>
      </div>
      <div class="tree-select-content">
        <span
          class="select-content"
          :id="'deleteMouse' + index"
          v-for="(item, index) in rightData"
          :key="item.id"
          @mousedown="mousedown(index, 2)"
          @mouseup="mouseup(item, 2, index)"
        >
          <span class="select-text">{{ item.label }}</span>
          <span class="select-text-X" @click="handerClickX(item, index, 2)"
            >X</span
          >
        </span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "home",
  data() {
    return {
      leftData: [
        { label: "首頁", id: 1 },
        { label: "咨詢", id: 2 },
        { label: "生活", id: 3 },
        { label: "財富", id: 4 },
        { label: "我的", id: 5 },
      ],
      rightData: [{ label: "世界", id: 6 }],
      isMoveTrue: false,
      isMove: false,
      moveId: "",
    };
  },
  mounted() {},
  components: {},
  methods: {
    mousedown(index, val) {
      this.isMoveTrue = true;
      if (val == 1) {
        this.moveId = "mouse" + index;
      } else {
        this.moveId = "deleteMouse" + index;
      }
    },
    mousemove(event) {
      if (this.isMoveTrue) {
        this.isMove = true;
        document.getElementById(this.moveId).style.position = "absolute";
        document.getElementById(this.moveId).style.top = event.clientY + "px";
        document.getElementById(this.moveId).style.left = event.clientX + "px";
        document.getElementById(this.moveId).style.transform =
          "translate(-50%,-50%)";
      }
    },
    mouseup(item, val, index) {
      if (!this.isMove) {
        this.isMoveTrue = false;
        this.moveId = "";
      }
      if (this.isMoveTrue && val == 2) {
        this.$nextTick(() => {
          this.rightData.splice(index, 1);
          this.leftData.push(item);
        });
      } else if (this.isMoveTrue && val) {
        this.leftData.splice(index, 1);
        this.rightData.push(item);
      }
      document.getElementById(this.moveId).style.display = "none";
      this.isMoveTrue = false;
      this.isMove = false;
      this.moveId = "";
    },
    handerClickX(item, index, val) {
      if (val == 1) {
        this.leftData.splice(index, 1);
        this.rightData.push(item);
      } else {
        this.rightData.splice(index, 1);
        this.leftData.push(item);
      }
    },
  },
};
</script>

<style scoped>
#home {
  display: flex;
  justify-content: space-around;
}
.tree-select-content {
  width: 40%;
  height: 300px;
  background: #f9faff;
  border: 1px solid #dee0ec;
  border-radius: 4px;
  display: flex;
  flex-wrap: wrap;
  align-content: baseline;
}
.select-content {
  width: max-content;
  height: 20px;
  padding: 1.6%;
  border: 1px solid #d6dbed;
  margin: 2% 1% 0;
  background: #ffffff;
  box-shadow: 0 0 8px 0 rgba(72, 119, 236, 0.1);
  border-radius: 4px;
}
.select-content:hover span {
  color: #4877ec;
}
.select-content:hover {
  cursor: pointer;
  background: #f8faff;
  border: 1px solid #3e75f4;
}
.select-text {
  font-size: 15px;
  color: #2e2f36;
  text-align: center;
  font-weight: 400;
}
.select-text-X {
  font-size: 15px;
  color: #4877ec;
  letter-spacing: 0;
  font-weight: 400;
  margin-left: 12px;
  cursor: pointer;
}
</style>

效果圖:
在這裡插入圖片描述

三、Vue 拖拽組件 vuedraggable

vuedraggable 是標準的組件式封裝,並且將可拖動元素放進了 transition-group 上面,過渡動畫都比較好。

使用方式:

yarn add vuedraggable

import vuedraggable from 'vuedraggable';

在使用的時候,可以通過 v-model 來雙向綁定本地 data,如果需要更新或者是觸發父組件監聽的事件,可以在 updated() 中去 emit。

案例:

<template>
  <div>
    <div>{{ drag ? "拖拽中" : "拖拽停止" }}</div>
    <!--使用draggable組件-->
    <draggable
      v-model="myArray"
      chosenClass="chosen"
      forceFallback="true"
      group="people"
      animation="1000"
      @start="onStart"
      @end="onEnd"
    >
      <transition-group>
        <div class="item" v-for="element in myArray" :key="element.id">
          {{ element.name }}
        </div>
      </transition-group>
    </draggable>
    <div class="color-list">
      <div
        class="color-item"
        v-for="color in colors"
        v-dragging="{ item: color, list: colors, group: 'color' }"
        :key="color.text"
      >
        {{ color.text }}
      </div>
    </div>
  </div>
</template>
  <style scoped>
/*被拖拽對象的樣式*/
.item {
  padding: 6px;
  background-color: #fdfdfd;
  border: solid 1px #eee;
  margin-bottom: 10px;
  cursor: move;
}
/*選中樣式*/
.chosen {
  border: solid 1px #3089dc !important;
}
</style>
  <script>
//導入draggable組件
import draggable from "vuedraggable";
export default {
  //註冊draggable組件
  components: {
    draggable,
  },
  data() {
    return {
      drag: false,
      //定義要被拖拽對象的數組
      myArray: [
        { people: "cn", id: 10, name: "www.itxst.com" },
        { people: "cn", id: 20, name: "www.baidu.com" },
        { people: "cn", id: 30, name: "www.taobao.com" },
        { people: "us", id: 40, name: "www.yahoo.com" },
      ],
      colors: [
        {
          text: "Aquamarine",
        },
        {
          text: "Hotpink",
        },
        {
          text: "Gold",
        },
        {
          text: "Crimson",
        },
        {
          text: "Blueviolet",
        },
        {
          text: "Lightblue",
        },
        {
          text: "Cornflowerblue",
        },
        {
          text: "Skyblue",
        },
        {
          text: "Burlywood",
        },
      ],
    };
  },
  methods: {
    //開始拖拽事件
    onStart() {
      this.drag = true;
    },
    //拖拽結束事件
    onEnd() {
      this.drag = false;
    },
  },
};
</script>

四、Awe-dnd指令封裝

vue-dragging 的 npm 包的名字是 awe-dnd ,並不是 vue-dragging,這個庫的特點是封裝了 v-dragging 全局指令,然後通過全局指令去數據綁定等。

相比及 vuedraggable 來說, awe-dnd 是沒有雙向綁定(這裡沒有雙向綁定並不是很嚴謹,準確的來說沒有暴露雙向綁定的方式),因此提供了事件,在拖拽結束的時候用來更新列表(不需要手動更新列表,其實內部是實現了雙向綁定的)或者是去觸發父組件監聽的事件。

安裝依賴:

npm install awe-dnd --save
yarn add awe-and

main.js

import VueDND from 'awe-dnd'

Vue.use(VueDND)

案例:

<template>
  <div>
    <div class="color-list">
      <div
        class="color-item"
        v-for="color in colors"
        v-dragging="{ item: color, list: colors, group: 'color' }"
        :key="color.text"
      >
        {{ color.text }}
      </div>
    </div>
  </div>
</template>
<style scoped>
/*被拖拽對象的樣式*/
.item {
  padding: 6px;
  background-color: #fdfdfd;
  border: solid 1px #eee;
  margin-bottom: 10px;
  cursor: move;
}
/*選中樣式*/
.chosen {
  border: solid 1px #3089dc !important;
}
</style>
<script>
export default {
  data() {
    return {
      drag: false,
      colors: [
        {
          text: "Aquamarine",
        },
        {
          text: "Hotpink",
        },
        {
          text: "Gold",
        },
        {
          text: "Crimson",
        },
        {
          text: "Blueviolet",
        },
        {
          text: "Lightblue",
        },
        {
          text: "Cornflowerblue",
        },
        {
          text: "Skyblue",
        },
        {
          text: "Burlywood",
        },
      ],
    };
  },
  methods: {},
};
</script>

若對您有幫助,請點擊跳轉到B站一鍵三連哦!感謝支持!!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作為國民經濟的命脈和樞紐,金融行業對底層資料庫的能力要求正在不斷提高。在眾多要求中,數據一致性無疑是重中之重,即數據不能出錯,最好還能提高併發效率。 TDSQL採用MC(輕量級GTM)+全局MVCC的全局讀一致性方案。如果只使用全局事務管理器GTM,除需維護全局序列外,還需要維護全局的事務衝突,這個 ...
  • 分析:給 reader 表添加數據. INSERT INTO:插入數據,插入數據的時候會檢查主鍵或者唯一索引,如果出現重覆就會報錯; 語法:INSERT INTO table_name VALUES (value1,value2,value3,...); --這種形式無需指定要插入數據的列名,只需提 ...
  • 整理下近期被 Apple 殘忍虐待的成果。 ps: 可以提供一個視頻鏈接,建議用微軟的OneDrive 。審核員方便點。國內那些個地址都需要登錄,需要登錄才能看視頻的場景,同樣會被拒 Guideline 1.1 - Safety - Objectionable Content Guideline 1 ...
  • 眾所周知,在開發蘋果應用時需要使用簽名(證書)才能進行打包安裝蘋果IPA,作為剛接觸ios開發的同學,只是學習ios app開發內測,並沒有上架appstore需求,對於蘋果開發者賬號認證需要支付688,真的是極大的浪費,使用appuploader,只需要註冊蘋果普通的賬號,不需要688認證,就可以 ...
  • AR技術的落地應用,推動著電商領域的不斷升級,通過增強現實為用戶帶來了虛擬與現實結合的AR購物體驗。如AR試衣、AR試鞋、AR試妝等功能的出現讓用戶在手機上就能體驗產品的佩戴效果,可以讓用戶更直觀、更真實的瞭解產品信息,提升消費者的購物愉悅感,幫助電商應用提高購物轉化率。華為AR Engine也為A ...
  • 書寫語法 輸出語句 變數 數據類型 運算符 == 與 區別: ==: 1、判斷類型是否一樣,如果不一樣,則進行類型轉換 2、再去比較其值 : 1、判斷類型是否一樣,如果不一樣,直接返回false 2、再去比較其值 類型轉換: * 其他類型轉為number:(一般使用parseInt) 1、strin ...
  • #背景 不知道webpack插件是怎麼回事,除了官方的文檔外,還有一個很直觀的方式,就是看源碼。 看源碼是一個挖寶的行動,也是一次冒險,我們可以找一些代碼量不是很大的源碼 比如webpack插件,我們就可以通過BannerPlugin源碼,來看下官方是如何實現一個插件的 希望對各位同學有所幫助,必要 ...
  • 我的Vue之旅。使用 Vue 3.1 + TypeScript + Router + Tailwind.css 構建手機底部導航欄、仿B站的登錄、註冊頁面。 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...