当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 实现可调整宽高的DIV(左右拖动和上下拖动)

实现可调整宽高的DIV(左右拖动和上下拖动)

2018年12月14日  | 移动技术网IT编程  | 我要评论

前言

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

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

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

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

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

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

方案的思路

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

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

.left{
  width: 500px;
  height: 100%;
  float: left;
  position: relative;
}

.v-resize{
  height: 100%;
  width: 4px;
  position: absolute;
  background: #fff;
  left: 500px;
  z-index: 2;
  cursor: col-resize;
  user-select: none;
}

.right{
  margin-left: 504px;
  background-color: lightsalmon;
  height: 100%;
}

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

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

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

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

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

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

方案的实现

jsx部分

import react, { component } from 'react'
import _ from 'underscore'
import styles from './index.css'

// 可调整宽高的div
export default class resizediv extends component {
  state = {
    ishresize: false,
    isvresize: false,
    hnum: 100,
    vnum: 500,
    hnumlimit: 30,
    vnumlimit: 30
  }

  resizeoffsetinfo = {
    clienttop: 0,
    clientleft: 0
  }

  leftheight = 0

  containerwidth = 0

  componentdidmount() {
    this.initresizeinfo()
    const throttled = _.throttle(() => {
      this.initresizeinfo()
    }, 200)

    window.onresize = throttled
  }
  componentwillunmount() {
    window.onresize = null
  }

  /**
  * 初始化resize信息
  */
  initresizeinfo = () => {
    const hele = document.getelementbyid('h_resize_container')
    this.resizeoffsetinfo = this.geteleoffset(hele)
    this.leftheight = hele.offsetheight
    this.containerwidth = document.getelementbyid('v_resize_container').offsetwidth
  }

  /**
  * 获取元素的偏移信息
  */
  geteleoffset(ele) {
    var clienttop = ele.offsettop
    var clientleft = ele.offsetleft
    let current = ele.offsetparent
    while (current !== null) {
      clienttop += current.offsettop
      clientleft += current.offsetleft
      current = current.offsetparent
    }
    return {
      clienttop,
      clientleft,
      height: ele.offsetheight,
      width: ele.offsetwidth
    }
  }

  /**
  * 开始拖动水平调整块
  */
  hresizedown = () => {
    this.setstate({
      ishresize: true
    })
  }

  /**
  * 拖动水平调整块
  */
  hresizeover = (e) => {
    const { ishresize, hnum, hnumlimit } = this.state
    if (ishresize && hnum >= hnumlimit && (this.resizeoffsetinfo.height - hnum >= hnumlimit)) {
      let newvalue = this.resizeoffsetinfo.clienttop + this.resizeoffsetinfo.height - e.clienty
      if (newvalue < hnumlimit) {
        newvalue = hnumlimit
      }
      if (newvalue > this.resizeoffsetinfo.height - hnumlimit) {
        newvalue = this.resizeoffsetinfo.height - hnumlimit
      }
      this.setstate({
        hnum: newvalue
      })
    }
  }

  /**
  * 开始拖动垂直调整块
  */
  vresizedown = () => {
    this.setstate({
      isvresize: true
    })
  }

  /**
  * 拖动垂直调整块
  */
  vresizeover = (e) => {
    const { isvresize, vnum, vnumlimit } = this.state
    if (isvresize && vnum >= vnumlimit && (this.containerwidth - vnum >= vnumlimit)) {
      let newvalue = e.clientx - this.resizeoffsetinfo.clientleft
      if (newvalue < vnumlimit) {
        newvalue = vnumlimit
      }
      if (newvalue > this.containerwidth - vnumlimit) {
        newvalue = this.containerwidth - vnumlimit
      }
      this.setstate({
        vnum: newvalue
      })
    }
  }

  /**
  * 只要鼠标松开或者离开区域,那么就停止resize
  */
  stopresize = () => {
    this.setstate({
      ishresize: false,
      isvresize: false
    })
  }

  render() {
    const hcursor = this.state.ishresize ? 'row-resize' : 'default'
    const hcolor = this.state.ishresize ? '#ddd' : '#fff'
    const vcursor = this.state.isvresize ? 'col-resize' : 'default'
    const vcolor = this.state.isvresize ? '#ddd' : '#fff'

    return (
      <div classname={styles['container']} onmouseup={this.stopresize} onmouseleave={this.stopresize}>
        <div id='v_resize_container' classname={styles['content']} onmousemove={this.vresizeover}>
          <div id='h_resize_container' style={{ width: this.state.vnum, cursor: vcursor }} classname={styles['left']}
            onmousemove={this.hresizeover}>
            <div style={{ bottom: this.state.hnum, cursor: hcursor }} classname={styles['left-top']}>aasd</div>
            <div style={{ bottom: this.state.hnum, backgroundcolor: hcolor }} draggable={false} onmousedown={this.hresizedown} classname={styles['h-resize']} />
            <div style={{ height: this.state.hnum + 4, cursor: hcursor }} classname={styles['left-bottom']}>asd</div>
          </div>
          <div style={{ left: this.state.vnum, backgroundcolor: vcolor }} draggable={false} onmousedown={this.vresizedown} classname={styles['v-resize']} />
          <div style={{ marginleft: this.state.vnum + 4, cursor: vcursor }} classname={styles['right']}>
            asdas
          </div>
        </div>
      </div>
    )
  }
}

css部分

.container{
  margin: 30px;
  overflow: hidden;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

.content{
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  min-height: 300px;
}

.left{
  width: 500px;
  height: 100%;
  float: left;
  position: relative;
}

.left-top{
  position: absolute;
  top: 0;
  bottom: 104px;
  width: 100%;
  background-color: lightblue;
}

.h-resize{
  height: 4px;
  width: 100%;
  background: #fff;
  position: absolute;
  bottom: 100px;
  z-index: 1;
  cursor: row-resize;
  user-select: none;
}

.left-bottom{
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 100px;
  background-color: lightgreen;
}

.v-resize{
  height: 100%;
  width: 4px;
  position: absolute;
  background: #fff;
  left: 500px;
  z-index: 2;
  cursor: col-resize;
  user-select: none;
}

.right{
  margin-left: 504px;
  background-color: lightsalmon;
  height: 100%;
}

总结

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

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

这是这个demo在github上的地址:demo地址

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网