Посмотрите на эффект без бб
два предложения
Недавно я работаю над проектом управления фоном на основе 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
};
}
}
Настройка стиля при перетаскивании
Во время перетаскивания нам нужно сделать три вещи. При перетаскивании перетаскиваемый элемент показывает только строку заголовка, а список из двух столбцов показывает только элемент заголовка, а перемещаемая позиция выделена серым цветом.
-
При перетаскивании элемента отображается только строка заголовка: По умолчанию он включен
html5Эффект перетаскивания элемента. Тут явно не нужен.forceFallbackизменить наfalseты можешь закрытьhtml5эффект по умолчанию. пройти мимоchosenClass: "sortable"Измените имя класса перетаскиваемого элемента. Скрыть напрямую с помощью css.sortable { .component-box { display: none; height: 0; } } -
В двухколоночном списке отображается только элемент 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имя класса. Когда перетаскивание закончится, удалите имя класса. -
Позиция, которую нужно переместить, выделена серым цветом
Нужно использовать выше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>