Vue實現(xiàn)拖拽穿梭框功能四種方式

一晚碾、使用原生js實現(xiàn)拖拽

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

<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要寫在行內(nèi)樣式里面 -->
      <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 // 獲取鼠標在方塊內(nèi)的x軸距
          let innerY = event.clientY - offsetY // 獲取鼠標在方塊內(nèi)的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事件
          // 否則鼠標抬起后還可以繼續(xù)拖拽方塊
          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實現(xiàn)拖拽穿梭框

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

效果圖:

屏幕截圖 2022-09-28 201708.png

三茵臭、Vue 拖拽組件 vuedraggable

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

使用方式:

yarn add vuedraggable

import vuedraggable from 'vuedraggable';

在使用的時候奇徒,可以通過 v-model 來雙向綁定本地 data雏亚,如果需要更新或者是觸發(fā)父組件監(jiān)聽的事件,可以在 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,
      //定義要被拖拽對象的數(shù)組
      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;
    },
    //拖拽結(jié)束事件
    onEnd() {
      this.drag = false;
    },
  },
};
</script>

四罢低、Awe-dnd指令封裝

vue-dragging 的 npm 包的名字是 awe-dnd ,并不是 vue-dragging胖笛,這個庫的特點是封裝了 v-dragging 全局指令网持,然后通過全局指令去數(shù)據(jù)綁定等。

相比及 vuedraggable 來說长踊, awe-dnd 是沒有雙向綁定(這里沒有雙向綁定并不是很嚴謹功舀,準確的來說沒有暴露雙向綁定的方式),因此提供了事件身弊,在拖拽結(jié)束的時候用來更新列表(不需要手動更新列表辟汰,其實內(nèi)部是實現(xiàn)了雙向綁定的)或者是去觸發(fā)父組件監(jiān)聽的事件。

安裝依賴:

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>

若對您有幫助阱佛,請點擊跳轉(zhuǎn)到B站一鍵三連哦位喂!感謝支持K浞纭!!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掘鄙,一起剝皮案震驚了整個濱河市判没,隨后出現(xiàn)的幾起案子袱耽,更是在濱河造成了極大的恐慌本鸣,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壮莹,死亡現(xiàn)場離奇詭異翅帜,居然都是意外死亡,警方通過查閱死者的電腦和手機命满,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門涝滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胶台,你說我怎么就攤上這事歼疮。” “怎么了诈唬?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵韩脏,是天一觀的道長。 經(jīng)常有香客問我铸磅,道長赡矢,這世上最難降的妖魔是什么杭朱? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮吹散,結(jié)果婚禮上弧械,老公的妹妹穿的比我還像新娘。我一直安慰自己空民,他們只是感情好刃唐,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著界轩,像睡著了一般画饥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浊猾,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天抖甘,我揣著相機與錄音,去河邊找鬼与殃。 笑死单山,一個胖子當著我的面吹牛碍现,可吹牛的內(nèi)容都是我干的幅疼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼昼接,長吁一口氣:“原來是場噩夢啊……” “哼爽篷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慢睡,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤逐工,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漂辐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泪喊,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年髓涯,在試婚紗的時候發(fā)現(xiàn)自己被綠了袒啼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡纬纪,死狀恐怖蚓再,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情包各,我是刑警寧澤摘仅,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站问畅,受9級特大地震影響娃属,放射性物質(zhì)發(fā)生泄漏六荒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一膳犹、第九天 我趴在偏房一處隱蔽的房頂上張望恬吕。 院中可真熱鬧,春花似錦须床、人聲如沸铐料。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钠惩。三九已至,卻和暖如春族阅,著一層夾襖步出監(jiān)牢的瞬間篓跛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工坦刀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愧沟,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓鲤遥,卻偏偏與公主長得像沐寺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盖奈,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容