js如何实现div的合并与分解。

新手上路,请多包涵

image.png
我是用于做大屏监控系统的。大佬们能帮忙看下我的需求提供思路吗
需求一:通过选定几个连在一起的div合并为一个新的矩形。
需求二:选定某个div,让其分裂为四个小的div。

阅读 4k
2 个回答

跟我现在公司的业务估计差不多,下面代码只是大概给出一个思路,希望有用。代码里面好像有 Bug,合并一次之后,就不能再合并了,应该是事件、DOM 上面有问题,暂时也没时间弄了,你自己查查吧。

补充:再补充一下,示例里面 cell 并没有放任何内容,理论上来讲,按需求的话,应该还需要放 video 或者其它视频控制或者 chart 图表之类的,在事件监听上面看看是不是有问题,不过如果使用的是 VUE 或者 React 之类的框架的话,应该就没有这些问题了,这里面的示例没有任何任何 UI 框架,如果使用 UI 框架的话,应该会简单很多,核心逻辑其实就是两个类, Cell 类管理某一个 cell 的所有状态, LayoutManager 类就管理布局相关的逻辑,如果使用 UI 框架,那就是两个组件了, Cell 的子组件就是各种各样真实的业务组件实例。

https://codepen.io/pantao/pen...

HTML

<div id="cast-wall"></div>

<div id="tools">
  <button data-action="split">拆分</button>
  <button data-action="composite">合并</button>
</div>

CSS

body {
  margin: 0;
}

#cast-wall {
  width: 100vw;
  height: calc(100vh - 50px);
  background-color: #345;
  position: relative;
}

#tools {
  height: 50px;
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
  padding: 0 20px;
}

JavaScript

const createUuid = () => {
  // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  const _lut = [];

  for (let i = 0; i < 256; i++) {
    _lut[i] = (i < 16 ? '0' : '') + i.toString(16);
  }

  const d0 = (Math.random() * 0xffffffff) | 0;
  const d1 = (Math.random() * 0xffffffff) | 0;
  const d2 = (Math.random() * 0xffffffff) | 0;
  const d3 = (Math.random() * 0xffffffff) | 0;
  const uuid =
    _lut[d0 & 0xff] +
    _lut[(d0 >> 8) & 0xff] +
    _lut[(d0 >> 16) & 0xff] +
    _lut[(d0 >> 24) & 0xff] +
    '-' +
    _lut[d1 & 0xff] +
    _lut[(d1 >> 8) & 0xff] +
    '-' +
    _lut[((d1 >> 16) & 0x0f) | 0x40] +
    _lut[(d1 >> 24) & 0xff] +
    '-' +
    _lut[(d2 & 0x3f) | 0x80] +
    _lut[(d2 >> 8) & 0xff] +
    '-' +
    _lut[(d2 >> 16) & 0xff] +
    _lut[(d2 >> 24) & 0xff] +
    _lut[d3 & 0xff] +
    _lut[(d3 >> 8) & 0xff] +
    _lut[(d3 >> 16) & 0xff] +
    _lut[(d3 >> 24) & 0xff]; // .toUpperCase() here flattens concatenated strings to save heap memory space.

  return uuid.toUpperCase();
};


// 设定整个容器的宽高分别为 100
class Cell {
  // 宽度,百分比值
  _width = 0;
  
  // 高度,百分比值
  _height = 0;
  
  // 距离顶部位置,百分比值
  _top = 0;
  
  // 距离左侧位置,百分比值
  _left = 0;
  
  _selected = false;
  
  uuid = '';
  
  // 当前 cell 的元素
  element = null;
  
  // 子级
  children = [];
  
  // 父级
  parent = null;
  
  mounted = false;
  
  get left() {
    return this._left;
  }
  
  set left(left) {
    this.element.style.left = `${left}%`;
    this._left = left;
  }
  
  get top() {
    return this._top;
  }
  
  set top(top) {
    this.element.style.top = `${top}%`;
    this._top = top;
  }
  
  get width() {
    return this._width;
  }
  
  set width(width) {
    this.element.style.width = `${width}%`;
    this._width = width;
  }
  
  get height() {
    return this._height;
  }
  
  set height(height) {
    this.element.style.height = `${height}%`;
    this._height = height;
  }
  
  get selected() {
    return this._selected;
  }
  
  set selected(selected) {
    if (selected) {
      this.element.style.borderColor = 'rgba(255, 200, 200, 0.7)';
    } else {
      this.element.style.borderColor = 'rgba(255, 255, 255, 0.3)';
    }
    this._selected = selected;
  }
  
  constructor(left, top, width, height) {
    this.uuid = createUuid();
    
    this.element = document.createElement('div');
    this.element.style.position = 'absolute';
    this.element.style.boxSizing = 'border-box';
    this.element.style.border = '1px solid rgba(255, 255, 255, 0.3)';
    this.element.classList.add('cell');
    this.element.dataset.uuid = this.uuid;
    
    this.left = left;
    this.top = top;
    this.width = width;
    this.height = height;
  }
  
  /**
   * 拆分
   * 逻辑就是把宽拆分
   */
  split(cols = 2, rows = 2) {
    if (cols <= 0 || rows <= 0) {
      throw new TypeError('行列数量错误')
    }
    const x = Math.ceil(cols);
    const y = Math.ceil(rows);
    
    const { left, top } = this;
    const width = this.width / cols;
    const height = this.height / rows;
    const cells = [];
    
    for (let i = 0; i < x; i += 1) {
      for (let j = 0; j < y; j += 1) {
        const splitted = new Cell(left + i * width, top + j * height, width, height);
        splitted.parent = this;
        cells.push(splitted);
      }
    }
    return cells;
  }
  
  // 拼合
  composite(cell) {
    if (!cell) {
      throw new TypeError('cell required');
    }
    
    // 合并
    let pos = '';
    let { top, left, width, height } = this;
    if (
      // 处于同一行之中
      top === cell.top &&
      // 那么必须高度一至
      height === cell.height
    ) {
      width += cell.width;
      // 在我左侧,我的左侧正好是 cell 的右边
      if (left === cell.left + cell.width) {
        left = cell.left;
        pos = 'left';
      }
      // 在我的右侧
      else if (left + this.width === cell.left) {
        pos = 'right';
      } else {
        // 不连续
        throw new Error('处于同一行,但不连续');
      }
    }
    // 处于同一列
    else if (
      // 处于同一列之中
      left === cell.left &&
      // 即么宽度必须一致
      width === cell.width
    ) {
      height += cell.height;
      // 在我上面
      if (top = cell.top + cell.height) {
        top = cell.top;
        pos = 'top';
      }
      // 在我下面
      else if (top + this.height === cell.top) {
        pos = 'under';
      } else {
        // 不连续
        throw new Error('处于同一列,但不连续');
      }
    } else {
      throw new Error('不连续')
    }
    
    Object.assign(this, { left, top, width, height });
    cell.selected = false;
    this.children = [cell];
    return this;
  }
}

class LayoutManager {
  // 当前 Layout Manager 的 cells
  cells = []
  
  element = null;
  
  toolsElement = null;
  
  constructor(element, toolsElement) {
    this.handleCellClick = this.handleCellClick.bind(this);
    
    this.element = element;
    this.toolsElement = toolsElement;
    
    this.toolsElement.addEventListener('click', this.handleToolsClick.bind(this))
    
    element.style.position = 'relative';
    
    const cell = new Cell(0, 0, 100, 100);
    this.mountCell(cell);
    this.cells = [cell];
  }
  
  /**
   * 挂载节点
   */
  mountCell(cell) {
    this.element.appendChild(cell.element);
    this.element.addEventListener('click', this.handleCellClick);
    cell.mounted = true;
  }
  
  unmountCell(cell) {
    this.element.removeChild(cell.element);
    this.element.removeEventListener('click', this.handleCellClick);
    cell.mounted = false;
  }
  
  split(cell_) {
    const cell = cell_ || this.cells.filter(c => c.selected);
    if (Array.isArray(cell)) {
      cell.forEach(c => this.split(c));
      return this
    }
    
    const index = this.cells.findIndex(c => c.uuid === cell.uuid);
    
    if (index === -1) {
      return this;
    }
    
    const splitted = cell.split();
    
    cell.selected = false;
    this.unmountCell(cell);
    
    this.cells.splice(index, 1, ...splitted);
    
    splitted.forEach(c => this.mountCell(c));
 
    return this;
  }
  
  composite(cells_) {
    const cells = cells_ || this.cells.filter(c => c.selected);
    
    if (Array.isArray(cells) && cells.length > 1) {
      console.log(cells);
      let cell = cells[0];
      let index
      for (let i = 1; i < cells.length; i += 1) {
        try {
          // 尝试合并当前 cell 与下一个 cell
          cell.composite(cells[i]);
          this.unmountCell(cells[i]);
          index = this.cells.findIndex(c => c.uuid === cells[i].uuid);
          if (index > -1) {
            this.cells.splice(index, 1);
          }
        } catch (error) {
          // 失败的话
          console.log(error.message);
          cell = cells[i];
        }
      }
      
    }
    
    return this;
  }
  
  handleCellClick(event) {
    const { uuid } = event.target.dataset;
    if (uuid) {
      const cell = this.cells.find(cell => cell.uuid === uuid);
      if (cell) {
        cell.selected = !cell.selected;
      }
    }
  }
  
  handleToolsClick(event) {
    try {
      const { action } = event.target.dataset;
      
      switch(action) {
        case 'split':
          this.split();
          break;
        case 'composite':
          this.composite();
          break;
      }
    } catch (error) {
      console.log(error);
    }
  }
}

const main = () => {
  const wall = new LayoutManager(
    document.getElementById('cast-wall'),
    document.getElementById('tools')
  );
}

main();
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题