前言

本例是在React中实现,不过改一改通过原生js也很好实现,另外兼容性也做到了IE9。(IE8讲道理也是可以的)。

首先看一下需要实现的需求:

要拖动图中的白色横条调整绿色和蓝色区域的高度,要拖动白色竖条调整左边区域和红色区域的宽度。

一两年前曾经遇到过这个需求,当时直接在网上搜了个解决方案贴上去了,不过那个解决方案很挫。

这次的项目又遇到这个需求,而且是三个块的拖动。不仅需要左右拖动还需要上下拖动。

在这里特地记录下解决方案,也希望可以得到一些反馈与优化。

方案的思路

横条拖动和竖条拖动原理差不多,那就先来实现竖条左右拖动调整宽度。

水平方向的布局是通过以下方式实现:

  1. .left{
  2. width: 500px;
  3. height: 100%;
  4. float: left;
  5. position: relative;
  6. }
  7. .v-resize{
  8. height: 100%;
  9. width: 4px;
  10. position: absolute;
  11. background: #fff;
  12. left: 500px;
  13. z-index: 2;
  14. cursor: col-resize;
  15. user-select: none;
  16. }
  17. .right{
  18. margin-left: 504px;
  19. background-color: lightsalmon;
  20. height: 100%;
  21. }

通过样式我们可以了解到,只要同时改变left块的宽度,白色竖条v-resize的left定位和right块的定位(这个数相差不大,我们将这个数定义为vNum),即可实现水平拖动的效果。

通过鼠标按下白色竖条,开启水平拖动,监控鼠标位置,计算vNum,在鼠标放开或者鼠标移出拖动区域时停止水平拖动。

计算vNum的本质实际上就是通过鼠标位置减去拖动区域离浏览器左侧位置,从而得到vNum。

因为每块区域都有最大和最小宽度的限制,这里仅仅加了个vNumLimit来进行限制,即竖条最少要离最左侧和最右侧的距离。

这里还要考虑到每次窗口resize时,各个参数可能都会有所调整,所以加了窗口resize的处理,当然也加了防抖。

本来想要分步讲解,但是冬寒人乏,懒病发作。

方案的实现

jsx部分

  1. import React, { Component } from 'react'
  2. import _ from 'underscore'
  3. import styles from './index.css'
  4. // 可调整宽高的Div
  5. export default class ResizeDiv extends Component {
  6. state = {
  7. isHResize: false,
  8. isVResize: false,
  9. hNum: 100,
  10. vNum: 500,
  11. hNumLimit: 30,
  12. vNumLimit: 30
  13. }
  14. resizeOffsetInfo = {
  15. clientTop: 0,
  16. clientLeft: 0
  17. }
  18. leftHeight = 0
  19. containerWidth = 0
  20. componentDidMount() {
  21. this.initResizeInfo()
  22. const throttled = _.throttle(() => {
  23. this.initResizeInfo()
  24. }, 200)
  25. window.onresize = throttled
  26. }
  27. componentWillUnmount() {
  28. window.onresize = null
  29. }
  30. /**
  31. * 初始化resize信息
  32. */
  33. initResizeInfo = () => {
  34. const hEle = document.getElementById('h_resize_container')
  35. this.resizeOffsetInfo = this.getEleOffset(hEle)
  36. this.leftHeight = hEle.offsetHeight
  37. this.containerWidth = document.getElementById('v_resize_container').offsetWidth
  38. }
  39. /**
  40. * 获取元素的偏移信息
  41. */
  42. getEleOffset(ele) {
  43. var clientTop = ele.offsetTop
  44. var clientLeft = ele.offsetLeft
  45. let current = ele.offsetParent
  46. while (current !== null) {
  47. clientTop += current.offsetTop
  48. clientLeft += current.offsetLeft
  49. current = current.offsetParent
  50. }
  51. return {
  52. clientTop,
  53. clientLeft,
  54. height: ele.offsetHeight,
  55. width: ele.offsetWidth
  56. }
  57. }
  58. /**
  59. * 开始拖动水平调整块
  60. */
  61. hResizeDown = () => {
  62. this.setState({
  63. isHResize: true
  64. })
  65. }
  66. /**
  67. * 拖动水平调整块
  68. */
  69. hResizeOver = (e) => {
  70. const { isHResize, hNum, hNumLimit } = this.state
  71. if (isHResize && hNum >= hNumLimit && (this.resizeOffsetInfo.height - hNum >= hNumLimit)) {
  72. let newValue = this.resizeOffsetInfo.clientTop + this.resizeOffsetInfo.height - e.clientY
  73. if (newValue < hNumLimit) {
  74. newValue = hNumLimit
  75. }
  76. if (newValue > this.resizeOffsetInfo.height - hNumLimit) {
  77. newValue = this.resizeOffsetInfo.height - hNumLimit
  78. }
  79. this.setState({
  80. hNum: newValue
  81. })
  82. }
  83. }
  84. /**
  85. * 开始拖动垂直调整块
  86. */
  87. vResizeDown = () => {
  88. this.setState({
  89. isVResize: true
  90. })
  91. }
  92. /**
  93. * 拖动垂直调整块
  94. */
  95. vResizeOver = (e) => {
  96. const { isVResize, vNum, vNumLimit } = this.state
  97. if (isVResize && vNum >= vNumLimit && (this.containerWidth - vNum >= vNumLimit)) {
  98. let newValue = e.clientX - this.resizeOffsetInfo.clientLeft
  99. if (newValue < vNumLimit) {
  100. newValue = vNumLimit
  101. }
  102. if (newValue > this.containerWidth - vNumLimit) {
  103. newValue = this.containerWidth - vNumLimit
  104. }
  105. this.setState({
  106. vNum: newValue
  107. })
  108. }
  109. }
  110. /**
  111. * 只要鼠标松开或者离开区域,那么就停止resize
  112. */
  113. stopResize = () => {
  114. this.setState({
  115. isHResize: false,
  116. isVResize: false
  117. })
  118. }
  119. render() {
  120. const hCursor = this.state.isHResize ? 'row-resize' : 'default'
  121. const hColor = this.state.isHResize ? '#ddd' : '#fff'
  122. const vCursor = this.state.isVResize ? 'col-resize' : 'default'
  123. const vColor = this.state.isVResize ? '#ddd' : '#fff'
  124. return (
  125. <div className={styles['container']} onMouseUp={this.stopResize} onMouseLeave={this.stopResize}>
  126. <div id='v_resize_container' className={styles['content']} onMouseMove={this.vResizeOver}>
  127. <div id='h_resize_container' style={{ width: this.state.vNum, cursor: vCursor }} className={styles['left']}
  128. onMouseMove={this.hResizeOver}>
  129. <div style={{ bottom: this.state.hNum, cursor: hCursor }} className={styles['left-top']}>aasd</div>
  130. <div style={{ bottom: this.state.hNum, backgroundColor: hColor }} draggable={false} onMouseDown={this.hResizeDown} className={styles['h-resize']} />
  131. <div style={{ height: this.state.hNum + 4, cursor: hCursor }} className={styles['left-bottom']}>asd</div>
  132. </div>
  133. <div style={{ left: this.state.vNum, backgroundColor: vColor }} draggable={false} onMouseDown={this.vResizeDown} className={styles['v-resize']} />
  134. <div style={{ marginLeft: this.state.vNum + 4, cursor: vCursor }} className={styles['right']}>
  135. asdas
  136. </div>
  137. </div>
  138. </div>
  139. )
  140. }
  141. }

css部分

  1. .container{
  2. margin: 30px;
  3. overflow: hidden;
  4. position: absolute;
  5. top: 0;
  6. left: 0;
  7. bottom: 0;
  8. right: 0;
  9. }
  10. .content{
  11. position: absolute;
  12. top: 0;
  13. left: 0;
  14. bottom: 0;
  15. right: 0;
  16. min-height: 300px;
  17. }
  18. .left{
  19. width: 500px;
  20. height: 100%;
  21. float: left;
  22. position: relative;
  23. }
  24. .left-top{
  25. position: absolute;
  26. top: 0;
  27. bottom: 104px;
  28. width: 100%;
  29. background-color: lightblue;
  30. }
  31. .h-resize{
  32. height: 4px;
  33. width: 100%;
  34. background: #fff;
  35. position: absolute;
  36. bottom: 100px;
  37. z-index: 1;
  38. cursor: row-resize;
  39. user-select: none;
  40. }
  41. .left-bottom{
  42. position: absolute;
  43. bottom: 0;
  44. width: 100%;
  45. height: 100px;
  46. background-color: lightgreen;
  47. }
  48. .v-resize{
  49. height: 100%;
  50. width: 4px;
  51. position: absolute;
  52. background: #fff;
  53. left: 500px;
  54. z-index: 2;
  55. cursor: col-resize;
  56. user-select: none;
  57. }
  58. .right{
  59. margin-left: 504px;
  60. background-color: lightsalmon;
  61. height: 100%;
  62. }

总结

技术上其实还是比较简单的,不过丝般润滑的左右移动还是挺有成就感的。

如果有更好的玩法还望不吝赐教。

这是这个demo的地址:demo地址

实现可调整宽高的DIV(左右拖动和上下拖动)的更多相关文章

  1. 不定宽高的DIV,垂直水平居中

    1.怎么让一个不定宽高的DIV,垂直水平居中? 答:1)使用CSS方法. 父盒子设置: display:table-cell; text-align:center; vertical-align:mi ...

  2. 固定宽高的DIV绝对居中示例

    看了一些代码,然后自己试验了一番,分享如下示例: 实现点: 如果元素的宽高固定,那么,css指定样式为top:50%;left:50%; 而margin-top和 margin-left 指定为负数, ...

  3. css实现不定宽高的div水平、垂直居中

    一共有三个方案: 1,第一种方案主要使用了css3中transform进行元素偏移,效果非常好 这方法功能很强大,也比较灵活,不仅仅局限在实现居中显示.  兼容方面也一样拿IE来做比较,第二种方法IE ...

  4. 已知宽高和未知宽高的div块的水平垂直居中

    //已知宽高的情况 .div1_container{     border:1px solid #00ee00;     height:300px;     position:relative; } ...

  5. 怎么让一个不定宽高的div垂直水平居中?

    方法一:使用CSS3 transform 父盒子设置:position:relative; div设置:position:absolute;transform:translate(-50%,-50%) ...

  6. handsontable组件和jqwidgets(jqxdragdrop组件)在一个页面产生调整宽高bug

    修改handsontable.full.js handsontable绑定的"mouseup"事件,默认是window区域太大.引起冲突.

  7. 写个js动态调整图片宽高 (原创)

    <body style="TEXT-ALIGN: center;"> <div id="testID" style="backgro ...

  8. css实现div不定宽高垂直水平居中解决方案

    在项目中我们经常能碰见然图片垂直水平居中,不定宽高的div垂直水平居中,等等~~ 现在我将介绍我所知道的几种用css来解决的几种方案. 1.父元素text-align:center;display:t ...

  9. iview Carousel 轮播图自适应宽高;iview 轮播图 图片重叠问题;iview tabs 高度互相影响问题;vue this问题;

    最终效果图: 一.轮播图中图片自适应宽高:  <Carousel loop v-bind:height="imgHeight+'px'" v-model="caro ...

随机推荐

  1. Redis常用命令【字符串】

    1.启动Redis客户端 进入src目录下,执行:redis-cli启动Redis客户端 2.help 帮助 帮助命令,用来查看redis命令的使用方式 3.set 设置 3.1设置 3.2不存在才设 ...

  2. SpringBoot Controller 中 HttpServletRequest ServletInputStream 读取不到数据该怎么处理

    在Springboot程序启动后,会默认添加OrderedCharacterEncodingFilter和HiddenHttpMethodFilter过滤器.在HiddenHttpMethodFilt ...

  3. 转:修改IIS虚拟目录名称bat脚本

    @echo off echo ------------------------------------------------------------------------------ echo - ...

  4. 计数排序与桶排序python实现

    计数排序与桶排序python实现 计数排序 计数排序原理: 找到给定序列的最小值与最大值 创建一个长度为最大值-最小值+1的数组,初始化都为0 然后遍历原序列,并为数组中索引为当前值-最小值的值+1 ...

  5. LeetCode算法题-Maximum Depth of Binary Tree

    这是悦乐书的第164次更新,第166篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第23题(顺位题号是104).给定二叉树,找到它的最大深度.最大深度是从根节点到最远叶节 ...

  6. U盘插入电脑3.0的口没有反应了,2.0的口就可以

    如果驱动没有问题的话,很有可能是优盘硬件故障. 尝试解决办法: 1.使劲插(就是用力一插到底).... 2.插入三分之一,不过速度只能达到2.0的速度.

  7. spark SQL读取ORC文件从Driver启动到开始执行Task(或stage)间隔时间太长(计算Partition时间太长)且产出orc单个文件中stripe个数太多问题解决方案

    1.背景: 控制上游文件个数每天7000个,每个文件大小小于256M,50亿条+,orc格式.查看每个文件的stripe个数,500个左右,查询命令:hdfs fsck viewfs://hadoop ...

  8. Fluentd初探 简介与安装

    Fluentd是一个开源的数据收集器,专为处理数据流设计,有点像 syslogd ,但是使用JSON作为数据格式.它采用了插件式的架构,具有高可扩展性高可用性,同时还实现了高可靠的信息转发. 据分(Y ...

  9. (转)Spring Boot(八):RabbitMQ 详解

    http://www.ityouknow.com/springboot/2016/11/30/spring-boot-rabbitMQ.html RabbitMQ 即一个消息队列,主要是用来实现应用程 ...

  10. 面试总结——JVM篇

    前言:该篇主要对Java虚拟机相关的题目进行介绍. JVM篇 基本上在面试的时候,都会或多或少的涉及JVM,主要看面试官的侧重点,笔者在面试过程中,是通过volatile问题,引导了JVM相关问题上的 ...