Сделайте эффект перетаскивания домашней страницы Zen Tao на основе vue

Vue.js
Сделайте эффект перетаскивания домашней страницы Zen Tao на основе vue

Посмотрите на эффект без бб

预览图

Адрес источника

два предложения

Недавно я работаю над проектом управления фоном на основе Vue. Обычно статистика прогресса проекта ведется на Zen Road. Итак~ ​​Затем лидер решил, что с эффектом перетаскивания все в порядке, можно ли добавить его в наш проект? Раз ведущий говорит, давайте сделаем это. .

Все технологии: vue + vuedraggable

Реализация перетаскивания основана наvuedraggableразработка плагина.

Домашняя страница представляет собой макет с двумя столбцами, и каждый компонент можно перетаскивать вверх и вниз или влево и вправо.

页面布局

Основные шаги

макет

Этот макет является наиболее распространенным макетом с двумя столбцами, и здесь используется гибкий макет. Слева адаптивная, справа фиксированная ширина.

.layout-container {
  display: flex;
  .left {
    flex: 1;
    margin-right: 40px;
  }
  .right {
    width: 550px;
  }
}  

Перетащите реализацию

используется здесьvuedraggableплагин. Его нужно ввести и использовать в компоненте.draggableЭквивалентно перетаскиванию контейнера, для этого, очевидно, требуются два перетаскиваемых контейнера. Так соответственно.left .rightДобавьте два контейнера перетаскивания в . По умолчанию здесь уже доступно перетаскивание. Эффект плагина по-прежнему очень мощный.

<div class="layout-container">
     <!--左栏-->
    <div class="left">
        <draggable
          v-bind="dragOptions"
          class="list-group"
          :list="item"
        >
         // ... 拖拽元素或组件
        </draggable>
      </div>
      <!--右栏-->
    <div class="right">
        <draggable
          v-bind="dragOptions"
          class="list-group"
          :list="item"
        >
          // ... 拖拽元素或组件
        </draggable>
    </div>
</div>
<script>
import draggable from "vuedraggable";
export default {
  components: {draggable},
  computed: {
    dragOptions() {
      return {
        animation: 30,
        handle: ".drag-handle",
        group: "description",
        ghostClass: "ghost",
        chosenClass: "sortable",
        forceFallback: true
      };
    }
  }
};
</script>

Тем не менее, это все еще немного отличается от того, что я хочу.

Перетаскивание влево и вправо против перетаскивания только строки заголовка

В этом блоке нужно только настроить соответствующие элементы конфигурации, что может быть относительно просто. Для перетаскивания влево и вправо необходимо указать одно и то же значение для перетаскиваемого контейнера.groupАтрибуты. Указание того, что перетаскивание титровального элемента требует настройкиhandleИмя селектора для перетаскиваемого элемента.

Ниже приводится краткое введение в часто используемые элементы конфигурации:

  • disabled:boolean определяет, доступен ли сортируемый объект, когда он равен true, сортируемый объект не может перетаскивать сортировку и другие функции, когда он равен false, его можно сортировать, что эквивалентно переключателю;
  • group: используется для установки контейнера, который можно перетаскивать.Если элемент конфигурации двух контейнеров одинаков, их можно перетаскивать друг к другу;
  • animation:number Единица: мс, определяет время анимации сортировки;
  • handle: selector Формат представляет собой строку простых селекторов css, так что элементы в блоке списка, соответствующие селектору, становятся дескриптором перетаскивания, а блок списка можно перетаскивать, только удерживая дескриптор перетаскивания;
  • filter: selector Формат представляет собой строку простых селекторов css, которая определяет, какие единицы списка нельзя перетаскивать, и можно установить несколько селекторов, разделенных символом "," посередине;
  • draggable:selector Строка в формате простого селектора css, определяющая, какие ячейки списка можно перетаскивать.
  • ghostClass:selector Формат представляет собой строку простых селекторов css.При перетаскивании элемента списка копия будет сгенерирована как теневой элемент для имитации сортировки перетаскиваемого элемента.Этот элемент конфигурации предназначен для добавления класса к этому теневому элементу.Мы таким образом можно редактировать стили для теневых элементов;
  • chosenClass: selector Формат представляет собой строку простых селекторов css.При выборе единицы списка к единице будет добавлен класс;
  • forceFallback: boolean Если установлено значение true, родное перетаскивание html5 не будет использоваться, а стили некоторых элементов при перетаскивании могут быть изменены;
  • fallbackClass:string Когда для forceFallback установлено значение true, стиль ячейки, присоединяемой мышью во время перетаскивания;

Соответствующая конфигурация выглядит следующим образом:

 computed: {
    dragOptions() {
      return {
        animation: 30,
        handle: ".drag-handle",
        group: "description",
        ghostClass: "ghost",
        chosenClass: "sortable",
        forceFallback: true
      };
    }
  }

Настройка стиля при перетаскивании

Во время перетаскивания нам нужно сделать три вещи. При перетаскивании перетаскиваемый элемент показывает только строку заголовка, а список из двух столбцов показывает только элемент заголовка, а перемещаемая позиция выделена серым цветом.

  1. При перетаскивании элемента отображается только строка заголовка: По умолчанию он включенhtml5Эффект перетаскивания элемента. Тут явно не нужен.forceFallbackизменить наfalseты можешь закрытьhtml5эффект по умолчанию. пройти мимоchosenClass: "sortable"Измените имя класса перетаскиваемого элемента. Скрыть напрямую с помощью css

    .sortable {
      .component-box {
        display: none;
        height: 0;
      }
    }
    
  2. В двухколоночном списке отображается только элемент title Здесь я использую два события для достижения.

    • onStart: функция обратного вызова для начала перетаскивания ячейки списка
    • onEnd: функция Функция обратного вызова после завершения перетаскивания элемента списка.
    <div class="layout-container" :class="{drag:dragging}">
        //...
    </div>
    
    data() {
        return {
          dragging: false
        };
    },
    methods: {
        onStart() {
          this.dragging = true;
        },
        onEnd() {
          this.dragging = false;
        }
     }
    
    .drag {
      .component-box {
        display: none;
      }
    }
    

    Дайте, когда вы начнете перетаскивать.layout-containerДобавить к.dragимя класса. Когда перетаскивание закончится, удалите имя класса.

  3. Позиция, которую нужно переместить, выделена серым цветом
    Нужно использовать вышеghostClass: "ghost"элемент конфигурации. и добавьте соответствующий css.

    .ghost {
      .drag-handle {
        background: rgb(129, 168, 187);
      }
    }
    

    Что ж, в принципе, это было достигнуто. . .

Отображение динамических компонентов

Далее идет динамическое отображение данных. Здесь нам нужны динамические компоненты в vue. . Прикрепите ссылку на официальную документациюНажмите, чтобы просмотреть.

Затем содержимое каждого перетаскиваемого элемента записывается как компонент, который можно свободно перетаскивать с помощью динамических компонентов.

// 将所用组件引入
import {
  timeline,
  calendar,
  welcome,
  carousel,
  imgs,
  KonList
} from "@/components/DragComponents";

components: {
    draggable,
    timeline,
    calendar,
    welcome,
    carousel,
    imgs,
    KonList
}

Сотрудничатьv-forЗацикливайте данные и отображайте их динамически.

<component :is="element.name"/>

Этот блок связан с форматом данных, непосредственно код вы можете посмотреть в конце статьи. . . Здесь это обсуждаться не будет. .

хранение данных

После того, как перетаскивание закончилось, нам нужно кэшировать последовательность перетаскивания во внешнем интерфейсе, и при следующей записи мы можем продолжать использовать перетаскиваемые данные.

// 获取新的布局
 getLayout() {
      let myLayout = JSON.parse(window.localStorage.getItem("kon"));
      if (!myLayout || Object.keys(myLayout).length === 0)
        myLayout = this.layout;
      const newLayout = {};
      for (const side in myLayout) {
        newLayout[side] = myLayout[side].map(i => {
          return this.componentList.find(c => c.id === i);
        });
      }
      this.mainData = newLayout;
},
// 设置新的布局
setLayout() {
    const res = {};
    for (const side in this.mainData) {
        const item = this.mainData[side].map(i => i.id);
        res[side]=item;
    }
    window.localStorage.setItem("kon", JSON.stringify(res));
}

Так что мне просто нужноmountedчтобы получить новый макет. .

 mounted() {
    this.getLayout();
 }

После завершения перетаскивания установите новый макет

onEnd() {
    this.dragging = false;
    this.setLayout();
}

В проекте рекомендуется сотрудничать с бэккой для хранения данных пользовательского макета. После каждого перетаскивания новый интерфейс запроса на обработку компоновки сохраняется в базе данных и сохраняется в кэше одновременно. При входе на страницу снова прочитайте данные в кэше, если нет, запросите внутренний интерфейс, чтобы получить макет пользователя, а затем снова сохраните его в кеше. Если есть, прочитайте данные в кэше напрямую.

Два последних слова

На самом деле вышеописанный эффект не представляет особой сложности, просто уделите немного времени, просмотрите соответствующие документы, вы можете его сделать, и запишите на Наггетс, просто хочу поделиться с вами своими идеями. В то же время, я надеюсь общаться с вами и добиться прогресса вместе.

Жизнь не легка, давай все

Прикрепите исходный код:адрес проекта

<template>
  <div :class="{drag:dragging}">
    <div class="layout-container">
      <div :class="key" v-for="(item, key) in mainData" :key="key">
        <draggable
          v-bind="dragOptions"
          class="list-group"
          :list="item"
          @end="onEnd"
          @start="onStart"
        >
          <transition-group name="list">
            <div class="list-group-item" v-for="(element, index) in item" :key="index">
              <div class="drag-handle">{{ element.title }}</div>
              <div class="component-box">
                <component :is="element.name"/>
              </div>
            </div>
          </transition-group>
        </draggable>
      </div>
    </div>
  </div>
</template>
<script>
import draggable from "vuedraggable";
import {
  timeline,
  calendar,
  welcome,
  carousel,
  imgs,
  KonList
} from "@/components/DragComponents";

export default {
  components: {
    draggable,
    timeline,
    calendar,
    welcome,
    carousel,
    imgs,
    KonList
  },
  data() {
    return {
      dragging: false,
      componentList: [
        { name: "KonList", title: "追番地址", id: "5" },
        { name: "imgs", title: "五月最强新番", id: "4" },
        { name: "timeline", title: "日程组件", id: "2" },
        { name: "carousel", title: "走马灯组件", id: "1" },
        { name: "calendar", title: "日历组件", id: "3" }
      ],
      layout: {
        left: ["5", "4"],
        right: ["2", "1", "3"]
      },
      mainData: {}
    };
  },
  computed: {
    dragOptions() {
      return {
        animation: 30,
        handle: ".drag-handle",
        group: "description",
        ghostClass: "ghost",
        chosenClass: "sortable",
        forceFallback: true
      };
    }
  },
  mounted() {
    this.getLayout();
  },
  methods: {
    onStart() {
      this.dragging = true;
    },
    onEnd() {
      this.dragging = false;
      this.setLayout();
    },
    getLayout() {
      let myLayout = JSON.parse(window.localStorage.getItem("kon"));
      if (!myLayout || Object.keys(myLayout).length === 0)
        myLayout = this.layout;
      const newLayout = {};
      for (const side in myLayout) {
        newLayout[side] = myLayout[side].map(i => {
          return this.componentList.find(c => c.id === i);
        });
      }
      this.mainData = newLayout;
    },
    setLayout() {
      const res = {};
      for (const side in this.mainData) {
        const item = this.mainData[side].map(i => i.id);
        res[side]=item;
      }
      window.localStorage.setItem("kon", JSON.stringify(res));
    }
  }
};
</script>
<style lang="scss" scoped>
.layout-container {
  height: 100%;
  display: flex;
  .left {
    flex: 1;
    margin-right: 40px;
  }
  .right {
    width: 550px;
  }
  .list-group-item {
    margin-bottom: 20px;
    border-radius: 6px;
    overflow: hidden;
    background: #fff;
  }
  .component-box {
    padding: 20px;
  }
  .drag-handle {
    cursor: move;
    height: 40px;
    line-height: 40px;
    color: #fff;
    font-weight: 700;
    font-size: 16px;
    padding: 0 20px;
    background: #6cf;
  }
}
.drag {
  .component-box {
    display: none;
  }
}

.list-enter-active {
  transition: all .3s linear;

}
.list-enter,
.list-leave-to {
  opacity: .5;
}

.sortable {
  .component-box {
    display: none;
    height: 0;
  }
}
.list-group {
  > span {
    display: block;
    min-height: 20px;
  }
}

.ghost {
  .drag-handle {
    background: rgb(129, 168, 187);
  }
}
</style>