今天學習 layout 下的各個 js. 在此以前, 須要回顧一下 core/layout.
注意在 core 下面有 layout.js, module 下面也有 layout.js, 我是被混淆了, 不能換個名字麼? node
=== 佈局基類 Layout 位於 core/layout.js === 算法
佈局的基類, 各類不一樣的佈局今後類派生. class Layout { // 子類須要實現此佈局算法. 算法輸入是父節點, 及其子節點(數組), // 要求佈局這些子節點(即相對於父節點的變換) virtual doLayout(parent, children[]): throw 'sub-class impl' align(),stack(),move() 等輔助方法(子類可以使用), 稍後看. }
在理解佈局前, 須要先了解一點變換的知識, 以及 kity 中對變換的包裝(和實現). c#
變換這裏指將一個點(x1,y1) 經函數 f 映射到另外一個點 (x2,y2). 數組
對於平移(translate)有:
x2 = x1 + a, y2 = y1 + b
這裏 a,b 分別是 x,y 軸平移量. svg
在 svg 中至關於爲元素設置一個 transform="translate(a,b)" (或其它等價形式). 函數
對於縮放(scale)有:
x2 = a*x1, y2 = b*y1
在 svg 中至關於爲元素設置 transform="scale(a,b)" (或等價形式) 工具
對於旋轉(rotate)一個角度 α:
x2 = x1*cosα + y1*sinα
y2 = y1*cosα - x1*sinα
對應 svg 中至關於 transform="rotate(α)" 佈局
斜切變換 skewX(α), skewY() 類似:
x2 = x1 + y1*tanα, y2 = y1 學習
上述各類變換均可是線性變化, 即 x2,y2 是 a*x1+b*y1+c 的形式, 所以可表示爲線性代數的
矩陣乘法. 這樣變換就能夠寫做: 動畫
點 x,y 平移 tx,ty 距離: [1 0 tx] [x] [x+tx] [0 1 ty]*[y] = [y+ty] [0 0 1 ] [1] [ 1 ] 點 x,y 縮放 sx,sy 倍: [sx 0 0] [x] [x*sx] [0 sy 0]*[y] = [y*sy] [0 0 1] [1] [1] 點 x,y 繞原點旋轉 α 角的矩陣, 向量和乘的結果略: [cos -sin 0] [sin cos 0] [ 0 0 1] 斜切 skewX: skewY: [1 tan(ax) 0] [1 0 0] [0 1 0] [tan(ay) 1 0] [0 0 1] [0 0 1]
使用矩陣的最強大的地方在於, 多個線性變換能夠乘積, 而矩陣*矩陣仍是矩陣, 這便是 svg matrix 的含義了.
多個(線性)變換組合在一塊兒, 便是多個矩陣的乘積, 矩陣乘積仍然是矩陣, svg 中 transform="matrix(a,b,c,d,e,f)" 即表示矩陣: [a c e] [b d f] [0 0 1]
這樣, 你不管爲一個元素施加多少次線性變換, 其最終結果都是一個簡單的矩陣.
所以我合理地猜想這些變換,矩陣在 kity svg 庫中都有對應的封裝類/方法. 例如 kity.Matrix 類...
=== 下面研究 layout/mind.js, btree.js ===
以上咱們看到了 Layout (虛)基類, 如今咱們找一個實作的子類來查看 doLayout() 是具體怎麼實現的.
位於 src/layout 目錄下有 6 個 js 文件. 例如 tianpan.js(拼音天盤?), fish-bone*(魚骨圖) 等.
我簡單看了下, 其中 mind.js 對應缺省佈局, 其引用了 btree.js, 因此咱們先研究這兩個.
// 實際名字可稱爲 MindLayout 或 DefaultLayout class <noname-layout> : public Layout { doLayout(node, children): { // 這裏算法須要仔細看 // 1. 將 children 切分紅兩部分 left~right. 也可認爲是 up~down. var left[] = 前一半 children, right[] = 後一半 children; // 2. 獲得 left, right (子)佈局器 (layout) var left_layout = Minder.left_layout_object; // right_layout 也同樣, 因此只需研究明白 left 一半便可. // 3. 使用子佈局器 佈局各自一半. left_layout.doLayout(node, left[]) // right 一半相同 // 4. 合在一塊兒, 設置 node 的 vertex_out, vector_out box = node.content_box set out-vertex,out-vector // 估計和連線有關, 之後看. } }
這裏 left, right 子佈局器在 btree.js 中. 該文件其實生成了 dir=left,right,top,bottom 四個佈局器,
爲了簡化問題, 我實際代入該生成函數以 dir=left, 模擬出一個類, 以方便理解.
// 將一組子節點佈局到本身的左側, 子節點右邊界是對齊的(right, align), // 垂直方向(即沿着 y 軸)順序排列(stack). class LeftLayout : public Layout { doLayout(node, children[]): { // 1. axis = 'x', oppsite = 'right' // left 是 x 軸的, 相對方向爲 right. parent.out_vertex = ..., .out_vector = ... (略) // 2. for-each (child in children[]) { child.layout_transform = new kity.Matrix() // 等於重置了變換矩陣? child.in_vertex = ..., .in_vector = ... (暫時略) } // 3. 使用基類輔助方法. 對齊子節點, 堆疊成一列. base::align(children[], 'right') // right = oppsite of left base::stack(children[], 'y') // y = oppsite of x-axis // 4. 計算偏移各子節點的一個 dx,dy 值. p_box = parent.content_box // 父節點位置. c_box = getBranchBox() // 工具方法:獲取給點的子節點所佔的佈局區域 // 這裏仍是對 dx 的計算有點疑惑, 可能須要調試下... dx = ..., dy = ... // c_box 放 p_box 左邊+margin, y 值中心對齊. // 平移子節點到該偏移, 實際是調用 base::move() 方法. for-each (child) node.translate(dx, dy) } }
這裏重點是將一組子節點靠右對齊(即全部 child.right 值相同), 沿着 y 軸堆疊(順序排列, 中間有間距),
而後佈置在父節點的左側. 實際使用三個 Layout 基類的方法: align(), stack(), move().
下面特化 align() 爲 align_right(), 在上面的例子中調用的 align() 給出參數爲 'right':
(其它 align left,top,bottom 相似, 只是多一個 switch(dir) 判斷.
class Layout { align_right(nodes[]) { for-each (node in nodes[]) { // 經此平移, 全部 node 節點的 right 值變爲 0, 因此就右對齊了. node.transform_matrix.translate(-node.right, 0) } } }
下面特化 stack() 爲 stack_y():
class Layout { stack_y(nodes) { distance = node.margin-bottom // 節點間距離 position = 0 // 節點在 y 軸上的位置. for-each (node) { // 對 node 的當前變換矩陣 再疊加一個 dy 平移, 使得其 y 軸定位到 position 位置. matrix = node.transform_matrix matrix.translate(0, position - node.top) // 計算下一個位置. 考慮兩個節點之間的間距. position += node.height + distance_of_curr_node_and_next_node } } }
一組 nodes[] 在 align_right() 以後, node.right = 0; 在 stack_y() 以後 nodes[0].top = 0;
這至關於整組節點的右上角是 (0,0) 原點了. 上面計算偏移 dx,dy 的疑惑也略微解開了一些, 由於那裏已經
知道整個 children_box .top=0, .right=0 了.
如今咱們知道了 Layout 的做用是計算子節點的佈局位置, 該位置是相對於父節點的. 對於其它佈局形式,
如魚骨圖, 天盤?圖估計做用相似, 只是計算方法有所不一樣, 佈局的子節點的位置也就有些不一樣.
這裏 Layout 只是算出了位置, 但並未將節點實際移動到那裏, 因而問題就產生了: 誰,何時,怎麼移動(動畫?)節點到新的位置的呢?