制作一个拖动条来调整 CSS 网格内的 div 大小

问题描述 投票:0回答:4

我有 2 个盒子和一个垂直的 div 线,位于一个独特的容器 div 中(代码和小提琴如下)。

我使用 CSS 网格将元素放置在容器内

我想要完成的是使用垂直线根据垂直线的位置水平调整两个框的大小。

如果这个问题很菜鸟,我很抱歉,我是网络开发新手,之前只使用过Python,已经尝试过谷歌和stackoverflow搜索,但所有解决方案似乎都过于复杂并且通常需要额外的库,我一直在寻找更简单的东西并且仅使用JS。

HTML:

<div class="wrapper">
  <div class="box a">A</div>
  <div class="handler"></div>
  <div class="box b">B</div>
</div>

CSS:

body {
  margin: 40px;
}

.wrapper {
  display: grid;
  grid-template-columns: 200px 8px 200px;
  grid-gap: 10px;
  background-color: #fff;
  color: #444;
}

.box {
  background-color: #444;
  color: #fff;
  border-radius: 5px;
  padding: 20px;
  font-size: 150%;
  resize: both;
}

.handler{
    width: 3px;
    height: 100%;
    padding: 0px 0;
    top: 0;
    background: red;
    draggable: true;
}

https://jsfiddle.net/gv8Lwckh/6/

javascript html css css-grid
4个回答
111
投票

你想要做的事情可以使用 CSS flexbox 来完成——不需要使用 CSS grid。坏消息是 HTML + CSS 并没有那么智能,声明

resize
draggable
将使布局变得灵活并且可以通过用户交互进行调整。为此,你必须使用 JS。好消息是这实际上并不太复杂。

这是以下代码输出的快速屏幕截图:

但是,为了让您理解我将在下面发布的代码,您必须熟悉:

  • 使用
    .addEventListener
    进行事件绑定。在这种情况下,我们将使用
    mousedown
    mouseup
    mousemove
    的组合来确定用户是否正在拖动元素
  • CSS 弹性盒子布局

解决方案描述

使用 CSS 进行初始布局

首先,您需要使用 CSS flexbox 来布局您的盒子。我们只需在父级上声明

display: flex
,然后使用
flex: 1 1 auto
(翻译过来就是“让元素变大,让元素缩小,并且宽度相等)”。这种布局仅在页面初始渲染时有效:

.wrapper {
  /* Use flexbox */
  display: flex;
}

.box {
  /* Use box-sizing so that element's outerwidth will match width property */
  box-sizing: border-box;

  /* Allow box to grow and shrink, and ensure they are all equally sized */
  flex: 1 1 auto;
}

聆听拖动交互

您想要监听可能源自

.handler
元素的鼠标事件,并且您需要一个全局标志来记住用户是否正在拖动:

var handler = document.querySelector('.handler');
var isHandlerDragging = false;

然后你可以使用以下逻辑来检查用户是否在拖动:

document.addEventListener('mousedown', function(e) {
  // If mousedown event is fired from .handler, toggle flag to true
  if (e.target === handler) {
    isHandlerDragging = true;
  }
});

document.addEventListener('mousemove', function(e) {
  // Don't do anything if dragging flag is false
  if (!isHandlerDragging) {
    return false;
  }

  // Set boxA width properly
  // [...more logic here...]
});

document.addEventListener('mouseup', function(e) {
  // Turn off dragging flag when user mouse is up
  isHandlerDragging = false;
});

计算盒子A的宽度

现在剩下的就是计算框 A 的宽度(将插入到上面代码中的

[...more logic here...]
占位符中),以便它与鼠标的移动相匹配。 Flexbox 将确保 B 框填满剩余空间:

// Get offset
var containerOffsetLeft = wrapper.offsetLeft;

// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientX - containerOffsetLeft;

// Resize box A
// * 8px is the left/right spacing between .handler and its inner pseudo-element
// * Set flex-grow to 0 to prevent it from growing
boxA.style.width = (pointerRelativeXpos - 8) + 'px';
boxA.style.flexGrow = 0;

工作示例

var handler = document.querySelector('.handler');
var wrapper = handler.closest('.wrapper');
var boxA = wrapper.querySelector('.box');
var isHandlerDragging = false;

document.addEventListener('mousedown', function(e) {
  // If mousedown event is fired from .handler, toggle flag to true
  if (e.target === handler) {
    isHandlerDragging = true;
  }
});

document.addEventListener('mousemove', function(e) {
  // Don't do anything if dragging flag is false
  if (!isHandlerDragging) {
    return false;
  }

  // Get offset
  var containerOffsetLeft = wrapper.offsetLeft;

  // Get x-coordinate of pointer relative to container
  var pointerRelativeXpos = e.clientX - containerOffsetLeft;
  
  // Arbitrary minimum width set on box A, otherwise its inner content will collapse to width of 0
  var boxAminWidth = 60;

  // Resize box A
  // * 8px is the left/right spacing between .handler and its inner pseudo-element
  // * Set flex-grow to 0 to prevent it from growing
  boxA.style.width = (Math.max(boxAminWidth, pointerRelativeXpos - 8)) + 'px';
  boxA.style.flexGrow = 0;
});

document.addEventListener('mouseup', function(e) {
  // Turn off dragging flag when user mouse is up
  isHandlerDragging = false;
});
body {
  margin: 40px;
}

.wrapper {
  background-color: #fff;
  color: #444;
  /* Use flexbox */
  display: flex;
}

.box {
  background-color: #444;
  color: #fff;
  border-radius: 5px;
  padding: 20px;
  font-size: 150%;
  
  /* Use box-sizing so that element's outerwidth will match width property */
  box-sizing: border-box;
  
  /* Allow box to grow and shrink, and ensure they are all equally sized */
  flex: 1 1 auto;
}

.handler {
  width: 20px;
  padding: 0;
  cursor: ew-resize;
  flex: 0 0 auto;
}

.handler::before {
  content: '';
  display: block;
  width: 4px;
  height: 100%;
  background: red;
  margin: 0 auto;
}
<div class="wrapper">
  <div class="box">A</div>
  <div class="handler"></div>
  <div class="box">B</div>
</div>


31
投票

这是拖动事件处理的示例,但使用 CSS 网格

技巧是在网格容器上设置网格模板列(或行)而不是网格项的大小

let isLeftDragging = false;
let isRightDragging = false;

function ResetColumnSizes() {
  // when page resizes return to default col sizes
  let page = document.getElementById("pageFrame");
  page.style.gridTemplateColumns = "2fr 6px 6fr 6px 2fr";
}

function SetCursor(cursor) {
  let page = document.getElementById("page");
  page.style.cursor = cursor;
}

function StartLeftDrag() {
  // console.log("mouse down");
  isLeftDragging = true;

  SetCursor("ew-resize");
}

function StartRightDrag() {
  // console.log("mouse down");
  isRightDragging = true;

  SetCursor("ew-resize");
}

function EndDrag() {
  // console.log("mouse up");
  isLeftDragging = false;
  isRightDragging = false;

  SetCursor("auto");
}

function OnDrag(event) {
  if (isLeftDragging || isRightDragging) {
    // console.log("Dragging");
    //console.log(event);

    let page = document.getElementById("page");
    let leftcol = document.getElementById("leftcol");
    let rightcol = document.getElementById("rightcol");

    let leftColWidth = isLeftDragging ? event.clientX : leftcol.clientWidth;
    let rightColWidth = isRightDragging ? page.clientWidth - event.clientX : rightcol.clientWidth;

    let dragbarWidth = 6;

    let cols = [
      leftColWidth,
      dragbarWidth,
      page.clientWidth - (2 * dragbarWidth) - leftColWidth - rightColWidth,
      dragbarWidth,
      rightColWidth
    ];

    let newColDefn = cols.map(c => c.toString() + "px").join(" ");

    // console.log(newColDefn);
    page.style.gridTemplateColumns = newColDefn;

    event.preventDefault()
  }
}
#page {
  height: 100%;
  background-color: pink;
  display: grid;
  grid-template-areas: 'header header header header header' 'leftcol leftdragbar tabs tabs tabs' 'leftcol leftdragbar tabpages rightdragbar rightcol' 'leftcol leftdragbar footer footer footer';
  grid-template-rows: min-content 1fr 9fr 1fr;
  grid-template-columns: 2fr 6px 6fr 6px 2fr;
}


/*****************************/

#header {
  background-color: lightblue;
  overflow: auto;
  grid-area: header;
}

#leftcol {
  background-color: #aaaaaa;
  overflow: auto;
  grid-area: leftcol;
}

#leftdragbar {
  background-color: black;
  grid-area: leftdragbar;
  cursor: ew-resize;
}

#tabs {
  background-color: #cccccc;
  overflow: auto;
  grid-area: tabs;
}

#tabpages {
  background-color: #888888;
  overflow: auto;
  grid-area: tabpages;
}

#rightdragbar {
  background-color: black;
  grid-area: rightdragbar;
  cursor: ew-resize;
}

#rightcol {
  background-color: #aaaaaa;
  overflow: auto;
  grid-area: rightcol;
}

#footer {
  background-color: lightblue;
  overflow: auto;
  grid-area: footer;
}
<body onresize="ResetColumnSizes()">
  <div id="page" onmouseup="EndDrag()" onmousemove="OnDrag(event)">
    <div id="header">
      Header
    </div>
    <div id="leftcol">
      Left Col
    </div>
    <div id="leftdragbar" onmousedown="StartLeftDrag()"></div>
    <div id="tabs">
      Tabs
    </div>
    <div id="tabpages">
      Tab Pages
    </div>
    <div id="rightdragbar" onmousedown="StartRightDrag()"></div>
    <div id="rightcol">
      Rightcol
    </div>
    <div id="footer">
      Footer
    </div>
  </div>
</body>

https://codepen.io/lukerazor/pen/GVBMZK


4
投票

我改变了,所以你可以添加更多水平和垂直滑块。 test1.html:

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="test1.css">
        <script src=  "test1.js" > </script>
    </head>
    <body>
        <div id="page" onmouseup="EndDrag()" onmousemove="OnDrag(event)">
            <div id="header">
                Header asdlkj flkdfj sdflkksdjf sd;flsdjf sd;flkjsd;fljsd;flsdj;fjsd f;sdlfj;sdlfj
            </div>
            <div id="leftcol">
                Left Col
            </div>
            <div id="leftdragbar" onmousedown="StartHDrag(1)"></div>
            <div id="tabs">
                Tabs
            </div>
            <div id="topdragbar" onmousedown="StartVDrag(2)"></div>
            <div id="tabpages">
                Tab Pages
            </div>
            <div id="rightdragbar" onmousedown="StartHDrag(3)"></div>
            <div id="rightcol">
                Rightcol
            </div>
            <div id="botdragbar" onmousedown="StartVDrag(4)"></div>
            <div id="footer">
                Footer
            </div>
        </div>
        <div id= 'status'></div>
    </body>
</html>

test1.css

body {
}

#page {
    height: 100vh;
    background-color: pink;
    display: grid;
    grid-template-areas:
        'header header header header header'
        'leftcol leftdragbar tabs tabs tabs'
        'leftcol leftdragbar topdragbar topdragbar topdragbar'
        'leftcol leftdragbar tabpages rightdragbar rightcol'
        'botdragbar botdragbar botdragbar botdragbar botdragbar'
        'footer footer footer footer footer';
    grid-template-rows: min-content 1fr 6px 9fr 6px 1fr;
    grid-template-columns: 2fr 6px 6fr 6px 2fr; 
}

/*****************************/
#header {
    background-color: lightblue;
    overflow: auto;
    grid-area: header;
}

#leftcol {
    background-color: #aaaaaa;
    overflow: auto;
    grid-area: leftcol;
}

#leftdragbar {
    background-color: black;
    grid-area: leftdragbar;
    cursor: ew-resize;
}

#topdragbar {
    background-color: black;
    grid-area: topdragbar;
    cursor: ns-resize;
}

#botdragbar {
    background-color: black;
    grid-area: botdragbar;
    cursor: ns-resize;
}

#tabs {
    background-color: #cccccc;
    overflow: auto;
    grid-area: tabs;
}

#tabpages {
    background-color: #888888;
    overflow: auto;
    grid-area: tabpages;
}

#rightdragbar {
    background-color: black;
    grid-area: rightdragbar;
    cursor: ew-resize;
}


#rightcol {
    background-color: #aaaaaa;
    overflow: auto;
    grid-area: rightcol;
}

#footer {
    background-color: lightblue;
    overflow: auto;
    grid-area: footer;
}

test1.js

let isHDragging = false;
let isVDragging = false;

let cols = ['2fr','6px','6fr','6px','2fr'];  //grid-template-columns: 2fr 6px 6fr 6px 2fr;
let colns = ['leftcol','','tabpages','','rightcol'];
let Tcols = [];

let rows = ['min-content','1fr','6px','9fr','6px','1fr'];  //grid-template-rows: min-content 1fr 6px 9fr 1fr
let rowns = ['header','tabs','','tabpages','','footer'];
let Trows = []
let CLfactor ;
let CRfactor ;
let gWcol = -1;
let gWrow = -1;

function StartHDrag(pWcol) {
    isHDragging = true;
    SetCursor("ew-resize");
    CLfactor = parseFloat(cols[pWcol-1]) / document.getElementById(colns[pWcol-1]).clientWidth;
    CRfactor = parseFloat(cols[pWcol+1]) / document.getElementById(colns[pWcol+1]).clientWidth;
    Tcols = cols.map(parseFloat);
    gWcol = pWcol;
}

function StartVDrag(pRow) {
    isVDragging = true;
    SetCursor("ns-resize");
    CLfactor = parseFloat(rows[pRow-1]) / document.getElementById(rowns[pRow-1]).clientHeight;
    CRfactor = parseFloat(rows[pRow+1]) / document.getElementById(rowns[pRow+1]).clientHeight;
    Trows = rows.map(parseFloat);
    gWrow = pRow;
}

function SetCursor(cursor) {
    let page = document.getElementById("page");
    page.style.cursor = cursor;
}

function EndDrag() {
    isHDragging = false;
    isVDragging = false;
    SetCursor("auto");
}

function OnDrag(event) {
    if(isHDragging) {
        Tcols[gWcol-1] +=  (CLfactor * event.movementX);
        Tcols[gWcol+1] -=  (CLfactor * event.movementX);
        
        cols[gWcol-1]  = Math.max(Tcols[gWcol-1],0.01) + "fr";
        cols[gWcol+1]  = Math.max(Tcols[gWcol+1],0.01) + "fr";
        let newColDefn = cols.join(" ");
        page.style.gridTemplateColumns = newColDefn;
        
    } else if (isVDragging) {
        Trows[gWrow-1] +=  (CLfactor * event.movementY);
        Trows[gWrow+1] -=  (CLfactor * event.movementY);
        
        rows[gWrow-1]  = Math.max(Trows[gWrow-1],0.01) + "fr";
        rows[gWrow+1]  = Math.max(Trows[gWrow+1],0.01) + "fr";
        let newRowDefn = rows.join(" ");
        page.style.gridTemplateRows = newRowDefn;
        document.getElementById("footer").innerHTML = newRowDefn;
    }
    event.preventDefault()
}

3
投票

要真正匹配问题!

Making a dragbar to resize divs inside CSS grids
.


let target = document.querySelector("div") // Target container element
let md = false;     // Will be true at mouse down
let xorigin;        // Click origin X position
let gtcorigin = []; // Origin Grid Template Columns in pixels

const pointerdown = (e) => {
  if (e.target.classList[0] === "handler"){ // Filter to target the wanted element
    md = true;                              // Set mouse down
    xorigin = e.screenX;                    // Store the origin X position
    // Grid Template Columns, array of pixels as float
    gtcorigin = window.getComputedStyle(target)["grid-template-columns"].split(" ").map((a) => +(a.slice(0, -2)));
    document.body.style.cursor = "col-resize" // This makes things nice
    document.body.style.userSelect = "none"   // This makes things nice
  }
}
const pointerup = (e) => {
  md = false; // Reset bool at mouse up
  document.body.style.cursor = "pointer"
  document.body.style.userSelect = "unset"
}

const resizer = (e) => {
  if (md){ // Mouse is down hover the handler element
    let gtc = window.getComputedStyle(target)["grid-template-columns"].split(" ").map((a) => +(a.slice(0, -2)));       // Grid Template Columns, array of pixels as float
    let xdragdif = xorigin - e.screenX; // Move in pixels since the click
    gtc[0] = gtcorigin[0] - xdragdif    // First column, if negative, it will grow
    gtc[2] = gtcorigin[2] + xdragdif    // Third column
    gtc = gtc.map((a) => a+"px")        // Set back the values in string with "px"
    document.querySelector("console").textContent = gtc.join(" ") // !!! This is only for the demo
    target.style.gridTemplateColumns = gtc.join(" ") // Apply the new Grid Template Column as inline style. 
  }
}

// Attach all events on the largest container element. Here the body is used. 
document.body.addEventListener("pointerdown", pointerdown, false)
document.body.addEventListener("pointerup", pointerup, false)
document.body.addEventListener("pointermove", resizer, false)
body {
  margin: 40px;
  overflow-x: hidden
}

.wrapper {
  display: grid;
  grid-template-columns: 200px 8px 200px;
  grid-gap: 10px;
  background-color: #fff;
  color: #444;
}

.box {
  background-color: #444;
  color: #fff;
  border-radius: 5px;
  padding: 20px;
  font-size: 150%;
}

.handler{
  width: 3px;
  height: 100%;
  padding: 0px 0;
  top: 0;
  background: red;
  cursor: col-resize
}
<div class="wrapper">
  <div class="box">A</div>
  <div class="handler"></div>
  <div class="box">B</div>
</div>


<console></console>

© www.soinside.com 2019 - 2024. All rights reserved.