Vue3實(shí)現(xiàn)組件拖拽實(shí)時(shí)預(yù)覽功能
1. 需求分析
實(shí)現(xiàn)一個(gè)CMS內(nèi)容管理系統(tǒng),在后臺(tái)進(jìn)行內(nèi)容編輯,在官網(wǎng)更新展示內(nèi)容。
關(guān)于后臺(tái)的編輯功能,大致分為兩部分:組件拖拽預(yù)覽
、組件內(nèi)容編輯實(shí)時(shí)預(yù)覽
。
對(duì)于組件拖拽預(yù)覽,用戶可以在含有各種功能組件的列表中,選擇需要的組件進(jìn)行拖拽。將組件拖拽到預(yù)覽畫布中后,可以在畫布中預(yù)覽組件的內(nèi)容。
對(duì)于組件內(nèi)容編輯實(shí)時(shí)預(yù)覽,用戶可以點(diǎn)擊編輯按鈕,顯示對(duì)應(yīng)組件的內(nèi)容編輯信息。當(dāng)修改組件內(nèi)容時(shí),在畫布中可以實(shí)時(shí)預(yù)覽組件的變化。
2. 實(shí)現(xiàn)步驟
2.1 關(guān)于拖拽組件庫的選擇
關(guān)于拖拽組件庫,在github
上有很多,最熱門的當(dāng)屬vuedraggable
這個(gè)庫。
它基于Sortable.js
,擁有完整的中文文檔,所以很多朋友在做拖拽功能時(shí),都會(huì)優(yōu)先考慮它。
但是,我在使用的過程中,在組件拖拽、取消組件拖拽這里,遇到了一些小問題。不知道是我操作的問題,還是庫本身存在BUG,所以最終沒有選用它。
于是我發(fā)現(xiàn)了,一個(gè)更加好用的拖拽庫vue-draggable-next
。
它的出現(xiàn)是參考了vuedraggable
這個(gè)庫,所以說很多用法很相似,但是使用起來較之更加友善??偨Y(jié)為一個(gè)詞,自由。
下面我們的拖拽功能實(shí)現(xiàn),就是利用了vue-draggable-next
這個(gè)庫。
如果你對(duì)拖拽底層原理感興趣,并且有空余時(shí)間,勞請(qǐng)閱至第三部分拖拽原理總結(jié)。
2.2 拖拽功能代碼實(shí)例
模版和邏輯中代碼都分為了三部分:組件列表
、預(yù)覽畫布
、內(nèi)容編輯
。
布局時(shí),樣式根據(jù)需求自定義,此處只是提供了功能的基本邏輯實(shí)現(xiàn)。
使用時(shí),關(guān)于拖拽組件的需求,根據(jù)文檔中提供的屬性和事件的描述,靈活配置完善。
vue-draggable-next
庫文檔地址:https://github.com/anish2690/vue-draggable-next
安裝依賴:
npm install vue-draggable-next //or yarn add vue-draggable-next
模板代碼:
<template> <div style="display: flex;cursor: pointer;"> <!-- 組件列表 左側(cè) --> <div style="width:30vw;padding:30px;height:300px;border: 1px solid #000;text-align: center"> <h1>組件列表</h1> <VueDraggableNext :list="componentNameList" :group="{ name: 'people', pull: 'clone', put: false }" :sort="false"> <div v-for="element in componentNameList" :key="element.name"> <div style="padding: 10px;background: #ccc;margin-bottom: 10px"> <el-button> {{ element.name }}</el-button> </div> </div> </VueDraggableNext> </div> <!-- 預(yù)覽畫布 中間 --> <div style="width:30vw;padding:30px;height:300px;border: 1px solid #000;text-align: center"> <h1>預(yù)覽畫布</h1> <VueDraggableNext :list="componentList" group="people" :sort="false"> <div v-for="(element, index) in componentList" :key="element.name"> <div> <el-button @click="changeComponent(element)" size="small">編輯</el-button> <el-button @click="deleteComponent(index)" size="small">刪除</el-button> </div> <component :is="element.component" :details="element.details"></component> </div> </VueDraggableNext> </div> <!-- 內(nèi)容編輯 右側(cè) --> <div style="width:30vw;padding:30px;height:300px;border: 1px solid #000;text-align: center"> <h1>內(nèi)容編輯</h1> <div v-for="(nowDetails, index) in nowComponentDetail.details" :key="index"> {{ nowDetails.key }}: <el-input clearable v-model="nowDetails.value" /> </div> </div> </div> </template>
邏輯代碼:
<script setup> import {markRaw, reactive, ref, watch, nextTick} from "vue"; // 引入需要拖拽的組件 import About from "@/views/AboutView.vue" // 引入拖拽庫 import { VueDraggableNext } from "vue-draggable-next"; // 組件列表數(shù)據(jù) const componentNameList = [ { // 組件ID componentId:0, // 組件名 name:'關(guān)于組件', // 組件描述 description: '關(guān)于組件', // 組件信息 component: markRaw(About), // 組件需要編輯內(nèi)容 details: About.props.details.default }, { componentId:1, name:'關(guān)于組件1', description: '關(guān)于組件1', component:markRaw(About), details: About.props.details.default }, ]; // 預(yù)覽畫布數(shù)據(jù) let componentList = reactive([]); watch(componentList, () => { console.log(componentList, 'componentList') }) // 畫布中刪除的點(diǎn)擊事件 const deleteComponent = (index) => { componentList.splice(index, 1); }; // 內(nèi)容編輯數(shù)據(jù) let nowComponentDetail = ref({}); // 畫布中編輯的點(diǎn)擊事件 const changeComponent = (element) => { nowComponentDetail.value = element; }; </script> <script setup> import {markRaw, reactive, ref, watch, nextTick} from "vue"; // 引入需要拖拽的組件 import About from "@/views/AboutView.vue" // 引入拖拽庫 import { VueDraggableNext } from "vue-draggable-next"; // 組件列表數(shù)據(jù) const componentNameList = [ { // 組件ID componentId:0, // 組件名 name:'關(guān)于組件', // 組件描述 description: '關(guān)于組件', // 組件信息 component: markRaw(About), // 組件需要編輯內(nèi)容 details: About.props.details.default }, { componentId:1, name:'關(guān)于組件1', description: '關(guān)于組件1', component:markRaw(About), details: About.props.details.default }, ]; // 預(yù)覽畫布數(shù)據(jù) let componentList = reactive([]); watch(componentList, () => { console.log(componentList, 'componentList') }) // 畫布中刪除的點(diǎn)擊事件 const deleteComponent = (index) => { componentList.splice(index, 1); }; // 內(nèi)容編輯數(shù)據(jù) let nowComponentDetail = ref({}); // 畫布中編輯的點(diǎn)擊事件 const changeComponent = (element) => { nowComponentDetail.value = element; }; </script>
2.3功能組件代碼實(shí)例(參考)
此處提供了組件列表中,任意功能組件的編寫實(shí)例,組件的具體功能根據(jù)需求自定義。特別注意,組件Props
中details
對(duì)象屬性的結(jié)構(gòu)寫法,要靈活應(yīng)用。模版代碼:
<template> <div style="padding: 10px;background: pink;margin-bottom: 10px"> {{ props.details.content }} </div> </template>
邏輯代碼:
<script setup> import { defineProps } from "vue"; const props = defineProps({ details: { type: Object, default: { content: { key: '內(nèi)容', value: "我是About", }, } } }) </script>
3. 問題詳解
3.1拖拽實(shí)現(xiàn)的底層原理
拖拽的實(shí)現(xiàn)原理可以簡單描述為以下幾個(gè)步驟:
觸發(fā)拖拽
:通過監(jiān)聽鼠標(biāo)或觸摸事件,當(dāng)用戶按下鼠標(biāo)左鍵或觸摸屏幕時(shí),表示開始拖拽操作。記錄拖拽信息
:在拖拽開始時(shí),記錄拖拽起始位置(鼠標(biāo)或觸摸點(diǎn)的坐標(biāo)),以及需要拖拽的元素的初始位置(相對(duì)于文檔的坐標(biāo))。更新拖拽元素位置
:在拖拽過程中,通過監(jiān)聽鼠標(biāo)移動(dòng)或觸摸滑動(dòng)事件,計(jì)算鼠標(biāo)或觸摸點(diǎn)的當(dāng)前位置,并根據(jù)鼠標(biāo)或觸摸點(diǎn)的位置變化,更新拖拽元素的位置。這可以通過修改元素的 CSS 屬性(如left和top)或使用 CSS 變換(如translateX和translateY)來實(shí)現(xiàn)。處理拖拽結(jié)束
:當(dāng)用戶釋放鼠標(biāo)左鍵或觸摸結(jié)束時(shí),表示拖拽結(jié)束。此時(shí)可以執(zhí)行一些操作,如更新拖拽元素的最終位置、觸發(fā)事件或回調(diào)函數(shù)等。
以下是拖拽實(shí)現(xiàn)的基本原理代碼實(shí)例:
// 獲取拖拽元素 const draggableElement = document.getElementById('draggable'); // 記錄拖拽起始位置和拖拽元素的初始位置 let startX, startY, initialX, initialY; // 監(jiān)聽鼠標(biāo)按下事件 draggableElement.addEventListener('mousedown', dragStart); draggableElement.addEventListener('touchstart', dragStart); // 監(jiān)聽鼠標(biāo)移動(dòng)事件 document.addEventListener('mousemove', drag); document.addEventListener('touchmove', drag); // 監(jiān)聽鼠標(biāo)釋放事件 document.addEventListener('mouseup', dragEnd); document.addEventListener('touchend', dragEnd); // 拖拽開始事件處理程序 function dragStart(event) { event.preventDefault(); if (event.type === 'touchstart') { startX = event.touches[0].clientX; startY = event.touches[0].clientY; } else { startX = event.clientX; startY = event.clientY; } initialX = draggableElement.offsetLeft; initialY = draggableElement.offsetTop; } // 拖拽事件處理程序 function drag(event) { event.preventDefault(); if (event.type === 'touchmove') { const currentX = event.touches[0].clientX - startX; const currentY = event.touches[0].clientY - startY; draggableElement.style.left = initialX + currentX + 'px'; draggableElement.style.top = initialY + currentY + 'px'; } else { const currentX = event.clientX - startX; const currentY = event.clientY - startY; draggableElement.style.left = initialX + currentX + 'px'; draggableElement.style.top = initialY + currentY + 'px'; } } // 拖拽結(jié)束事件處理程序 function dragEnd() { // 執(zhí)行拖拽結(jié)束后的操作 }
3.2關(guān)于vue-draggable-next庫的功能總結(jié)
vue-draggable-next
庫特點(diǎn)和功能的補(bǔ)充說明:
基于 Vue 3
:vue-draggable-next是為 Vue.js 3 版本設(shè)計(jì)的拖拽庫,充分利用了 Vue 3 的響應(yīng)式系統(tǒng)和組合式 API。輕量級(jí)和簡單易用
:該庫的代碼量較少,易于理解和使用。它提供了一組簡單的指令和組件,可以輕松地將拖拽功能集成到 Vue.js 應(yīng)用程序中。支持多種拖拽模式
:vue-draggable-next支持多種拖拽模式,包括自由拖拽、列表拖拽、網(wǎng)格拖拽等。您可以根據(jù)需求選擇適合的拖拽模式。自定義拖拽樣式和行為
:該庫允許您自定義拖拽元素的樣式和行為。您可以定義拖拽時(shí)的樣式、占位符元素、限制拖拽的范圍等。事件和回調(diào)支持
:vue-draggable-next提供了一組事件和回調(diào)函數(shù),可以在拖拽過程中監(jiān)聽和處理各種事件。例如,您可以監(jiān)聽拖拽開始、拖拽結(jié)束、拖拽元素排序等事件,并執(zhí)行相應(yīng)的操作。支持觸摸設(shè)備
:該庫對(duì)觸摸設(shè)備提供了良好的支持,可以在移動(dòng)設(shè)備上實(shí)現(xiàn)流暢的拖拽交互。
到此這篇關(guān)于Vue3實(shí)現(xiàn)組件拖拽實(shí)時(shí)預(yù)覽功能的文章就介紹到這了,更多相關(guān)Vue3組件拖拽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Vue3中shallowRef和shallowReactive的使用
這篇文章主要為大家介紹了Vue3中shallowRef和shallowReactive函數(shù)的使用方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Vue有一定的幫助,需要的可以參考一下2022-07-07vue+canvas實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)從上到下刷新瀑布圖效果(類似QT的)
這篇文章主要介紹了vue+canvas實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)從上到下刷新瀑布圖效果(類似QT的),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04vue?圖片路徑?“@/assets“?報(bào)錯(cuò)問題及解決
這篇文章主要介紹了vue?圖片路徑?“@/assets“?報(bào)錯(cuò)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09element的el-form-item的prop作用小結(jié)
Vue表單驗(yàn)證中的prop屬性用于指定需要驗(yàn)證的表單字段,它定義了字段名稱并與驗(yàn)證規(guī)則關(guān)聯(lián),確保數(shù)據(jù)的完整性和準(zhǔn)確性,本文就來介紹一下element的el-form-item的prop作用,感興趣的可以了解一下2025-01-01html-webpack-plugin修改頁面的title的方法
這篇文章主要介紹了html-webpack-plugin修改頁面的title的方法 ,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06