JavaScript矩形打包算法如何实现flex justify-content:space-between两端对齐
更优实现方案(基于业务约束定制,无需二次遍历修正)
你的场景有三个强约束,完全不需要用通用二叉树装箱算法的逻辑,直接在行划分阶段就计算对齐参数,一次布局即可完成:
核心思路
利用所有矩形高度一致、宽度为固定列宽整数倍的特性,采用行优先布局逻辑,直接在分配坐标时带入两端对齐的偏移计算:
- 遍历所有待打包矩形时先按行聚合:累加当前行矩形总宽度,直到加入下一个矩形会超出画布宽度,即完成当前行的划分
- 计算当前行两端对齐所需的均匀间距:
行总空隙 = 画布宽度 - (当前行所有矩形宽度和 + 预设gutter * (当前行矩形数量 - 1)),单段额外间距为行总空隙 / (当前行矩形数量 - 1) - 直接按计算出的总间距分配当前行每个矩形的x、y坐标,y坐标固定为
行序号 * (矩形高度 + gutter),x坐标按前一个矩形的x+宽度+gutter+单段额外间距累加即可 - 继续处理下一行,整个过程仅需一次遍历完成所有矩形坐标分配,效率远高于通用装箱算法
沿用现有Packer类的最小改动方案
如果不想换掉现有逻辑,也可以通过修改splitNode的右节点分配规则实现,不需要后续二次遍历:
因为所有矩形高度一致,每行的y坐标是固定值,每次检测到换行(即findNode命中了down节点)时,计算上一行的总空隙,直接修改上一行所有矩形的x坐标即可,改动量极小。
优化后适配两端对齐的Packer代码示例
export interface Block { w: number; h: number; fit?: Node; } export interface Node { x: number; y: number; w: number; h: number; used?: boolean; down?: Node; right?: Node; } export class JustifiedPacker { readonly w: number; readonly h: number; readonly root: Node; readonly gutter: number; // 存储当前行的所有块 #currentLineBlocks: Block[] = []; // 当前行累计宽度 #currentLineWidth = 0; // 当前行y坐标 #currentY = 0; // 固定块高(约束所有块高度一致) readonly #blockHeight: number; constructor(w: number, h: number, blockHeight: number, gutter?: number) { this.w = w; this.h = h; this.gutter = gutter ?? 5; this.#blockHeight = blockHeight + this.gutter; this.root = { x: 0, y: 0, w: w, h: h, used: false }; } fit(blocks: Block[]): void { for (const block of blocks) { const rawW = block.w + this.gutter; // 判断加入当前行是否超限 if (this.#currentLineWidth + rawW > this.w && this.#currentLineBlocks.length > 0) { // 计算当前行的对齐偏移 this.#adjustCurrentLine(); // 换行重置 this.#currentLineBlocks = []; this.#currentLineWidth = 0; this.#currentY += this.#blockHeight; } // 加入当前行 this.#currentLineBlocks.push(block); this.#currentLineWidth += rawW; // 临时赋值坐标 block.fit = { x: this.#currentLineWidth - rawW, y: this.#currentY, w: rawW, h: this.#blockHeight, used: true } } // 处理最后一行 if (this.#currentLineBlocks.length > 0) { this.#adjustCurrentLine(); } } // 计算当前行两端对齐的偏移,更新所有块的x坐标 #adjustCurrentLine(): void { const blockCount = this.#currentLineBlocks.length; if (blockCount <= 1) return; // 单个块无需调整 const totalGap = this.w - this.#currentLineWidth; const perGap = totalGap / (blockCount - 1); let offsetX = 0; for (let i = 0; i < blockCount; i++) { const block = this.#currentLineBlocks[i]; if (block.fit) { block.fit.x = offsetX; offsetX += block.fit.w + perGap; } } } } export default JustifiedPacker;
内容的提问来源于stack exchange,提问作者ANimator120
相关产品推荐
相关产品推荐

