在以前的 《Flutter 上默認的文本和字體知識點》 和 《帶你深入理解 Flutter 中的字體“冷”知識》 中,已經介紹了很多 Flutter 上關于字體有趣的知識點,而本篇講繼續介紹 Flutter 上關于 Text
的一個屬性:FontFeature
, 事實上相較于 Flutter ,本篇內容可能和前端或者設計關系更密切。
相信本篇絕對是你能看到關于 Flutter
FontFeature
相關的少數資料之一。
什么是 FontFeature
? 簡單來說就是影響字體形狀的一個屬性 ,在前端的對應領域里應該是 font-feature-settings
,它有別于 FontFamily
,是用于指定字體內字的形狀的一個參數。
如下圖所示是
frac
分數和tnum
表格數字的對比渲染效果,這種效果可以在不增加字體庫時實現特殊的渲染,另外Feature
也有特征的意思,所以也可以理解為字體特征。
我們知道 Flutter 默認在 Android 上使用的是 Roboto
字體,而在 iOS 上使用的是 SF
字體,但是其實 Roboto
字體也是分很多類型的,比如你去查閱手機的 system/fonts
目錄,就會發現很多帶有 Roboto
字樣的字體庫存在。
所以 Roboto
之類的字體庫是一個很大的字體集,不同的 font-weight
其實對應著不同的 ttf
,例如默認情況下的 Roboto
是不支持 font-weight
為 600 的配置:
所以如下圖所示,如果我們設置了 w400
- w700
的 weight
,可以很明顯看到中間的 500 和 600 其實是一樣的粗細,所以在設置 weight
或者設計 UI 時,就需要考慮不同平臺上的 weight
是否支持想要的效果。
回歸到 FontFeature
上,那 Roboto
自己默認支持多少種 features 呢? 答案是 26 種,它們的編碼如下所示,運行后效果也如下圖所示,從日常使用上看,這 26 種 Feature 基本滿足開發的大部分需求。
"c2sc"、 "ccmp"、 "dlig"、 "dnom"、 "frac"、 "liga"、 "lnum"、 "locl"、 "numr"、 "onum"、 "pnum"、 "salt"、 "smcp"、 "ss01"、 "ss02"、 "ss03"、 "ss04"、 "ss05"、 "ss06"、 "ss07"、 "tnum"、 "unic"、 "cpsp"、 "kern"、 "mark"、 "mkmk"
而 iOS 上的 SF pro
默認支持 39 種 Features , 它們的編碼如下所示,運行后效果也如下圖所示,可以看到 SF pro
支持的 Features 更多。
"c2sc"、 "calt"、 "case"、 "ccmp"、 "cv01"、 "cv02"、 "cv03"、 "cv04"、 "cv05"、 "cv06"、 "cv07"、 "cv08"、 "cv09"、 "cv10"、 "dnom"、 "frac"、 "liga"、 "locl"、 "numr"、 "pnum"、 "smcp"、 "ss01"、 "ss02"、 "ss03"、 "ss05"、 "ss06"、 "ss07"、 "ss08"、 "ss09"、 "ss12"、 "ss13"、 "ss14"、 "ss15"、 "ss16"、 "ss17"、 "subs"、 "sups"、 "tnum"、 "kern"
所以可以看到,并不是所有字體支持的 Features 都是一樣的,比如 iOS 上支持 sups
上標顯示和 subs
下標顯示,但是 Android 上的 Roboto 并不支持,甚至很多第三方字體其實并不支持 Features 。
同樣在 Web 上也存在各種限制,比如
swsh
(花體)默認下基本不支持瀏覽器,fwid
、nlck
不支持 Safari 瀏覽器等。
有趣的是,在 Flutter Web 有一個渲染文本時會變模糊的問題#58159,這個問題目前官方還沒有修復,但是你可以通過給 Text
設置任意 FontFeatures
來解決這個問題。
因為出現模糊的情況一般都是因為使用了
canvas
標簽繪制文本,而如果Text
控件具有fontFeatures
時,就會被設置為<p>
+<span>
進行渲染,從而避免問題。
最后,如果對 FontFeature 還感興趣的朋友,可以通過一下資料深入了解,如果你還有什么關于字體上的問題,歡迎留言討論。
如果你想了解更多的 features 類型,可以通過 https://en.wikipedia.org/wiki/List_of_typographic_features 了解更多;
如果你對自己的使用的字體支持什么 features 感興趣,可以通過 https://wakamaifondue.com 了解更多;
補充內容
基于網友的問題再補充一下拓展知識,畢竟這方面內容也不多。
事實上在 dart 里就可以看到對應 FontWeight
約定俗稱用的是字體集里的什么字體:
名稱 | 值 |
---|---|
Thin | w100 |
Extra | w200 |
Light | w300 |
Normal/regular/plain | w400(默認) |
Medium | w500 |
Semi-bold | w600 |
Bold | w700 |
Extra-bold- | w800 |
Black | 900 |
所以如果對于默認字體有疑問,可以在你的手機字體找找是否有對應的字體,比如雖然我們說 roboto 沒有 600 ,但是如果是 roboto mono 字體集是有 600 的 fontweight,甚至還有 600 斜體: https://fonts.google.com/specimen/Roboto+Mono 。
另外注意這是 Flutter 而不是原生,具體實現調用是在 Engine 的 paragraph_skia.cc 和 paragraph_builder_skia.cc 下對應的 setFontFamilies
相關邏輯,當然默認字體庫指定在 typography.dart
下就看到,例如 'Roboto'
、 '.SF UI Display'
、'.SF UI Text'
、'.AppleSystemUIFont'
、 'Segoe UI'
:
名稱 | 值 |
---|---|
Android,Fuchsia,Linux | Roboto |
iOS | .SF UI Display,.SF UI Text |
MacOS | .AppleSystemUIFont |
Windows | Segoe UI |
例如:.SF Text 適用于更小的字體;.SF Display 則適用于偏大的字體,我記得分水嶺好像是 20pt 左右,不過 SF(San Francisco) 屬于動態字體,系統會動態匹配。
另外如果你在 Mac 的 Web 上使用 Flutter Web,可以看到指定的是 .AppleSystemUIFont
,而對于 .AppleSystemUIFont
它其實不算是一種字體,而是蘋果上字體的一種集合別稱:
[圖片上傳失敗...(image-40f5ce-1648368234737)]
還有,如果你去看 Flutter 默認自帶的 cupertino/context_menu_action.dart
,就可以看到一個有趣的情況:
為了強調和 iOS 上的樣式盡量一直,當開發者配置
isDefaultAction == true
時,會強行指定'.SF UI Text'
并指定為FontWeight.w600
。
當然,前面我們說了那么多,主要是針對英文的情況下,而在中文下還是有差異的,之前的文章也介紹過:
-
默認在 iOS 上:
- 中文字體:
PingFang SC
- 英文字體:
.SF UI Text
、.SF UI Display
- 中文字體:
-
默認在 Android 上:
- 中文字體:
Source Han Sans
/Noto
- 英文字體:
Roboto
- 中文字體:
例如,在蘋果上的簡體中文其實會是 PingFang SC
字體,對應還有PingFang TC
和 PingFang HK
的繁體集,而關于這個問題在 Flutter 上之前還出現過比較有意思的 bug :
用戶在輸入拼音時,iOS 會在中文拼音之間添加額外的
unicode \u2006
字符,比如輸入"nihao"
,iOS 系統會在 skia 中添加文字“ni\u2006hao ”
,從而導致字體無效的情況。
當然后續的 #16709 修復了這個問題 ,而在以前的文章我也講過,當時我遇到了 “Flutter 在 iOS 系統上,系統語言是韓文時,在和中文一起出現會導致字體顯示異常" 的問題 :
解決方法也很簡單,就是給 fontFamilyFallback
配置上 ["PingFang SC" , "Heiti SC"]
就可以了,這是因為韓文在蘋果手機上使用的應該是 Apple SD Gothic Neo
這樣的超集字體庫,【廣】這個字符在這個字體集上是不存在的,所以就變成了中文的【廣】;
所以可以看到,字體相關是一個平時很少會深入接觸的東西,但是一旦涉及多語言和繪制,就很容易碰到問題的領域。