Last active
October 27, 2020 07:26
-
-
Save way2ex/9736be451df029f7d4f28853a829d51c to your computer and use it in GitHub Desktop.
Creating grid layout config by list items with width and height
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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