这种拖拽布局功能其实在电脑操作系统或者桌面应用里面是经常使用的基础功能,只是有时候在进行web开发的时候,对这个功能需求量不够明显,但却是很好用,也很实用。能够让用户自己拖拽布局,方便查看某个区域更多内容,这一点足够吸引人的。


这里用原生实现这个特殊的拖拽布局功能,可以作为参考和学习使用。话不多说,先看看实现的静态效果和动态效果。


静态效果:




动态效果:




为了查看更佳效果,需要把源码在浏览器上运行起来。另一个值得关心的事项:案例中左右盒子是可以完全拖拽关闭的,也就是宽度为0px。实际应用中可能不需要这种效果,没关系,可以限制左右盒子的最小宽度,判断小于最小宽度就不再进行缩小即可,放大限制 也是类似的。


实现原理


想象一下,拖拽布局一般都需要两个div盒子,同时需要一个可以拖拽的class=resize盒子(以下简称resize),它们都是块元素。然后加上HTML标签和css的基础左右布局样式,大致轮廓就出来了。


到这里还没有实现拖拽resize布局逻辑,接着往下看。


拖拽resize原理分析:


当用户把鼠标移动到resize盒子上面,此时出现可以左右拖拽的标识,这个标识可以用css的cursor: w-resize来实现。


如果用户按下鼠标左键,可以监听鼠标按下事件,获得鼠标按下的startX = evt.clientX,也能获得resize相对于父元素的左偏移值offsetLeft。


resizeBar.onmousedown(evt) {

 var startX = e.clientX;

 resizeBar.left = resizeBar.offsetLeft;

}

如果用户按住鼠标左键开始向左或者向右拖拽,需要精确计算用户拖拽的实际距离,也是鼠标移动的实际距离。计算方式:resize的左偏移值offsetLeft+(鼠标移动实际距离 + 鼠标按下距离)= 实际移动距离movelen。


document.onmousemove = function (e) {

  var moveLen = resizeBar.left + (e.clientX - startX);

}

那么,resize的左边距离移动实际距离就是这样获得的。


document.onmousemove = function (e) {

  var moveLen = resizeBar.left + (e.clientX - startX);

  resizeBar.style.left = moveLen + 'px';

}

左边|右边盒子宽度就可以得到了。


document.onmousemove = function (e) {

  var moveLen = resizeBar.left + (e.clientX - startX);

  resizeBar.style.left = moveLen + 'px';

  leftBox.style.width = moveLen + "px";

  rightBox.style.width =

  boxWidth - resizeWidth - moveLen + "px";

}

到这里拖拽核心逻辑就结束了,为了更好的理解拖拽过程,于是就有了下方不怎么好看的抽象图,画的不好,多多理解。




源码贴上


HTML


<body>

<div class="box">

   <div class="left overflow">

       <p>左边盒子</p>

   </div>

   <div class="resize"></div>

   <div class="right overflow">

       <p>右边盒子</p>

   </div>

</div>

</body>

CSS


<style>

.box {

   width: 100%;

   height: 100vh;

   display: flex;

}

.left {

   background: lightblue;

}

.resize {

   width: 10px;

   height: 100%;

   background-color: #399aef;

   cursor: w-resize;

}

.right {

   background: rgba(0, 0, 0, 0.4);

}

p {

   padding: 20px;

   text-align: center;

   font-size: 25px;

   font-weight: 600;

}

[class*="overflow"] {

   width: 49%;

   height: 100%;

   overflow: hidden;

}

.userselect {

   -webkit-user-select: none;

   -moz-user-select: none;

   -ms-user-select: none;

   user-select: none;

}

</style>

JavaScript


<script>

function dragResize() {

   var resizeBar = document.querySelector(".resize");

   var leftBox = document.querySelector(".left");

   var box = document.querySelector(".box");

   var rightBox = document.querySelector(".right");

   var resizeWidth = resizeBar.offsetWidth;

   var boxWidth = box.offsetWidth;

   resizeBar.onmousedown = function (e) {

       var startX = e.clientX;

       resizeBar.left = resizeBar.offsetLeft;


       // 鼠标拖动事件

       document.onmousemove = function (e) {

           var moveLen = resizeBar.left + (e.clientX - startX);

           resizeBar.style.left = moveLen + 'px';

           leftBox.style.width = moveLen + "px";

           rightBox.style.width = boxWidth - resizeWidth - moveLen + "px";

           e.target.style.cursor = "w-resize"

           // 拖拽过程中,禁止选中文本

           leftBox.classList.add('userselect')

           rightBox.classList.add('userselect')

       };


       // 鼠标松开事件

       document.onmouseup = function (evt) {

           document.onmousemove = null;

           document.onmouseup = null;

           // 清空cursor

           leftBox.style.cursor = "default"

           rightBox.style.cursor = "default"

           leftBox.classList.remove('userselect')

           rightBox.classList.remove('userselect')

       };

   };

}

dragResize()