Skip to content

Instantly share code, notes, and snippets.

@way2ex
Last active October 27, 2020 07:26
Show Gist options
  • Save way2ex/9736be451df029f7d4f28853a829d51c to your computer and use it in GitHub Desktop.
Save way2ex/9736be451df029f7d4f28853a829d51c to your computer and use it in GitHub Desktop.
Creating grid layout config by list items with width and height
const CELL_STATE = {
EMPTY: 0,
EMPTY_LINE: 'o',
UNUSABLE: 'u',
};
import { TYPES } from './const';
function isEmpty(state) {
return [ CELL_STATE.EMPTY, CELL_STATE.EMPTY_LINE ].includes(state);
}
// function isEmptyLine(state) {
// return state === CELL_STATE.EMPTY_LINE;
// }
function isEmptyGrid(state) {
return state === CELL_STATE.EMPTY;
}
/**
* 矩阵代表网格,矩阵元素为单元格,index 从 1 开始, 所以矩阵具有初始行和初始列
*/
export default class GridMatrix {
constructor(columns) {
this.lastFullUsedRow = 0;
this.originColumns = columns;
this.columns = columns * 2 - 1;
this.matrix = [ undefined ];
this.addRow();
}
getLayoutData(list) {
const result = [];
for (let index = 0; index < list.length; index++) {
const { w = 1, h = 1, type, ...otherProps } = list[index];
if (type === TYPES.V_LINE) {
if (index === 0) {
throw new Error('[GridShow] 第一个元素不能为 垂直分割线.');
} else if (this.columns < 2) {
throw new Error('[GridShow] 列数不大于 1 时不能设置垂直分割线.');
}
const { endX: lastEndX, startY } = result[index - 1];
const endY = startY + h;
let startX;
let endX;
if (lastEndX >= this.columns) {
startX = 2;
endX = 3;
} else {
startX = lastEndX;
endX = lastEndX + 1;
}
result.push({
...otherProps,
column: `${startX}/${endX}`,
row: `${startY}/${endY}`,
startX, endX,
startY, endY,
});
this.occupyArea({ startX, startY, endX, endY });
continue;
}
const { startX, endX, startY, endY } = this.getPosition(w, h);
// console.log('当前处理结果:', startX, startY, endX, endY, index + 1);
this.occupyArea({ startX, startY, endX, endY, itemIndex: index + 1 });
this.updateFullUsedRow(startY, endY);
result.push({
...otherProps,
column: `${startX}/${endX}`,
row: `${startY}/${endY}`,
startX, endX,
startY, endY,
w, h
});
}
return result;
}
getGridTemplateStyle() {
return {
gridTemplateColumns: this.matrix[1]
.map((c, index) => { return index % 2 === 0 ? '10px' : '1fr'; })
.slice(1)
.join(' '),
};
}
/**
* 获取在网格中的位置
* @param {Number} width 宽度
* @param {Number} height 高度
* @return {Object} position
*/
getPosition(width, height) {
const startRow = this.getSearchStartRow();
// console.log('start', height);
if (startRow >= this.matrix.length) {
this.addRow();
}
for (let i = startRow; i < this.matrix.length; i++) {
const startX = this.getFirstValidX(i, width);
if (startX === null) {
// 此时需要设置一下占满的行
this.setLastFullUsedRow(i);
continue;
}
return {
startX,
endX: startX + width * 2 - 1,
startY: i,
endY: i + height,
};
}
return {
startX: 1,
endX: 1 + width * 2 - 1,
startY: this.matrix.length,
endY: this.matrix.length + height,
};
}
getSearchStartRow() {
for (let i = this.lastFullUsedRow + 1; i < this.matrix.length; i++) {
if (!this.isFullUsed(i)) {
this.setLastFullUsedRow(i - 1);
return i;
}
}
this.setLastFullUsedRow(this.matrix.length - 1);
return this.matrix.length;
}
/**
* 获取有效的空间位置
* @param {Number} rowNum 当前行
* @param {Number} space 所需要的空间
* @return {Number|null} 有效的位置或null
*/
getFirstValidX(rowNum, width) {
const row = this.matrix[rowNum];
let i; let j; let validStart;
for (i = 1; i < row.length; i++) {
if (!isEmptyGrid(row[i])) {
continue;
}
validStart = i;
let validSpace = 0;
for (j = validStart; j < row.length; j++) {
if (!isEmpty(row[j])) { break; }
if (isEmptyGrid(row[j])) {
validSpace++;
}
}
// console.log(`第${i}列寻找: `, validSpace);
if (validSpace >= width) {
return validStart;
}
this.occupyArea({ startX: validStart, startY: rowNum, endX: j, endY: rowNum + 1, itemIndex: CELL_STATE.UNUSABLE });
i = j;
}
return null;
}
occupyArea({ startX, startY, endX, endY, itemIndex = CELL_STATE.UNUSABLE }) {
// 这里调用者应确保 width 不会超出
for (let j = startY; j < endY; j++) {
if (!this.matrix[j]) {
this.addRow();
}
const row = this.matrix[j];
for (let i = startX; i < endX; i++) {
if (isEmptyGrid(row[i])) {
row[i] = itemIndex;
} else {
row[i] = CELL_STATE.UNUSABLE;
}
}
}
}
updateFullUsedRow(startY, endY) {
for (let i = startY; i < endY; i++) {
if (!this.isFullUsed(i)) {
// console.log(`第${i}行没有占满`, startY, endY);
return;
}
// console.log('整行占据:', i);
this.setLastFullUsedRow(i);
}
}
isFullUsed(rowNum) {
// console.log(`第${rowNum}行`, this.matrix[rowNum]);
return this.matrix[rowNum].slice(1).every(cell => !isEmptyGrid(cell));
}
setLastFullUsedRow(row) {
this.lastFullUsedRow = row;
}
addRow() {
const row = new Array(this.columns + 1).fill(0);
for (let i = 2; i < row.length; i += 2) {
row[i] = CELL_STATE.EMPTY_LINE;
}
this.matrix.push(row);
}
print() {
for (let i = 1; i < this.matrix.length; i++) {
// eslint-disable-next-line no-console
console.log(this.matrix[i].slice(1).join(' '));
}
}
getScheme() {
return this.matrix.slice(1).map(row => row.slice(1));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment