還是有問題
This commit is contained in:
@@ -432,128 +432,116 @@
|
|||||||
// });
|
// });
|
||||||
//}
|
//}
|
||||||
let s = Number(d.fontSize);
|
let s = Number(d.fontSize);
|
||||||
let th = parseFloat(d.textHeight) || Number(h);
|
let th = Number(d.textHeight) || Number(d.height);
|
||||||
let v_gap = parseFloat(d.textSpan) || 0; // 確保強制轉型為數字,避免字串相加錯誤
|
let v_gap = Number(d.textSpan) || 0;
|
||||||
let h_gap = 5;
|
let h_gap = 5;
|
||||||
let base_s_px = parseFloat(d.fontSize) * 1.333;
|
|
||||||
|
|
||||||
// 1. 保留每個名字獨立的「專屬最大字體」,3個字跟5個字的短名大小會各自獨立計算
|
// 1. 設定初始字體 (不再主動放大,統一從預設大小開始測試)
|
||||||
|
let long_s = Number(d.fontSize);
|
||||||
|
let short_s = Number(d.fontSize);
|
||||||
|
let base_s_px = Number(d.fontSize) * 1.333;
|
||||||
|
|
||||||
|
// 2. 判斷誰是長名、誰是短名 (以預設大小判定,判斷後永遠不變)
|
||||||
let items = mid.map(name => {
|
let items = mid.map(name => {
|
||||||
let l = name.length;
|
let l = name.length;
|
||||||
// 依據原始設定的基準大小,判定是短名還長名
|
|
||||||
let isShort = (l * base_s_px) <= (th + v_gap);
|
let isShort = (l * base_s_px) <= (th + v_gap);
|
||||||
|
return { name: name, l: l, isShort: isShort, isMulti: false, size: 0 };
|
||||||
// 短名跟長名,各自依照容器 (th 或 h) 算出能放大的極限最佳字體
|
|
||||||
let optimal_s = Math.floor((isShort ? th : h) / (l * 1.333));
|
|
||||||
if (optimal_s > parseFloat(d.fontSize)) optimal_s = parseFloat(d.fontSize);
|
|
||||||
if (optimal_s < 6) optimal_s = 6;
|
|
||||||
|
|
||||||
return { name: name, l: l, isShort: isShort, optimal_s: optimal_s, size: optimal_s, isMulti: false };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let sizes = [];
|
let sizes = [];
|
||||||
let long_ratio = 1.0;
|
let shrink_turn = 'long'; // 設定交替縮小指標,優先從長名開始
|
||||||
let short_ratio = 1.0;
|
|
||||||
let shrink_turn = 'long'; // 優先從長名開始縮小
|
|
||||||
|
|
||||||
// 2. 動態排版預測與比例縮小迴圈
|
// 3. 預測排版與縮小迴圈
|
||||||
while (true) {
|
while (true) {
|
||||||
// 套用當前的縮小比例
|
let totalWidth = 0;
|
||||||
items.forEach(x => {
|
|
||||||
let s = Math.floor(x.optimal_s * (x.isShort ? short_ratio : long_ratio));
|
|
||||||
x.size = s < 6 ? 6 : s;
|
|
||||||
x.isMulti = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
let cols = []; // 用陣列精準記錄每一實體欄位所需的寬度
|
|
||||||
let current_col_h = 0;
|
let current_col_h = 0;
|
||||||
let current_col_w = 0;
|
let current_col_w = 0;
|
||||||
|
let cols_width = []; // 用來收集每一實體行的真實寬度
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
let item = items[i];
|
let item = items[i];
|
||||||
let size_px = item.size * 1.333;
|
|
||||||
|
// 賦予當前迴圈測試中的字體大小
|
||||||
|
let s = item.isShort ? short_s : long_s;
|
||||||
|
item.size = s;
|
||||||
|
let size_px = s * 1.333;
|
||||||
|
|
||||||
if (item.isShort) {
|
if (item.isShort) {
|
||||||
|
// 短名字計算 (高度至少為 th,若字數過多則取實際字體高度)
|
||||||
let item_h = Math.max(th, item.l * size_px);
|
let item_h = Math.max(th, item.l * size_px);
|
||||||
|
|
||||||
if (current_col_h === 0) {
|
if (current_col_h === 0) {
|
||||||
// 開啟新欄位
|
// 開新行
|
||||||
current_col_h = item_h;
|
current_col_h = item_h;
|
||||||
current_col_w = size_px;
|
current_col_w = size_px;
|
||||||
} else if (current_col_h + v_gap + item_h <= h) {
|
} else if ((current_col_h + v_gap + item_h) <= h) {
|
||||||
// 高度足夠,與上一個短名字堆疊在同一行
|
// 該行高度夠放,繼續疊在同一行
|
||||||
current_col_h += v_gap + item_h;
|
current_col_h += (v_gap + item_h);
|
||||||
if (size_px > current_col_w) current_col_w = size_px;
|
if (size_px > current_col_w) current_col_w = size_px; // 取該行最寬的字體寬度
|
||||||
} else {
|
} else {
|
||||||
// 高度不足,結算舊欄位,開啟新欄位
|
// 該行放不下,結算並折到下一行
|
||||||
cols.push(current_col_w);
|
cols_width.push(current_col_w);
|
||||||
current_col_h = item_h;
|
current_col_h = item_h;
|
||||||
current_col_w = size_px;
|
current_col_w = size_px;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 長名字必定獨佔或多佔欄位,先將前面累積的短名欄位結算封裝
|
// 長名字計算:遇到長名字,必須獨佔或多佔空間,先把前面短名字的行結算掉
|
||||||
if (current_col_h > 0) {
|
if (current_col_h > 0) {
|
||||||
cols.push(current_col_w);
|
cols_width.push(current_col_w);
|
||||||
current_col_h = 0;
|
current_col_h = 0;
|
||||||
current_col_w = 0;
|
current_col_w = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_w = size_px;
|
let item_w = size_px;
|
||||||
if (item.l * size_px > h) {
|
// 判斷長名字是否會超過總高度 h,需不需要折成多行
|
||||||
|
if ((item.l * size_px) > h) {
|
||||||
item.isMulti = true;
|
item.isMulti = true;
|
||||||
let chars_per_col = Math.floor(h / size_px);
|
let chars_per_col = Math.floor(h / size_px);
|
||||||
if (chars_per_col < 1) chars_per_col = 1;
|
if (chars_per_col < 1) chars_per_col = 1;
|
||||||
let item_cols = Math.ceil(item.l / chars_per_col);
|
let item_cols = Math.ceil(item.l / chars_per_col);
|
||||||
item_w = item_cols * (size_px * 1.1);
|
item_w = item_cols * (size_px * 1.1); // 計算折行後的總寬度
|
||||||
|
} else {
|
||||||
|
item.isMulti = false;
|
||||||
}
|
}
|
||||||
cols.push(item_w);
|
|
||||||
// 這裡 current_col_h 保持為 0,確保下一個短名字能順利開啟新欄位!
|
cols_width.push(item_w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 補上最後一個還沒結算的欄位
|
// 迴圈結束,補上最後一行還沒結算的寬度
|
||||||
if (current_col_h > 0) cols.push(current_col_w);
|
if (current_col_h > 0) cols_width.push(current_col_w);
|
||||||
|
|
||||||
// 計算總寬度:所有欄寬總和 + 欄間距
|
// 4. 結算總寬度 = 所有行寬加總 + 行與行之間的水平間距
|
||||||
let totalWidth = cols.reduce((a, b) => a + b, 0) + (cols.length > 1 ? (cols.length - 1) * h_gap : 0);
|
totalWidth = cols_width.reduce((a, b) => a + b, 0) + (cols_width.length > 1 ? (cols_width.length - 1) * h_gap : 0);
|
||||||
|
|
||||||
// 若總寬度未爆框,則排版完成
|
// 判斷是否完美塞進設定的總寬度 w 裡面
|
||||||
if (totalWidth <= w) {
|
if (totalWidth <= w) {
|
||||||
sizes = items.map(x => ({ name: x.name, size: x.size, isMulti: x.isMulti, isShort: x.isShort }));
|
sizes = items.map(x => ({ name: x.name, size: x.size, isMulti: x.isMulti, isShort: x.isShort }));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 若寬度爆框,啟動「輪流比例縮小」機制 (每次扣 5%)
|
// 5. 若超過寬度,啟動輪流縮小字體再算一次 (每次 -1)
|
||||||
let tryShrink = (type) => {
|
let hasLong = items.some(x => !x.isShort);
|
||||||
let didShrink = false;
|
let hasShort = items.some(x => x.isShort);
|
||||||
items.forEach(x => {
|
|
||||||
if ((type === 'long' ? !x.isShort : x.isShort) && x.size > 6) {
|
if (hasLong && hasShort) {
|
||||||
// 預測扣減 5% 後,字體是否真的有變小 (Math.floor 的關係)
|
// 長短名皆有,輪流互相縮小
|
||||||
let nextRatio = (type === 'long' ? long_ratio : short_ratio) - 0.05;
|
if (shrink_turn === 'long') {
|
||||||
let nextSize = Math.floor(x.optimal_s * nextRatio);
|
if (long_s > 6) { long_s--; shrink_turn = 'short'; }
|
||||||
if (nextSize < x.size) didShrink = true;
|
else if (short_s > 6) { short_s--; shrink_turn = 'long'; }
|
||||||
}
|
else break; // 都縮到底限,強制跳出
|
||||||
});
|
} else {
|
||||||
if (didShrink) {
|
if (short_s > 6) { short_s--; shrink_turn = 'long'; }
|
||||||
if (type === 'long') long_ratio -= 0.05;
|
else if (long_s > 6) { long_s--; shrink_turn = 'short'; }
|
||||||
else short_ratio -= 0.05;
|
else break;
|
||||||
}
|
}
|
||||||
return didShrink;
|
} else if (hasLong) {
|
||||||
};
|
// 只有長名字,一路縮長名字
|
||||||
|
if (long_s > 6) long_s--;
|
||||||
let reduced = false;
|
else break;
|
||||||
// 依據回合狀態,輪流要求長、短名字縮小
|
|
||||||
if (shrink_turn === 'long') {
|
|
||||||
if (tryShrink('long')) { reduced = true; shrink_turn = 'short'; }
|
|
||||||
else if (tryShrink('short')) { reduced = true; shrink_turn = 'long'; }
|
|
||||||
} else {
|
} else {
|
||||||
if (tryShrink('short')) { reduced = true; shrink_turn = 'long'; }
|
// 只有短名字,一路縮短名字
|
||||||
else if (tryShrink('long')) { reduced = true; shrink_turn = 'short'; }
|
if (short_s > 6) short_s--;
|
||||||
}
|
else break;
|
||||||
|
|
||||||
// 全部到達極限大小,強制退出迴圈防無窮迴圈
|
|
||||||
if (!reduced) {
|
|
||||||
sizes = items.map(x => ({ name: x.name, size: x.size, isMulti: x.isMulti, isShort: x.isShort }));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user