突破限制,CSS font-variation 可变字体的魅力
今天,在 CodePen 上看到一个很有意思的效果 -- GSAP 3 ETC Variable Font Wave,借助了 JS 动画库 GSAP 实现,一起来看看:
我寻思着能否使用 CSS 复刻一版,鼓捣了一会,利用纯 CSS 成功实现了原效果。
上述效果,最核心的就是文字的动画,文字从较细贴着较紧,到较粗隔着较远不断变化。有人会认为这里是 transform: scale()
,实则不然。
scale
是等比例放大缩小一个物体,而仔细观察上述效果,明显是有字体的粗细、字体的字宽的变化。这里,其实用到了 CSS 比较新的特性 -- 可变字体,也就是 font-variation
。
本文,将借助这个效果,介绍一下什么是 CSS font-variation。
什么是 CSS font-variation,可变字体?
根据 MDN -- Variable fonts,可变字体(Variable fonts)是 OpenType 字体规范上的演进,它允许将同一字体的多个变体统合进单独的字体文件中。从而无需再将不同字宽、字重或不同样式的字体分割成不同的字体文件。我们只需通过CSS与一行 @font-face 引用,即可获取包含在这个单一文件中的各种字体变体。
emm,概念有点难理解,简单解释一下。
与可变字体对应的,是标准(静态)字体。
标准(静态)字体就是只代表字体的某一特定的宽度/字重/样式的组合的字体文件,通常我们在页面引入的字体文件都是这种,只代表这个字体的某一特定的宽度/字重/样式的组合。
而如果我们想引入一个字体家族(譬如 Roboto 字体族),它可能包含了 “Roboto Regular”(常规字重)、“Roboto Bold”(粗体),或是 “Roboto Bold Italic”(粗体+斜体) 等一系列字体文件。这意味着我们可能需要 20 或 30 个不同的字体文件才能算是有了一整个字体家族(对于有着不同宽度的大型字体来说,这个数量还要翻上几倍)。
而可变字体 -- font-variation
,可以将它理解为 all in one
,通过使用可变字体,所有字重、字宽、斜体等情况的排列组合都可以被装进一个文件中。当然,这个文件可能比常规的单个字体文件大一些。
静态字体的局限性
举个例子,在 Google Font,我随便选取一个标准静态字体,实现一个字体 font-weight
的变化动画:
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300&display=swap');
p {
font-family: 'Lato', sans-serif;
font-size: 48px;
}
p:nth-child(1) {
font-weight: 100;
}
p:nth-child(2) {
font-weight: 200;
}
p:nth-child(3) {
font-weight: 300;
}
p:nth-child(4) {
font-weight: 400;
}
p:nth-child(5) {
font-weight: 500;
}
p:nth-child(6) {
font-weight: 600;
}
看看结果:
并没有我们想象中的,因为字体粗细从 100 到 600,所以字体依次变粗的情况,一共只有两种字重:
- 当
font-weight:
处于 100 - 500 的时候,其实都是font-weight: normal
; - 当
font-weight: 600
的时候,其实是命中了font-weight: bold
。
这个也就是传统静态字体的局限性,单一字体文件中,其实是不会有该字体的所有粗细、字宽的类型的。
可变字体的多样性
接下来,我们换上可变字体。
加载可变字体的语法与其他 web 字体非常相似,但有一些显著的差异,这些差异是通过对现代浏览器中可用的传统 @font-face 语法的升级提供的。
基本语法是相同的,但是字体技术是不一样的,并且可变字体可以提供像对 font-weight
和 font-stretch
等描述符的允许范围,而不是根据加载的字体文件来命名。
下面,我们将加载一个支持字体粗细从 100
到 900
,字体伸缩变形支持从 10%
到 400%
的 AnyBody
可变字体。
@font-face {
font-family: 'Anybody';
src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
font-display: block;
font-style: normal italic;
font-weight: 100 900;
font-stretch: 10% 400%;
}
p {
font-family: 'Anybody', sans-serif;
font-size: 48px;
}
p:nth-child(1) {
font-weight: 100;
}
// ...
p:nth-child(6) {
font-weight: 600;
}
同样是设定字体粗细从 100 - 600,效果如下:
这一次,可以看到,字体有明显的均匀变化,支持 font-weight: 100
到 font-weight: 600
的逐渐变化。这儿就是可变字体的魅力。
理解 font-variation-settings
除了直接通过 font-weight
去控制可变字体的粗细,CSS 还提供了一个新的属性 font-variation-settings
去同时控制可变字体的多个属性。
可变字体新格式的核心是可变轴的概念,其描述了字体设计中某一特性的允许变化范围。
所有可变字体都有至少有 5 个可以通过 font-variation-settings
控制的属性轴,它们属于注册轴(registered),能够映射现有的 CSS 属性或者值。
它们是:
- 字重轴 "wght":对应
font-weight
,控制字体的粗细 - 宽度轴 "wdth":对应
font-stretch
,控制字体的伸缩 - 斜度轴 "slnt" (slant):对应字体的
font-style: oblique + angle
,控制字体的倾斜 - 斜体轴 "ital":对应字体的
font-style: italic
,控制字体的倾斜(注意,和font-style: oblique
是不一样的倾斜) - 光学尺寸轴 "opsz":对应字体的
font-optical-sizing
,控制字体的光学尺寸
好吧,可能会有一点点懵,没事,通过一个例子马上就能理解什么意思。
还是利用上述的可变字体,我们利用 font-variation-settings
实现一个字体粗细的变化的动画:
<p>Anybody</p>
@font-face {
font-family: 'Anybody';
src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
font-display: block;
font-style: normal italic;
font-weight: 100 900;
font-stretch: 10% 400%;
}
p {
font-family: 'Anybody';
font-size: 48px;
animation: fontWeightChange 2s infinite alternate linear;
}
@keyframes fontWeightChange {
0% {
font-variation-settings: 'wght' 100;
}
100% {
font-variation-settings: "wght" 600;
}
}
效果如下:
其中,其实可以理解为,利用了 font-variation-settings: "wght"
的变化的动画,等同于 font-weight
变化动画:
利用 font-variation-settings 进行字体的多个特征同时变化
OK,那么如果既然是一样的效果,为什么还要多此一举搞个 font-variation-settings
呢?
那是因为 font-variation-settings
除了支持字体的粗细变化外,还支持上述说的注册轴设定的多个样式属性变化,以及自定义轴的一些字体样式属性变化。
这次,除了字体粗细外,我们再添加上 "wdth"
的变化,也就是字体的伸缩。
<p>Anybody</p>
@font-face {
font-family: 'Anybody';
src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
font-display: block;
font-style: normal italic;
font-weight: 100 900;
font-stretch: 10% 400%;
}
p {
font-family: 'Anybody';
font-size: 48px;
animation: fontWeightChange 2s infinite alternate linear;
}
@keyframes fontWeightChange {
0% {
font-variation-settings: 'wght' 100, 'wdth' 60;
}
100% {
font-variation-settings: "wght" 600, 'wdth' 400;
}
}
这次,进行的是字体粗细从 100 到 600,字体伸缩从 60% 到 400% 的动画效果,效果如下:
也就是说,font-variation-settings
是同时支持多个字体样式一起变化的,这一点非常重要。
到这里,其实我们已经可以利用这个实现题图所示的效果了,我们简单改造下,添加多行,再给每行设定一个负的动画延迟即可:
<div class="g-container">
<ul>
<li>ANYBODY</li>
<li>ANYBODY</li>
<li>ANYBODY</li>
<li>ANYBODY</li>
<li>ANYBODY</li>
<li>ANYBODY</li>
<li>ANYBODY</li>
<li>ANYBODY</li>
</ul>
</div>
借助 SCSS 简化下代码,下述代码核心就是给每个 li
,添加一个相同的动画 font-variation-settings
动画,并且依次设置了等差的 animation-delay
:
li {
animation: change 0.8s infinite linear alternate;
}
@for $i from 1 to 9 {
li:nth-child(#{$i}) {
animation-delay: #{($i - 1) * -0.125}s;
}
}
@keyframes change {
0% {
font-variation-settings: 'wdth' 60, 'wght' 100;
opacity: .5;
}
100% {
font-variation-settings: 'wdth' 400, 'wght' 900;
opacity: 1;
}
}
效果如下:
好,接下来,利用 CSS 3D 简单构造一下 3D 场景即可,完整的 CSS 代码如下:
@font-face {
font-family: 'Anybody';
src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
font-display: block;
font-style: normal italic;
font-weight: 100 900;
font-stretch: 10% 400%;
}
.g-container {
position: relative;
margin: auto;
display: flex;
font-size: 48px;
font-family: 'Anybody';
color: #fff;
transform-style: preserve-3d;
perspective: 200px;
}
ul {
background: radial-gradient(farthest-side at 110px 0px, rgba(255, 255, 255, 0.2) 0%, #171717 100%);
padding: 5px;
transform-style: preserve-3d;
transform: translateZ(-60px) rotateX(30deg) translateY(-30px);
animation: move 3s infinite alternate;
&::before {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 45px;
background: #141313;
transform: rotateX(-230deg);
transform-origin: 50% 100%;
}
}
li {
width: 410px;
animation: change 0.8s infinite linear alternate;
}
@for $i from 1 to 9 {
li:nth-child(#{$i}) {
animation-delay: #{($i - 1) * -0.125}s;
}
}
@keyframes change {
0% {
font-variation-settings: 'wdth' 60, 'wght' 100;
opacity: .5;
}
100% {
font-variation-settings: 'wdth' 400, 'wght' 900;
opacity: 1;
}
}
@keyframes move {
100% {
transform: translateZ(-60px) rotateX(30deg) translateY(0px);
}
}
效果如下,我们就基本还原了题图的效果:
完整的代码及 DEMO 效果你可以戳这里:CodePen Demo -- Pure CSS Variable Font Wave
font-variation 的可变轴 -- 注册轴与自定义轴
回归到可变字体本身。上面提到了可变轴这个概念,它们分为注册轴与自定义轴,英文是:
- 注册轴 - registered axes
- 自定义轴 - custom axes
可变字体新格式的核心是可变轴的概念,其描述了字体设计中某一特性的允许变化范围。
例如‘字重轴’描述了字体的粗细;“宽度轴”描述了字体的宽窄;“斜体轴”描述是否使用斜体字形并且可相应地开关;等。请注意,轴既可以是范围选择又可以是开关选择。字重可能在1-999之间,而斜体可能只是简单的0或1(关闭或打开)。
如规范中所定义,存在两种变形轴,注册轴和自定义轴:
- 注册轴最为常见,常见到制定规范的作者认为有必要进行标准化。 目前注册的五个轴是字重,宽度,倾斜度,斜体和光学尺寸。
上文其实已经罗列了 5 个注册轴,并且简单介绍了它们的使用。再罗列一次:
- 字重轴 "wght":对应
font-weight
,控制字体的粗细 - 宽度轴 "wdth":对应
font-stretch
,控制字体的伸缩 - 斜度轴 "slnt" (slant):对应字体的
font-style: oblique + angle
,控制字体的倾斜 - 斜体轴 "ital":对应字体的
font-style: italic
,控制字体的倾斜(注意,和font-style: oblique
是不一样的倾斜) - 光学尺寸轴 "opsz":对应字体的
font-optical-sizing
,控制字体的光学尺寸
- 自定义轴实际上是无限的:字体设计师可以定义和界定他们喜欢的任何轴,并且只需要给它一个四个字母的标签以在字体文件格式本身中识别它。
我们来看一个 自定义轴 的例子:
<p>Grade</p>
p {
font-family: "Amstelvar VF", serif;
font-size: 64px;
font-variation-settings: 'GRAD' 88;
}
上述 font-family: "Amstelvar VF"
是一个可变字体,而 'GRAD' 属于自定义轴的一个,意为等级轴。
- 等级轴 'GRAD':“等级”一词指的是字体设计的相对重量或密度,但与传统的“重量”不同之处在于文本占据的物理空间不会改变,因此改变文本等级并不会改变文本或其周围元素的整体布局。 这使得等级成为有用的变化轴,因为它可以变化或动画而不会引起文本本身的回流。
MDN 上有关于改变 'GRAD' 的值,对应字体变化的一个 DEMO,效果如下:
值得注意的是,自定义轴可以是字体设计师想象的任何设计变化轴。可能有一些会逐渐变得相当普遍,随着规范的发展甚至演变成注册轴。
去哪找可变字体?
OK,如果现在我想在业务中使用一下可变字体,去实现一个效果或者动画,可以上哪里寻找可变字体的资源呢?
这里有一个很不错的网站 -- Variable Fonts。
上面收集了非常多的 Variable Fonts,并且罗列出了它们在注册轴上支持的字体属性的范围,譬如支持字重从 100 到 700,我们可以自由进行调试预览
Can i Use(2022-02-20)
现在能够开始使用可变字体了吗?
截止至今天,Can i Use 的截图:
兼容性已经非常的不错了,不考虑 IE 系列的话可以上到实际的生产环境中去。
最后
本文到此结束,希望对你有帮助
更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。
如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
突破限制,CSS font-variation 可变字体的魅力的更多相关文章
- CSS Font知识整理总结
1.什么是字体 字体是文字的外在形式,就是文字的风格,是文字的外衣.比如行书.楷书.草书,都是一种字体.同样一个字每个人写起来都会有差异,可以说每个人都有一套潜在的字体库.对于web页面来说,字体就是 ...
- CSS远程加载字体
CSS 远程加载字体的方法,做网站CSS的都知道,用户浏览网站时,网页上的字体是加载本地的.换言之,如果网站使用了用户电脑所没有安装的字体,那显示字体就会被默认字体所代替了,自然效果就大受影响了. 上 ...
- 【转载】CSS font关键字属性值的简单研究
文章转载自 张鑫旭-鑫空间-鑫生活 http://www.zhangxinxu.com/wordpress/ 原文链接:http://www.zhangxinxu.com/wordpress/?p=5 ...
- Font Awesome图标字体应用及相关
作为web开发者,难免要经常要用到些小图标,给自己web增添几分活力和多样性.像这些: 而Font Awesome刚好为我们提供了这些.到目前为止,Font Awesome提供了有500多个可缩放的的 ...
- DIV+CSS+JS实现色彩渐变字体
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- CSS精灵图与字体图标
CSS精灵图与字体图标 1. 精灵图 当用户访问一个网站时,需要向服务器发送请求,网页上的每张图像都要经过一次请求才能展现给用户.然而,一个网页中往往会应用很多小的背景图像作为修饰,当网页中的图像过多 ...
- 如何制作图标字体(如何将svg转换为css可用的图标字体)
转自: 如何制作图标字体(如何将svg转换为css可用的图标字体) 具体描述 在项目开发当中,我们常常遇到需要将获取到的svg转换为,css可用的图标字体,那么具体该如何进行操作呢 具体操作 登录ic ...
- CSS Web Fonts 网络字体
Fonts 1. CSS font-family 在 CSS 中,可以使用 font-family 属性来指定字体,浏览器渲染文字时候会根据这个属性应用于元素.如果没有指定这个属性或者指定的字体不存在 ...
- [WPF] 假装可变字体
1. 可变字体 上图中的两个动画,一个文字直接变粗,一个渐渐变粗,我觉得后者会更有趣.但普通的字体可达不到这种效果,例如微软雅黑,无论怎么调整它的 FontWeight,实际上它也只有三种粗细: 这时 ...
随机推荐
- python中的sort方法和sorted方法
一.sort()函数 描述 sort() 函数用于对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数. 语法 sort()方法语法: 1 list.sort(cmp=None, key=No ...
- 【刷题-LeetCode】200 Number of Islands
Number of Islands Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. ...
- 【刷题-PAT】A1112 Stucked Keyboard (20 分)
1112 Stucked Keyboard (20 分) On a broken keyboard, some of the keys are always stucked. So when you ...
- PayPal支付-Reaact框架
前情提要 之前用React框架做过一个网站的开发,客户是国外的公司,所以为迎合受众,支付模块添加了我国不常用但国外常用的Paypal.最近在整理文档,就把当时写的这篇经验总结再整合以下发布. payp ...
- vue学习17-插槽作用域
<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta http ...
- gin中自定义路由日志的格式
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" &quo ...
- ARTS Week 22
Algorithm 本周的 LeetCode 题目为 297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也 ...
- 【简记】SpringBoot禁用Swagger
楔子 Swagger 是 Java Web 开发中常用的接口文档生成类库,在开发和前后端联调时使用它来模拟接口调用能提高开发效率.但是,在生产环境可能并不需要它,一个原因是启用它会延长程序启动时间(动 ...
- 抽象类 final
抽象类 1.用abstract关键字来修饰一个类时,这个类叫做抽象类,用abstract来修饰一个方法时,这个方法叫抽象方法. 2.含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被 ...
- 不难懂-----redux
一.flux的缺陷 因为dispatcher和Store可以有多个互相管理起来特别麻烦 二.什么是redux 其实redux就是Flux的一种进阶实现.它是一个应用数据流框架,主要作用应用状态的管理 ...