文件
字型
Text 預設使用系統字體(iOS 為 San Francisco,Android 為 Roboto),除非字體系列另有指定(Rust 中用 .font_family(..),TSX 中用 fontFamily)。字體系列可以是通用字體系列、裝置上已安裝的字體,或隨應用程式一併發佈的字體檔案。
#系統字體與通用字體系列
fontFamily |
字體 |
|---|---|
(無)、"system-ui"、"sans-serif"、"ui-sans-serif"、"-apple-system" |
系統字體 |
"serif"、"ui-serif" |
系統襯線字體(iOS 為 New York,Android 為 Noto Serif) |
"monospace"、"ui-monospace" |
系統等寬字體(iOS 為 SF Mono) |
"ui-rounded" |
系統圓體(SF Pro Rounded),僅限 iOS |
系統字體支援 100 至 900 的所有字重。Tailwind 的 font-sans、font-serif 和 font-mono 對應前三列,而 font-sans 和 font-mono 則在主題設定了字體時對應主題的字體(為應用程式設定預設字體)。
裝置上已安裝的字體系列可直接以名稱指定(例如 "Georgia"、iOS 上的 "Avenir Next")。不同平台和版本所安裝的字體各有不同,因此設計所依賴的字體應隨應用程式發佈。在 iOS 上,若指定的字體系列不存在,會退回使用系統字體,並在日誌中記錄警告(font family 'X' is not available);可以指定一個清單來決定改用哪些字體(後備清單)。
#發佈字體
Rust crate 會發佈其 assets/ 目錄中的檔案:在 #[native_module] 函數中使用 registry.assets(assets!()) 會發佈全部檔案,而 asset!("fonts/Pacifico-Regular.ttf") 是一個 FontAsset,其 family 是字體的系列名稱,在 crate 編譯時從檔案中讀取。在 TSX 中,匯入 .ttf、.otf、.woff 或 .woff2 檔案:匯入的結果即為字體的系列名稱。無論哪種方式,字體都會在第一個畫面幀之前完成註冊,因此文字首次渲染時字體已準備就緒。WOFF 和 WOFF2 檔案是經壓縮的 TrueType 或 OpenType 字體(WOFF2 檔案的大小往往只有對應 TrueType 的一半,可減少應用程式套件的體積及網頁的下載量):NSH 會在所有平台上將它們當作其所包含的字體來讀取:
use nsh_core::{element::FontAsset, prelude::*};
const PACIFICO: FontAsset = asset!("fonts/Pacifico-Regular.ttf");
#[native_module("app/native")]
fn native(registry: &mut Registry) {
registry.assets(assets!());
}
text("Custom fonts").font_family(PACIFICO.family).text_size(px(30.0))import { Text } from "nsh";
import Pacifico from "./fonts/Pacifico-Regular.ttf";
<Text style={{ fontFamily: Pacifico, fontSize: 30 }}>Custom fonts</Text>PACIFICO.family 和 TSX 匯入的 Pacifico 都是字串 "Pacifico":即字體檔案中儲存的系列名稱(優先使用其 typographic family name,否則使用 family name),而不是檔案名稱。使用資源提供的值而非手動輸入名稱,可確保兩者保持一致;檔案缺失或損毀會令 Rust 建置失敗,而被匯入的檔案必定會被打包。
這些檔案與其他資源一樣隨應用程式發佈。字體只會在應用程式的行程中註冊,不會安裝到裝置上。
#後備清單
fontFamily 接受與 CSS 相同的清單格式:以逗號分隔多個字體系列,最優先的排在最前,含空格的名稱可加引號或不加("Brand, 'Noto Sans SC', sans-serif"),加了引號的名稱可保留其中的逗號('ACME, Inc')。文字會以平台所擁有的第一個字體系列排版;如果該字體缺少某個字元(其他文字系統或符號),則由清單中下一個擁有該字元的字體系列提供,之後才輪到系統的後備字體。透過清單,只涵蓋拉丁字母的品牌字體可以搭配相稱的字體來顯示中文,也可以在清單中列出應用程式下載的字體,並附上系統字體,在下載完成前先行顯示:
text("Brand 品牌").font_family("Brand, 'Noto Sans SC', sans-serif")<Text style={{ fontFamily: "Brand, 'Noto Sans SC', sans-serif" }}>Brand 品牌</Text>各平台以自身的字體機制保留清單的順序:iOS 和 macOS 使用 cascade list,Windows 使用 font fallback(每個段落一個:取其第一個樣式的清單),Linux 使用 Pango 字體系列清單,網頁使用瀏覽器自身的機制,GPUI 則使用其後備字體。在 Android 上,文字以其擁有的第一個字體系列排版;如果該字體是應用程式註冊的字體,清單中排在其後的已註冊字體即為其後備字體(Android 10 及以上)。
#字重與斜體
一個字體系列通常由多個檔案組成,每個字重和樣式各一個。請將每個檔案都發佈出去(Rust 中放入 assets/,TSX 中逐一匯入);它們會以相同的系列名稱註冊,並由字重和樣式來選擇字體樣式:
// assets/fonts: Inter-Regular.ttf, Inter-Medium.ttf, Inter-Bold.ttf, Inter-Italic.ttf
const INTER: FontAsset = asset!("fonts/Inter-Regular.ttf");
text("Regular, ")
.font_family(INTER.family)
.text_size(px(16.0))
.child(text("medium").font_weight(FontWeight::MEDIUM))
.child(", ")
.child(text("bold").font_weight(FontWeight::BOLD))
.child(" and ")
.child(text("italic").italic())
.child(".")import { Text } from "nsh";
import Inter from "./fonts/Inter-Regular.ttf";
import "./fonts/Inter-Medium.ttf";
import "./fonts/Inter-Bold.ttf";
import "./fonts/Inter-Italic.ttf";
const body = { fontFamily: Inter, fontSize: 16 } as const;
<Text style={body}>
Regular, <Text style={{ fontWeight: "500" }}>medium</Text>, <Text style={{ fontWeight: "bold" }}>bold</Text> and{" "}
<Text style={{ fontStyle: "italic" }}>italic</Text>.
</Text>當沒有任何已註冊的字體樣式完全符合所要求的字重時,會使用最接近的一個。字體樣式缺失時,各平台的處理方式不同:
| iOS | Android | |
|---|---|---|
| 沒有對應字體樣式的字重 | 最接近的已註冊字體樣式 | 最接近的字體樣式,並在適用時使用 Android 的合成粗體 |
| 沒有斜體字體樣式的斜體 | 正體字體樣式(不會傾斜) | 將正體字體樣式傾斜後的版本 |
請發佈你所使用的所有字體樣式,以便在兩個平台上得到相同的效果。
#類型化樣式與 Tailwind class 中的字體
在 Rust 中,.font_family(..) 可在任何放置樣式的位置使用資源的字體系列,而字重方法(.font_weight(FontWeight::SEMIBOLD))則用來選擇字體樣式。至於 class,請在 nsh.json 的 Tailwind 主題中為你的字體系列命名,以便作為 font-* class 使用:
{
"tailwind": {
"fontFamily": { "display": "Pacifico", "body": "Inter" }
}
}
use nsh_core::element::palette::VIOLET;
text("Custom fonts").font_family(PACIFICO.family).text_3xl().text_color(VIOLET.shade(600))
text("Bold Inter").font_family(INTER.family).font_weight(FontWeight::BOLD)import { Text } from "nsh";
import "./fonts/Pacifico-Regular.ttf";
import "./fonts/Inter-Regular.ttf";
import "./fonts/Inter-Bold.ttf";
<Text class="font-display text-3xl text-violet-600">Custom fonts</Text>
<Text class="font-body font-bold">Bold Inter</Text>任意的字體系列無需在主題中設定即可使用:font-[Pacifico]。字重 class(font-light、font-semibold、font-bold 等)選擇字體樣式的方式與 fontWeight 相同。View 上的文字樣式會向下傳遞給其內的 Text,因此在 View 上使用 .font_family(INTER.family),或使用 <View class="font-body">,即可為整個畫面設定字體系列。
#為應用程式設定預設字體
主題的 font-sans 即為應用程式的字體:每個沒有自行設定字體系列的 Text 都會以它繪製,Rust 元件和 TSX 皆然,就如同網頁使用 Tailwind 的 --font-sans 一樣。font-mono 是等寬字體,用於 font-mono class 以及 TextView 中的程式碼。請在應用程式的主題中設定這些字體(nsh.json 中的 "theme",Rust),並按上述方式發佈字體檔案:
{
"cssVars": {
"theme": {
"font-sans": "Inter, sans-serif",
"font-mono": "JetBrains Mono, monospace"
}
}
}
清單中第一個非通用的字體系列即為所用字體;如果第一個字體系列是通用字體系列(system-ui、sans-serif),則使用系統字體。也可以在容器上設定字體:其內的 Text 會繼承該字體。
#[component]
pub fn Root() -> impl IntoElement {
view().flex_1().font_family(INTER.family).child(app())
}mount(() => (
<View class="flex-1 font-body">
<App />
</View>
));#字體功能
fontVariant 會啟用字體的 OpenType 功能:"tabular-nums"、"proportional-nums"、"lining-nums"、"oldstyle-nums" 和 "small-caps"(Rust 中為 .tabular_nums()、.small_caps() 等;Text)。字體本身必須包含這些功能;系統字體都包含。