To-Do Calendar - Day10 用 Vue Draggable 實作拖曳效果

花了幾天終於把所有內頁、Modal 和前端效果都做完了~這次要介紹的是用 Vue Draggable 來完成拖曳排序功能。

載入 Vue Draggable

Vue.Draggable 是一款基於 Sortable.js 實現的 vue 拖曳套件,支援移動裝置拖拽(支持觸摸設備),可以在不同列表間拖拽,支援 vue 2 過渡動畫相容,官網有很詳細的文檔以及充足的代碼例子,上手容易,是一款非常優秀的 vue 拖拽元件。

  • 安裝 Vue Draggable

    Warning
    此專案用的是 Vue 2,所以安裝的是對應的 Vue.Draggable 版本。

    npm install vuedraggable@2.20.0 --save
    
  • 在需要使用拖拽的組件載入並註冊後,就可以使用了

    <script>
      import draggable from "vuedraggable";
    
      export default {
        components: {
          draggable
        },
        ...
    
  • 在要使用拖曳功能的列表外層用 <draggable> 包起來,並綁定 v-model

    <template>
      <main>
        <div>
          <draggable v-model="list">
            <div v-for="item in list" :key="item.id">
              <h1 class="text-center">{{ item.name }}</h1>
            </div>
          </draggable>
        </div>
      </main>
    </template>
    
      <script>
    import draggable from "vuedraggable";
    export default {
      components: {
        draggable,
      },
      data() {
        return {
          list: [
            { name: "John", id: 0 },
            { name: "Joao", id: 1 },
            { name: "Jean", id: 2 },
          ],
        };
      },
    };
    </script>
    
  • 運行開發環境,測試 draggable 是否運作

    image

    最基本的拖曳效果出來了!

draggable 屬性

可以使用 v-bind 屬性作為配置項或直接寫在 <draggable>,以下列出專案中較常使用到的屬性,其他屬性可參考 vue.draggable 中文文檔

  • group:用於分組,同一組的不同 list 可以相互拖曳
  • delay:鼠標選中列表單元可以開始拖動的延遲時間
  • disabled:是否啟用拖曳功能
  • animation:拖曳動畫時間長度(毫秒),搭配 <transition-group> 使用
  • chosenClass:設定被選中目標的樣式

draggable 事件

  • start(evt):開始拖拽時候觸發
  • add(evt):拖拽新增的時候觸發
  • remove(evt):從列表拖走,移除觸發
  • update(evt):列表更新觸發
  • end(evt):拖拽完成時觸發
  • choose(evt):選擇拖拽元素觸發
  • sort(evt):排序觸發
  • change(evt):如果數據不是整個提交,單個提交數據的時候就會用到它


實作原子習慣分頁拖曳功能

image

  • habitTracker.vue(單個列表)
    ...
    <draggable
      v-model="habitList"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
       type="transition"
       name="!drag ? 'flip-list' : null"
      >
        <li
          v-for="item in habitList"
          ...
        >
        {{ item.name }}
        ...
        </li>
      </transition-group>
    </draggable>
    
    import draggable from "vuedraggable";
    export default {
    components: {
      draggable
    },
    data() {
      return {
        habitList: [
          { id: "1", name: "吃早餐", checkColor: "red" },
          { id: "2", name: "喝水2000cc", checkColor: "yellow" },
          { id: "3", name: "寫技術部落格", checkColor: "green" },
          { id: "4", name: "伸展10分鐘", checkColor: "teal" },
          { id: "5", name: "睡前保養", checkColor: "blue" },
          { id: "6", name: "12點前就寢", checkColor: "pink" },
          { id: "7", name: "戴維持器", checkColor: "pink" },
        ],
      };
    },
    computed: {
      dragOptions() {
        return {
          animation: 300,
          disabled: false,
          chosenClass:"chosen",
        };
      },
    },
    


實作代辦事項分頁拖曳功能

image

  • 設定 group 讓不同列表的單元可以互相拖曳
  • todoLists.vue(2個列表)
    ...
    <draggable
      v-model="todoList"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
        type="transition"
        name="!drag ? 'flip-list' : null"
      >
        <div
          v-for="item in todoList"
          ...
          {{ item.name }}
          ...
      </div>
      </transition-group>
    </draggable>
    
    ...
    <draggable
      v-model="doneList"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
        type="transition"
        name="!drag ? 'flip-list' : null"
      >
        <div
          v-for="item in doneList"
          ...
          {{ item.name }}
          ...
      </div>
      </transition-group>
    </draggable>
    
    import draggable from "vuedraggable";
    export default {
    components: {
      draggable
    },
    data() {
      return {
        todoList: [
          { id: "TA01", name: "代辦事項1", labelType: "firstColor" },
          { id: "TA02", name: "代辦事項2", labelType: "secondColor" },
          { id: "TA03", name: "代辦事項3", labelType: "thirdColor" },
          { id: "TA04", name: "代辦事項4", labelType: "fourthColor" },
        ],
        doneList: [
          { id: "TA01", name: "完成事項1", labelType: "firstColor" },
          { id: "TA02", name: "完成事項2", labelType: "secondColor" },
          { id: "TA03", name: "完成事項3", labelType: "thirdColor" },
          { id: "TA04", name: "完成事項4", labelType: "fourthColor" },
        ],
      };
    },
    computed: {
      dragOptions() {
        return {
          animation: 300,
          group: "task",
          disabled: false,
          chosenClass: "chosen",
        };
      },
    },
    


實作便條貼牆分頁拖曳功能

image

  • 設定 group 讓不同列表的單元可以互相拖曳
  • notes.vue(4個列表)
    ...
    <draggable
      v-model="list1"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
        type="transition"
        name="!drag ? 'flip-list' : null"
      >
        <div
          v-for="item in list1"
          ...
          {{ item.name }}
          ...
      </div>
      </transition-group>
    </draggable>
    
    ...
    <draggable
      v-model="list2"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
        type="transition"
        name="!drag ? 'flip-list' : null"
      >
        <div
          v-for="item in list2"
          ...
          {{ item.name }}
          ...
      </div>
      </transition-group>
    </draggable>
    
    ...
    <draggable
      v-model="list3"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
        type="transition"
        name="!drag ? 'flip-list' : null"
      >
        <div
          v-for="item in list3"
          ...
          {{ item.name }}
          ...
      </div>
      </transition-group>
    </draggable>
    
    ...
    <draggable
      v-model="list4"
      v-bind="dragOptions"
      @start="drag = true"
      @end="drag = false"
      :move="getdata"
      @update="datadragEnd"
      class="list-group"
    >
      <transition-group
        type="transition"
        name="!drag ? 'flip-list' : null"
      >
        <div
          v-for="item in list4"
          ...
          {{ item.name }}
          ...
      </div>
      </transition-group>
    </draggable>
    
    import draggable from "vuedraggable";
    export default {
    components: {
      draggable
    },
    data() {
      return {
        list1: [
          {
            id: "N01",
            name: "title1",
            content: "aaa",
            bgColor: "#f7e3cb",
            tapeStyle: "06",
          },
          {
            id: "N05",
            name: "title5",
            content: "eee",
            bgColor: "#d7ebf4",
            tapeStyle: "04",
          },
        ],
        list2: [
          {
            id: "N02",
            name: "title2",
            content: "bbb",
            bgColor: "#ddeee8",
            tapeStyle: "02",
          },
          {
            id: "N07",
            name: "title7",
            content: "fff",
            bgColor: "#e7d4e8",
            tapeStyle: "05",
          },
        ],
        list3: [
          {
            id: "N03",
            name: "title3",
            content: "ccc",
            bgColor: "#f6d8e4",
            tapeStyle: "03",
          },
          {
            id: "N08",
            name: "title8",
            content: "fff",
            bgColor: "#d7ebf4",
            tapeStyle: "01",
          },
        ],
        list4: [
          {
            id: "N04",
            name: "title4",
            content: "ddd",
            bgColor: "#fff8b7",
            tapeStyle: "04",
          },
          {
            id: "N06",
            name: "title6",
            content: "ddd",
            bgColor: "#ddeee8",
            tapeStyle: "02",
          },
        ],
      };
    },
    computed: {
      dragOptions() {
        return {
          animation: 300,
          group: "note",
          disabled: false,
          chosenClass: "chosen",
        };
      },
    },
    


延伸閱讀

參考資料

comments

comments powered by Disqus