所见即所得, 通过拖拽实现一个自定义布局

前言

该项目基于react-grid-layoutantd的栅格布局实现。可能不适用所有项目, 但是思路也可以参考。

demo体验地址: 自定义布局

源码地址: 源码

实现功能

通过可视化拖拽区块, 组合区块生成自定义布局代码, 可直接引入有antd的项目中使用。

上面可视化操作后生成的布局代码:

<Row>
    <Col span="{10}" className="drag-item">
        <Col className="drag-subitem" style="{{" height:="" '10vh'="" }}="" span="{24}"/>
        <Col className="drag-subitem" style="{{" height:="" '95vh'="" }}="" span="{24}"/>

    </Col>
    <Col span="{14}" className="drag-item">
        <Col className="drag-subitem" style="{{" height:="" '25vh'="" }}="" span="{24}"/>
        <Col className="drag-subitem" style="{{" height:="" '80vh'="" }}="" span="{24}"/>

    </Col>
</Row>

代码生成的页面:

实现思路

要想生成任意布局, 栅格布局是最合适的。

这里不考虑特殊情况下的不规则布局。

我们先将布局按列划分, 每一列里面再分成几块, 这样基本就能满足我们大部分的业务场景了。

说起来简单,但是要和可视化拖拽结合起来就没那么简单了。

我们通过拖拽得到每个区块的宽高和位置信息, 借此来确定我们的区块应该放在哪个位置。

然后将其与antd的栅格布局的API结合, 生成我们想要的布局代码。

用过antd的栅格布局的应该知道, Row控制行布局, Col控制列布局。我的思路是:

整个页面都是一个Row, 然后根据拖拽信息划分列, 每个列里都是占满整个列宽的列, 这样就可以往下堆叠。

Col通过span属性控制宽度, 高度我们直接通过style属性加上去。

实现

实现可视化拖拽

这里不得不介绍一下强大的react-grid-layout库了。

git地址: react-grid-layout

我们只需要提供简单的基础信息就能生成可拖拽可伸缩的区块。 这里对API不做过多介绍, 主要将实现。

const dragItem = { x: 14, y: 14, w: 1, h: 1, maxH: 20 }

<GridLayout 600="" className="layout" onResizeStop="{onResizeStop}" onDragStop="{onDragStop}" preventCollision="{true}" margin="{[0," 0]}="" cols="{12}" rowHeight="{30}" width="{800}" style="{{" height:="" }}="">
    {
        Object.keys(dragObj).map(item => <div className="drag-item" style="{{" backgroundColor:="" '#'="" +="" ('00000'="" (Math.random()="" *="" 0x1000000="" <<="" 0).toString(16)).slice(-6)="" }}="" key="{item}" data-grid="{dragObj[item]}"/>)
    }
</GridLayout>

数据驱动视图, 我们通过添加区块事件改变代码中的dragObj, 即可实现往视图中添加区块。

当我们改变了视图中的区块位置或大小时, 我们需要保存当前的区块信息, 方便我们接下来的生成布局代码。

上面代码中的onResizeStoponDragStop事件可以帮我们监听我们的拖拽结束和改变区块大小的动作。

这两个事件做的事情是相同的, 记录当前区块状态, 这里看下事件代码:

const onResizeStop = (arr) => {
        let obj = {}, layoutObj = {}
        arr.forEach(item => {
            obj[item['i']] = { x: item['x'], y: item['y'], w: item['w'], h: item['h'], maxH: item['maxH'] }
            if (layoutObj[item['x']]) {
                layoutObj[item['x']].push(item)
            } else {
                layoutObj[item['x']] = [item]
            }
        })
        setLayoutObj(layoutObj)
        setDragObj(obj)
    }

这两个事件都会给我们提供一个包含所有区块信息的数组参数, 我们通过对这个参数做解析, 将其改造成我们可以使用的数据结构, 并保存在state中。

解析对象生成代码

当我们通过拖拽生成我们想要的数据结构后, 要生成我们想要的页面, 此时我们需要知道, 区块的位置和宽高如何映射到我们的布局中去

我这里通过约定: 拖拽区域大小为宽800, 高600。 每个区块的基础划分:

将高度20等分, 宽度12等分。

这样:

一个单位的h对应0.5vh的高度;

一个单位的w对应2个单位的span(antd的Col的span是24等分)

有了映射关系, 我们只要把存储区块信息的对象改造成代码串就行了:

const onCodeGenerator = () => {
        setCodeStr(
            '<Row>\n' 
            + 
            Object.keys(layoutObj).map(item => `    
            <Col span="{${layoutObj[item][0].w" *="" 2}}="" className="drag-item">
            ${layoutObj[item].map(subItem => `      
                <Col className="drag-subitem" style="{{height:'${subItem.h" *="" 5}vh'}}="" span="{24}"/>\n`).join('')}
            </Col>\n`
            ).join('')
            +
            '</Row>'
        )

    }

通过上面的代码进行改造, 就能得到我们想要的代码串, 直接复制, 粘贴到我们的项目中就得到了一个合适的布局。

后话

这算是一个工具性的东西, 我是根据当前公司业务的基础上进行的开发, 所以可能对大家的适用性不是很高, 这里记录下思路, 方便以后有类似的需求可以参考。

原文链接:juejin.im

上一篇:pm2 实践指南
下一篇:Git 实战手册

相关推荐

  • 🍊仿Element自定义Vue组件库

    (/public/upload/643b972fb2ebd2e6272ff8b16712b205) 前言 🍊 市面上目前已有各种各样的UI组件库,他们的强大毋庸置疑。

    1 个月前
  • 鼠标事件实现拖拽

    为什么需要拖拽 当前的互联网用户早已习惯了拖拽,习惯了拖拽带来的便利。任何一个前端项目都有加入拖拽这个功能的可能性。 拖拽的实现方案 鼠标事件实现 drag and drop api 这两种实现方...

    6 个月前
  • 面板拖拽之vue自定义指令

    前言 在指令里获取的this并不是vue对象,vnode.context才是vue对象,一般来说,指令最好不要访问vue上的data,以追求解耦,但是可以通过指令传进来的值去访问method或re...

    1 年前
  • 钉钉小程序 之 自定义组件的使用,以及父组件与子组件(自定义组件)传值

    目录: 本机环境 自定义组件(子组件) 页面中引入(父组件引入子组件) 父组件(使用自定义组件的页面)传值给子组件 子组件(自定义组件)传值给父组件 备注 一、本机环境 二、自定义组件(子...

    3 个月前
  • 采用shell自定义脚本,控制集成部署环境更加方便灵活

    Jenkins 是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变得可能。现在软件开发追求的是效率以及质量,Jenkins使得自动化成为可能! 亮点 采用shell自定义脚本,控...

    1 年前
  • 逻辑强化系列(一):彻底搞懂自定义组件使用 v-model

    前言 阅读本文前,希望你已经彻底理解了语法糖 vmodel 以及父子组件之间的通讯方法 vmodel 在组件上使用 vmodel 之前首先要知道,vmodel 的用处以及实际操作流程,以方便理解,而不...

    3 个月前
  • 通过字符串var [复制]获得自定义对象属性

    CommunityJayNicolas Hackleman(https://stackoverflow.com/users/1/community)提出了一个问题:Getting a Custom O...

    2 年前
  • 通过nodejs实现自定义命令

    我们开发用到的一些库都有自己特有的命令,如webpack,babel和jest等。通过给这些命令输入不同的参数,可以得到相应的功能。通过篇文章,你将学会如何一步步地编写运行在弄的环境的自定义命令。

    3 年前
  • 通过JavaScript的自定义富文本实现短信模板功能

    前言 公司最近有一个发送短信模板的功能实现需求。在网上找到一篇范文,发现并不适合我们的项目,引用了40%的内容,20%的代码,改成了自己的一个封装函数。 此demo在于实现自定义的短信模板,比如有...

    10 个月前
  • 轻松实现拖拽和预投影(附demo代码)

    感觉放弃csdn了,于是同样的内容搬迁到掘金上~~~也不是别的,我就是感觉csdn上放放问题就好了,有意思的小分享还是得掘金~(这绝不是舔,绝不是!!!) 这次的需求呢,听起来比较正常,指定目标区域的...

    2 个月前

官方社区

扫码加入 JavaScript 社区