UI Toolkit使用一個開源的Layout Engine: Yoga,Yoga是一個實作了Flexbox layout的HTML/CSS Layout system。
預設所有Visual Elements都是layout的一部份,在Layout中預設有以下行為
- Container會將它的Children做垂直(vertically)分佈
- Container矩形(rectangle)的位置包括其children rectangles
- 在計算尺寸(size calculation)時,若這個visual element有Text的話,會以這個Text的大小來計算
Flexbox基本
Flex Item
:在Flexbox中會有一個含有多個元素的Container稱為Flex Container
,在這個Flex Container中的element稱為Flex Item
Flexbox的layout使用
flex-flow directions
的方式來做佈局,也就是說裡面的items會根據一個軸來從開頭到結尾做排列,如main axis
的話從main-start
到main-end
排列;或cross axis
的話從cross-start
到cross-end
排列。Main axis
:由main-start
到main-end
的方向。- 預設是由左到右的方向與大部分語言文字方向相同的方向
- 注意:
Main axis
不一定是由左到右的方向,它會根據flex-direction
改變。
Cross axis
:由cross-start
到cross-end
的方向,也就是換行方向,如果Flex Item單行的數量多到超過Flex Container,需要換行時,預設會換到下一行的方向。- 預設是由上到下
Flex direction
:設定main axis
的方向,有以下方向可以設置row
(預設): 在LRT佈局中是從左到右;在RTL佈局中是從右到左row-reverse
: 與row
相反,在LRT佈局中是從右到左;在RTL佈局中是從左到右column
: 方向是從上到下column-reverse
: 與column
相反,從下到上- 以下是flex-direction為
row
的範例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<style>
.container {
display: flex;
flex-direction: row;
background-color: yellow;
}
.item{
margin: 10px;
width: 60px;
height: 60px;
background-color: blue;
color: white;
}
</style>
<div class="container1">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
</div>item1item2item3
flex-wrap:設定Flex是否換行,預設會讓flex items保持在一行。
nowrap
:所有flex items保持在一行wrap
:當flex items多到容不下時,會換到下一行,方向是從上到下wrap-reverse
:wrap
的相反,當flex items多到容不下時,會換到上一行,方向是從下到上- 以下是flex-wrap的的範例
nowrap
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container2-1 {
display: flex;
flex-direction: row;
background-color: yellow;
width:200px;
flex-wrap: nowrap;
}
</style>
<div class="container2-1">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
wrap
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container2-2 {
display: flex;
flex-direction: row;
background-color: yellow;
width:200px;
flex-wrap: wrap;
}
</style>
<div class="container2-2">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
wrap-reverse
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container2-3 {
display: flex;
flex-direction: row;
background-color: yellow;
width:200px;
flex-wrap: wrap-reverse;
}
</style>
<div class="container2-3">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
justify-content
:定義Flex Items在Flex Container在main axis
上空間分配的方式flex-start
:Items以flex-direction
的開始爲方向排列。flex-end
:Items以flex-direction
的尾部為方向排列。center
:Items在中心排列space-between
:Items在main axis
上平均排列,但首尾Items貼邊。space-around
:Items在main axis
上平均排列,Items之間使用相同的空間- 以下是Justify Content的例子
flex-start
1
2
3
4
5
6
7
8
9
10
11
12
13<style>
.container3-1 {
display: flex;
flex-direction: row;
background-color: yellow;
justify-content: flex-start;
}
</style>
<div class="container3-1">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
flex-end
1
2
3
4
5
6
7
8
9
10
11
12
13<style>
.container3-2 {
display: flex;
flex-direction: row;
background-color: yellow;
justify-content: flex-end;
}
</style>
<div class="container3-2">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
center
1
2
3
4
5
6
7
8
9
10
11.container3-3 {
display: flex;
flex-direction: row;
background-color: yellow;
justify-content: center;
}
<div class="container3-3">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
space-between
1
2
3
4
5
6
7
8
9
10
11.container3-4 {
display: flex;
flex-direction: row;
background-color: yellow;
justify-content: space-between;
}
<div class="container3-4">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
space-around
1
2
3
4
5
6
7
8
9
10
11
12
13<style>
.container3-5 {
display: flex;
flex-direction: row;
background-color: yellow;
justify-content: space-around;
}
</style>
<div class="container3-5">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
align-items:設定Flex Items對
cross axis
方向的排列方式- flex-start:Items會從cross axis的開始位置對齊
- flex-end:Items會從cross axis的尾端位置對齊
- stretch:當Items沒有設定高度時,Items會被拉伸填滿Container,(遵守最小寬度(min-width)/最大寬度(max-width))
- center:Items會從cross axis的中心位置對齊
- 以下是align-items的例子
flex-start
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container4-1 {
display: flex;
flex-direction: row;
background-color: yellow;
height: 200px;
align-items: flex-start;
}
</style>
<div class="container4-1">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
flex-end
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container4-2 {
display: flex;
flex-direction: row;
background-color: yellow;
height: 200px;
align-items: flex-end;
}
</style>
<div class="container4-2">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>123
stretch
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container4-3 {
display: flex;
flex-direction: row;
background-color: yellow;
height: 200px;
align-items: stretch;
}
</style>
<div class="container4-3">
<div style="background-color:coral;min-height:30px;width:60px;">1</div>
<div style="background-color:lightblue;min-height:50px;width:60px;">2</div>
<div style="background-color:lightgreen;min-height:190px;width:60px;">3</div>
</div>123
center
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container4-4 {
display: flex;
flex-direction: row;
background-color: yellow;
height: 200px;
align-items: center;
}
<div class="container4-4">
<div class="item red">1</div>
<div class="item green">2</div>
<div class="item blue">3</div>
</div>
</style>123
align-self:與align-items類似,但是只會應用到該Item上
- flex-start:Item會從cross axis的開始位置對齊
- flex-end:Item會從cross axis的尾端位置對齊
- stretch:Item會被拉伸填滿Container,(遵守最小寬度(min-width)/最大寬度(min-width))
- center:Item會從cross axis的中心位置對齊
flex-grow:設定item在容器中佔多少空間,或是分到多少剩餘空間。
- 以下舉例,容器的width為600px,紅色Item1的width為60px,綠色Item2的width為90px,藍色Item3的width為60px:
沒有設定 flex-grow
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container5 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container5">
<div class="red" style="width:60px;">1(60)</div>
<div class="green" style="width:90px;">2(90)</div>
<div class="blue" style="width:60px;">3(60)</div>
</div>1(60)2(90)3(60)
紅色Item的flex-grow:1,
剩餘的空間為600-60-90-60=390,這會把剩餘的空間(390)全部分紅色Item1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container5 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container5">
<div class="red item-a-5-1" style="width:60px;">1(60px+390px)</div>
<div class="green" style="width:90px;">2(90px)</div>
<div class="blue" style="width:60px;">3(60px)</div>
</div>1(60px+390px)2(90px)3(60px)
紅色Item的flex-grow:1,
綠色Item的flex-grow:2,
他們兩個會按比例分配剩餘空間390/3=130,紅色Item分配到130*1,綠色Item分到130*21
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container5 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container5">
<div class="red item-a-5-1" style="width:60px;">1(60+130*1)</div>
<div class="green item-a-5-2" style="width:90px;">2(90+130*2)</div>
<div class="blue" style="width:60px;">3(60)</div>
</div>1(60+130*1)2(90+130*2)3(60)
flex-grow的數值也可以是小數,
紅色Item的flex-grow:0.4,
綠色Item的flex-grow:0.6,
他們兩個按比例分配剩餘空間,紅色Item分配到390*0.4,綠色Item分到390*0.61
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container5 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container5">
<div class="red" style="width:60px; flex-grow:0.4;">1(60+390*0.4)</div>
<div class="green" style="width:90px; flex-grow:0.6;">2(90+390*0.6)</div>
<div class="blue" style="width:60px;">3(60)</div>
</div>1(60+390*0.4)2(90+390*0.6)3(60)
- 以下舉例,容器的width為600px,紅色Item1的width為60px,綠色Item2的width為90px,藍色Item3的width為60px:
flex-shrink:在容器空間不足時,控制Item如何收縮
- 此方式計算較複雜,除了flex-shrink設定比例,還需要根據容器中Item超出多少寬度,以及Item它本身的寬度來計算出各個Item收縮的比例。
- 每個Item收縮的權重為其
flex-shrink
*寬度
。 - 如果不希望被自動壓縮到,可以設定為0
- 以下舉例,容器的width為600px,紅色Item1的width為300px,綠色Item2的width為300px,藍色Item3的width為300px,紅色Item的flex-shrink:1,綠色Item的flex-shrink:1,藍色Item的flex-shrink:2
計算超出多少寬度
:600(容器) - 300(紅色) - 300(綠色) - 300(藍色) = 300 (超出300)
每個Item收縮的權重為其flex-shrink * 寬度
:1*300 + 1*300 + 2*300 = 1200
每個Item要收縮的寬度為:
紅色:300(超出的寬度) * 1 (flex-shrink) * 300(Item的寬度) / 1200(權重) = 75
綠色:300(超出的寬度) * 1 (flex-shrink) * 300(Item的寬度) / 1200(權重) = 75
藍色:300(超出的寬度) * 2 (flex-shrink) * 300(Item的寬度) / 1200(權重) = 150
因此三個元素最終的寬度為:
紅色:300 - 75 = 225
綠色:300 - 75 = 225
藍色:300 - 150 = 150
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container6 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container6">
<div class="red" style="width:300px; flex-shrink:1;">225</div>
<div class="green" style="width:300px; flex-shrink:1;">225</div>
<div class="blue" style="width:300px; flex-shrink:2;">150</div>
</div>225225150 - 再看另外一個例子:容器的width為600px,紅色Item的width為150px,綠色Item的width為300px,藍色Item的width為300px,紅色Item的flex-shrink:0,綠色Item的flex-shrink:2,藍色Item的flex-shrink:3
計算超出多少寬度
:600(容器) - 150(紅色) - 300(綠色) - 300(藍色) = 150 (超出150)
每個Item收縮的權重為其flex-shrink * 寬度
:0*150 + 2*300 + 3*300 = 1500
每個Item要收縮的寬度為:
紅色:150(超出的寬度) * 0 (flex-shrink) * 150(Item的寬度) / 1500(權重) = 0
綠色:150(超出的寬度) * 2 (flex-shrink) * 300(Item的寬度) / 1500(權重) = 60
藍色:150(超出的寬度) * 3 (flex-shrink) * 300(Item的寬度) / 1500(權重) = 90
因此三個元素最終的寬度為:
紅色:150 - 0 = 150
綠色:300 - 60 = 240
藍色:300 - 90 = 210
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container6 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container6 width:600px">
<div class="red" style="width:150px; flex-shrink:0;">150</div>
<div class="green" style="width:300px; flex-shrink:2;">240</div>
<div class="blue" style="width:300px; flex-shrink:3;">210</div>
</div>150240210 - flex-shrink也可以是小數:容器的width為600px,紅色Item的width為300px,綠色Item的width為300px,藍色Item的width為300px,紅色Item的flex-shrink:0.1,綠色Item的flex-shrink:0.4,藍色Item的flex-shrink:0.5
計算超出多少寬度
:600(容器) - 300(紅色) - 300(綠色) - 300(藍色) = 300 (超出150)
每個Item收縮的權重為其flex-shrink * 寬度
:0.1*300 + 0.4*300 + 0.5*300 = 300
每個Item要收縮的寬度為:
紅色:300(超出的寬度) * 0.1 (flex-shrink) * 300(Item的寬度) / 300(權重) = 30
綠色:300(超出的寬度) * 0.4 (flex-shrink) * 300(Item的寬度) / 300(權重) = 120
藍色:300(超出的寬度) * 0.5 (flex-shrink) * 300(Item的寬度) / 300(權重) = 150
因此三個元素最終的寬度為:
紅色:300 - 30 = 270
綠色:300 - 120 = 180
藍色:300 - 150 = 150
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container6 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container6 width:600px">
<div class="red" style="width:300px; flex-shrink:0.1;">270</div>
<div class="green" style="width:300px; flex-shrink:0.4;">180</div>
<div class="blue" style="width:300px; flex-shrink:0.5;">150</div>
</div>270180150
flex-basis
:設定Item佔用空間。如果沒設定或設為auto則Item的值為width(flex-direction:row
)或是height(flex-direction:column
)的值。- 以下是範例:
紅色Item的style="width:300px; flex-basis:auto;"。
綠色Item的style="width:300px; flex-basis:100px;"。
藍色Item的style="width:300px;"。
1
2
3
4
5
6
7
8
9
10
11
12
13
14<style>
.container6 {
display: flex;
flex-direction: row;
background-color: yellow;
width: 600px;
color: white;
}
</style>
<div class="container6 width:600px">
<div class="red" style="width:300px; flex-basis:auto;">300</div>
<div class="green" style="width:300px; flex-basis:100px;">100</div>
<div class="blue" style="width:300px;">300</div>
</div>300100300
- 以下是範例:
Reference: https://docs.unity3d.com/2020.1/Documentation/Manual/UIE-LayoutEngine.html
https://discussions.unity.com/t/ui-toolkit-introduction-and-flexbox-layout/316856
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
https://github.com/xieranmaya/blog/issues/9