900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > html 编辑器 拖动 可视化拖拽页面编辑器 一

html 编辑器 拖动 可视化拖拽页面编辑器 一

时间:2023-08-26 14:53:07

相关推荐

html 编辑器 拖动 可视化拖拽页面编辑器 一

前端技术日益发展,组件化日益成熟,作为一个前端,每天的工作就是用组件堆砌页面,有没有一种方式可以像CocosCreator,通过组件+脚本绑定的方式来实现我们的页面和功能,今天我们就来实现一个提高生产力的工具 可视化拖拽页面编辑器, 让产品和UI通过拖拽编辑页面,生产自己想要的页面。

技术框架采用Vue3 + Typescript + ElementPlus

每个章节下边都会贴出对应commit代码,方便大家对比学习

最终效果

实现功能:

主页面结构:左侧可选组件列表、中间容器画布、右侧编辑组件定义好的属性

从菜单拖拽组件到容器;

单选、多选;

容器内的组件可以拖拽移动位置;

组件拖拽调整宽高;

组件拖拽贴边,显示辅助线;

操作栏按钮与命令

撤销、重做;

导入、导出;

置顶、置底;

删除、清空;

组件绑定值;

根据组件标识,通过作用域插槽自定义某个组件的行为

一、项目搭建与页面布局

通过vue-cli生成项目

vue create visual-editor-vue

复制代码

选择手动配置

选择配置如下:

选择vue3.x版本

这一步选y,使用jsx写组件,需要添加对应的babel插件

接下来我们来实现基本的左中右布局

左侧菜单栏放置组件列表

中间是画布和工具栏,用来编辑预览页面

右侧是我们选中某个组件后,显示的该组件的属性

第一部分代码:基本布局

二、数据结构设计与双向绑定实现

数据结构设计

定义数据结构如下

container 表示画布容器

blocks 表示放置在容器中的组件

每个block表示一个组件,包含了组件的类型位置、宽高、选中状态等信息

画布采用绝对定位,里面的元素通过top、left来确定位置

{

"container": {

"height": 500,

"width": 800

},

"blocks": [

{

"componentKey": "button",

"top": 102,

"left": 136,

"adjustPosition": false,

"focus": false,

"zIndex": 0,

"width": 0,

"height": 0

},

{

"componentKey": "input",

"top": 148,

"left": 358,

"adjustPosition": false,

"focus": false,

"zIndex": 0,

"width": 244,

"height": 0

}

]

}

复制代码

数据双向绑定实现

组件采用vue3中的jsx语法编写,需要实现数据双向绑定机制,useModel就是用来处理数据双向绑定的

import { computed, defineComponent, ref, watch } from "vue";

// 用jsx封装组件的时候,实现双向数据绑定

export function useModel(getter: () => T, emitter: (val: T) => void){

const state = ref(getter()) as { value: T };

watch(getter, (val) => {

if (val !== state.value) {

state.value = val;

}

});

return {

get value() {

return state.value;

},

set value(val: T) {

if (state.value !== val) {

state.value = val;

emitter(val);

}

},

};

}

复制代码

useModel用法

// modelValue 外部可以用v-model绑定

export const TestUseModel = defineComponent({

props: {

modelValue: { type: String },

},

emits: {

"update:modelValue": (val?: string) => true,

},

setup(props, ctx) {

const model = useModel(

() => props.modelValue,

(val) => ctx.emit("update:modelValue", val)

);

return () => (

自定义输入框

);

},

});

复制代码

三、Block渲染

新建visual-editor-block的组件

block来表示在画布显示的组件元素

block先用文本来显示

import { computed, defineComponent, PropType } from "vue";

import { VisualEditorBlockData } from "./visual-editor.utils";

export const VisualEditorBlock = defineComponent({

props: {

block: {

type: Object as PropType,

},

},

setup(props) {

const styles = computed(() => ({

top: `${props.block?.top}px`,

left: `${props.block?.left}px`,

}));

return () => (

这是一条block

);

},

});

复制代码

将定义的数据用v-model传入editor

App.vue文件

import { VisualEditor } from "../src/packages/visual-editor";

export default defineComponent({

name: "App",

components: { VisualEditor },

data() {

return {

editorData: {

container: {

height: 500,

width: 800,

},

blocks: [

{ top: 100, left: 100 },

{ top: 200, left: 200 },

],

},

};

},

});

复制代码

引入block组件,并进行渲染

visual-editor.tsx文件

import { computed, defineComponent, PropType } from "vue";

import { useModel } from "./utils/useModel";

import { VisualEditorBlock } from "./visual-editor-block";

import "./visual-editor.scss";

import { VisualEditorModelValue } from "./visual-editor.utils";

export const VisualEditor = defineComponent({

props: {

modelValue: {

type: Object as PropType,

},

},

emits: {

"update:modelValue": (val?: VisualEditorModelValue) => true,

},

setup(props, ctx) {

const dataModel = useModel(

() => props.modelValue,

(val) => ctx.emit("update:modelValue", val)

);

const containerStyles = computed(() => ({

width: `${props.modelValue?.container.width}px`,

height: `${props.modelValue?.container.height}px`,

}));

return () => (

menuheadoperator

{(dataModel.value?.blocks || []).map((block, index: number) => (

))}

);

},

});

复制代码

最终效果

画布会根据我们定义的editorData对象,来进行展示,container来描述画布的大小,block来描述在画布上的每个组件

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。