
大家好,我是前端西瓜哥。這次來講解圖形編輯器排列(arrange)功能的實現(xiàn)。
先看效果。

有四種移動方式:
- 置頂(Front):將指定的圖形移動到頂部。
- 置底(Back):將制定圖形移動到底部。
- 上移一層(Forward):將指定元素往上移一層。
- 下移一層(Backward):將置頂元素往下移動一層。
需要注意保持被移動圖形,要保持它們原來的相對順序。
編輯器 github 地址:
https://github.com/F-star/suika
線上體驗:
https://blog.fstars.wang/app/suika/
Front 置頂
置頂,是將圖形放在最頂部的位置。
假設(shè)圖形樹對應的一個數(shù)組 graphs,需要被移動的元素集合為 movedGraphSet(Set 類型)。要做的是將這些元素移動到數(shù)組末尾。
圖形是按照數(shù)組的順序繪制的,后面繪制的會蓋住前面的圖形,所以是移動到末尾而不是開頭。這點需要注意。
我們只需要遞歸 graphs,不在 movedGraphSet 中的元素搬到新的數(shù)組中,在 movedGraphSet 中的元素,放到 tailGraphs 數(shù)組中。
const front = (graphs: Graph[], movedGraphSet: Set<Graph>) => {
const newGraphs: Graph[] = [];
const tailGraphs: Graph[] = [];
for (let i = 0; i < graphs.length; i++) {
const graph = graphs[i];
if (movedGraphSet.has(graph)) {
tailGraphs.push(graph);
} else {
newGraphs.push(graph);
}
}
newGraphs.push(...tailGraphs);
return newGraphs;
};
圖形樹可能會是鏈表,或者有 group 的概念,實現(xiàn)代碼或許不同,但思路是一樣的。
Back 置底
置底同理。
這次是從右往左遍歷。另外因為要減少數(shù)組搬移的操作,我們需要額外將數(shù)組做一個倒序。
往數(shù)組的頭部插入新元素,是要將原來的整個數(shù)組往后移動一格的,時間復雜度是 O(n),往末尾加則不需要。
const back = (graphs: Graph[], movedGraphSet: Set<Graph>) => {
const newGraphs: Graph[] = [];
const tailGraphs: Graph[] = [];
for (let i = graphs.length - 1; i >= 0; i--) {
const graph = graphs[i];
if (movedGraphSet.has(graph)) {
tailGraphs.push(graph);
} else {
newGraphs.push(graph);
}
}
newGraphs.push(...tailGraphs);
return newGraphs.reverse(); // 反向
};
Forward 上移一層
將存在于 movedGraphSet 的圖形都往后移動一個位置。需要注意多個需要移動的圖形如果緊鄰,是要將它們作為一個整體放到它們之后的第一個不移動圖形的后面的。
比如被操作數(shù)組為 [0, 1, 2, 3, 4, 5, 6, 7],指定數(shù)組元素為 [1, 2, 6],返回 [0, 3, 1, 2, 4, 5, 7, 6]。
一開始我想的從左往右遍歷,用多個指針記錄連續(xù)需要移動的圖形的,然后發(fā)現(xiàn)實現(xiàn)上也太復雜了吧。要維護指針,還要判斷指針什么時候應該移動什么的。
后面我換了個思路,改為從右往左遍歷。如果當前元素是需搬移元素,就和下一個元素交換。
這樣,一個不用搬移的元素就能往前擠過被搬運元素的集群。
const forward = (graphs: Graph[], movedGraphs: Set<Graph>) => {
const newGraphs = [...graphs];
for (let i = newGraphs.length - 2; i >= 0; i--) {
if (movedGraphs.has(newGraphs[i])) {
// 交換
[newGraphs[i], newGraphs[i + 1]] = [newGraphs[i + 1], newGraphs[i]];
}
}
return newGraphs;
};
Backward 下移一層
同理。
換個方向。
const backward = (graphs: Graph[], movedGraphs: Set<Graph>) => {
const newGraphs = [...graphs];
for (let i = 1; i < newGraphs.length; i++) {
if (movedGraphs.has(newGraphs[i])) {
[newGraphs[i], newGraphs[i - 1]] = [newGraphs[i - 1], newGraphs[i]];
}
}
return newGraphs;
};