我是用于做大屏监控系统的。大佬们能帮忙看下我的需求提供思路吗
需求一:通过选定几个连在一起的div合并为一个新的矩形。
需求二:选定某个div,让其分裂为四个小的div。
我是用于做大屏监控系统的。大佬们能帮忙看下我的需求提供思路吗
需求一:通过选定几个连在一起的div合并为一个新的矩形。
需求二:选定某个div,让其分裂为四个小的div。
跟我现在公司的业务估计差不多,下面代码只是大概给出一个思路,希望有用。代码里面好像有 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();
10 回答11.2k 阅读
5 回答4.8k 阅读✓ 已解决
4 回答3.1k 阅读✓ 已解决
2 回答2.8k 阅读✓ 已解决
3 回答4.9k 阅读✓ 已解决
1 回答3.2k 阅读✓ 已解决
3 回答2.3k 阅读✓ 已解决
听上去是一个 grid 布局。合并的操作不知道你想怎么合并,先写个分裂的吧。
https://jsrun.net/xBTKp/edit