2024年,Web UI 正迎来其前所未有的黄金时代,浏览器中涌现出众多革新的 Web 功能,它们正以前所未有的速度重塑我们构建Web应用的方式,引领着全新的 Web 体验浪潮。接下来,本文将深入探索这些令人瞩目的新功能。
全新互动体验
滚动驱动动画
滚动驱动动画是一种 CSS 动画,它将动画的进度与滚动事件相关联,使得动画效果可以根据用户滚动页面的行为来触发和控制。这种动画类型利用了CSS中的animation-timeline属性,它可以创建与滚动进度或视图变化相关的自定义时间线。
例如:
图片
滚动驱动动画的类型包括:
- 默认文档时间线:这是 CSS 动画传统上使用的时间线,随着文档加载后时间的流逝而进展。
- 滚动进度时间线:这种时间线通过滚动一个可滚动元素来进展。滚动位置转换为进度百分比,从0%开始到100%结束。
- 视图进度时间线:这种时间线基于元素(称为subject)在滚动器中的可见性变化来进展。默认情况下,当subject首次在滚动器的一端可见时,时间线为 0%,当它滚动到另一端时为100%。
例如,有一个图片画廊,希望当用户滚动到图片时,图片能够逐渐淡入显示:
<div class="gallery">
<div class="image-wrapper">
<img src="image1.jpg" class="gallery-image">
</div>
<div class="image-wrapper">
<img src="image2.jpg" class="gallery-image">
</div>
<!-- 更多图片 -->
<div class="scroll-stretcher"></div> <!-- 强制产生滚动条 -->
</div>
为.gallery-image类设置animation-timeline属性为scroll(),这将创建一个匿名的滚动进度时间线,当用户滚动画廊时,图片会根据其在滚动容器中的进度逐渐淡入,透明度从0变为1,即完全可见。
.gallery {
position: relative;
height: 500px; /* 固定高度以产生滚动条 */
overflow-y: scroll;
}
.image-wrapper {
height: 300px; /* 图片容器高度 */
position: relative;
}
.gallery-image {
width: 100%;
height: auto;
opacity: 0; /* 初始透明度为0,即完全透明 */
animation-name: fade-in;
animation-duration: 1s;
animation-fill-mode: forwards; /* 动画完成后保持最终状态 */
/* 指定滚动时间线,这里使用匿名时间线 */
animation-timeline: scroll();
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.scroll-stretcher {
height: 2000px; /* 强制内容超出容器高度,产生滚动条 */
}
浏览器支持:
图片
视图过渡
视图过渡是一个全新 Web API,它提供了一种更简便的方法来在 DOM 状态之间创建动画效果,甚至可以在页面加载之间实现动画效果。在 Chromium 126 中提供了多页面应用的跨文档视图过渡支持。
例如:
图片
假设有一个网页,其中包含多个<section>元素,希望当用户滚动页面时,这些<section>元素能够以不同的动画效果进入和退出视口。
<section class="view-transition" id="section1">Section 1 Content</section>
<section class="view-transition" id="section2">Section 2 Content</section>
所有<section>元素定义一个共同的view-transition-name,这样它们就可以参与到相同的视图转换中。使用@keyframes定义两个动画效果:enterViewport和exitViewport,分别用于元素进入和退出视口时的动画。通过animation-name将进入视口的动画效果应用到.view-transition类上。当用户滚动页面,使得<section>元素进入或退出视口时,相应的动画效果将被触发。
.view-transition {
/* 定义视图转换名称 */
view-transition-name: section-transition;
/* 其他样式... */
}
/* 定义进入视口时的动画效果 */
@keyframes enterViewport {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 定义退出视口时的动画效果 */
@keyframes exitViewport {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(20px);
}
}
/* 应用动画到视图转换 */
.view-transition {
animation-name: enterViewport;
animation-fill-mode: both;
animation-duration: 1s;
}
浏览器支持:
图片
全新 UI 组件
Popover API
Popover API 是一种新的Web API,旨在简化创建和管理弹出式交互的复杂性,用于在网页上创建和管理浮动内容(也称为弹出框、弹出窗口或气泡提示)的接口或技术。
在 HTML 中可以使用popover属性将一个元素标记为弹出框,并为其设置一个 id:
<div id="simple-popover" popover>
前端充电宝
</div>
可以使用一个按钮来控制这个弹出框,使用popovertarget属性指向弹出框的 id 即可完成绑定:
<button popovertarget="simple-popover">打开弹出框</button>
还可以通过控制 popovertargetaction 属性的值来控制 popover 的行为:hide(隐藏)、show(显示)或默认的toggle(切换)。
上面例子的效果如下,我们可以自己定义弹出框的样式和行为:
图片
浏览器支持:
图片
锚点定位
CSS 锚点定位 API 允许原生地将元素相对于其他元素(称为锚点)进行定位。锚点定位提供了一种强大的工具,用于构建复杂的用户界面,如菜单、子菜单、工具提示、弹出框(popover)、对话框(dialog)、卡片等,这些元素需要相对于页面上的其他元素进行精确放置。
例如,让对话框始终放在点击按钮的正下方:
图片
假设有一个按钮(锚点),当用户点击该按钮时,想要在页面上显示一个消息框(作为定位元素),并且该消息框应该出现在按钮的正下方。首先,需要给按钮元素添加一个 anchor-name 属性,为其指定一个唯一的标识符。
<button class="anchor-button" anchor-name="my-anchor">点击我</button>
然后,在 CSS 中,可以使用 position-anchor 属性或 anchor() 函数将消息框与锚点关联起来,并指定其位置。
.anchored-message {
position-anchor: my-anchor; /* 隐式锚点 */
position: absolute;
top: 100%; /* 相对于锚点的底部 */
left: 0; /* 相对于锚点的左侧 */
/* 其他样式... */
}
或者,使用 anchor() 函数进行更复杂的定位:
.anchored-message {
position: absolute;
top: anchor(bottom) + 10px; /* 相对于锚点底部下方 10px */
left: anchor(left); /* 相对于锚点左侧 */
/* 其他样式... */
}
浏览器支持:
图片
独占式手风琴
实现手风琴组件时需要将几个<details>元素组合在一起,通过视觉分组来表明它们之间的联系。在 Chrome 120 中,引入了一项新功能,即在<details>元素上支持name属性。使用这个属性时,具有相同名称值的多个<details>元素会形成一个语义组合。该组合中最多只能打开一个元素:当打开该组合中的一个<details>元素时,之前打开的一个将自动关闭,这种手风琴称为独占式手风琴。
<details name="my-accordion">
<summary>Summary 1</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas delectus quibusdam, eum, aperiam sunt non dolorum pariatur molestias suscipit aut quia quas vero, illo quisquam nostrum sequi excepturi. Aliquam, obcaecati?</p>
</details>
<details name="my-accordion" open>
<summary>Summary 2</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas delectus quibusdam, eum, aperiam sunt non dolorum pariatur molestias suscipit aut quia quas vero, illo quisquam nostrum sequi excepturi. Aliquam, obcaecati?</p>
</details>
<details name="my-accordion">
<summary>Summary 3</summary>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas delectus quibusdam, eum, aperiam sunt non dolorum pariatur molestias suscipit aut quia quas vero, illo quisquam nostrum sequi excepturi. Aliquam, obcaecati?</p>
</details>
作为独占式手风琴的一部分的<details>元素不一定需要是兄弟元素,它们可以散布在文档任意位置。
浏览器支持:
图片
:user-valid 和 :user-invalid
:user-valid 和 :user-invalid 伪类的行为类似于 :valid 和 :invalid,但只有在用户与输入进行了重要交互之后,才匹配表单控件。即使用户尚未开始与页面进行交互,必填但是空的表单控件将匹配 :invalid。只有当用户更改输入并将其保留在无效状态时,该控件才会匹配 :user-invali。有了这两个伪类,就不再需要编写有状态的代码来跟踪用户已更改的输入。
input:user-valid,
select:user-valid,
textarea:user-valid {
--state-color: green;
--bg: linear-gradient(45deg in oklch, lime, #02c3ff);
}
input:user-invalid,
select:user-invalid,
textarea:user-invalid {
--state-color: red;
--bg: linear-gradient(15deg in oklch, #ea00ff, #ffb472);
}
浏览器支持:
field-sizing: content
field-sizing用于控制表单输入元素(如输入框input和文本域textarea)的尺寸调整方式,当为输入框或文本域设置field-sizing: content;时,这些元素的大小会根据用户输入的内容自动调整。
textarea {
field-sizing: content;
width: 200px; /* 可以设置一个固定的宽度 */
min-height: 40px; /* 可以设置一个最小高度,以防止元素过小 */
}
浏览器支持:
在下拉菜单中添加分隔线
在<select>元素中使用<hr>是一项小而实用的特性,用于在选择列表中清晰分隔内容。
<label for="major-select">Please select a major:</label> <br/>
<select name="majors" id="major-select">
<option value="">Select a major</option>
<hr>
<option value="arth">Art History</option>
<option value="finearts">Fine Arts</option>
<option value="gdes">Graphic Design</option>
<option value="lit">Literature</option>
<option value="music">Music</option>
<hr>
<option value="aeroeng">Aerospace Engineering</option>
<option value="biochemeng">Biochemical Engineering</option>
<option value="civileng">Civil Engineering</option>
<option value="compeng">Computer Engineering</option>
<option value="eleng">Electrical Engineering</option>
<option value="mecheng">Mechanical Engineering</option>
</select>
效果如下:
图片
其他特性
原生 CSS 嵌套
目前所有主流浏览器都支持原生 CSS 嵌套。使用 CSS 嵌套,可以编写更少的代码,并且代码更易于阅读和维护。
在没有CSS嵌套时,只能这样输入完整的选择器路径:
.parent1 .child1,
.parent2 .child1 {
color: red;
}
.parent1 .child2,
.parent2 .child2 {
color: green;
}
.parent1 .child2:hover,
.parent2 .child2:hover {
color: blue;
}
使用新的原生嵌套语法,可以将子选择器嵌套在父选择器中:
.parent1, .parent2 {
.child1 {
color: red;
}
.child2 {
color: green;
&:hover {
color: blue;
}
}
}
浏览器支持:
text-wrap:balance | pretty
在开发时,我们无法预知文本的样式和长度。但浏览器内置了文本换行的智能处理机制,如text-wrap:balance和text-wrap:pretty,它们能自动优化文本布局,确保文本块的和谐和美观,同时避免孤立字符和不当的连字符使用。这些功能无需手动干预,且适应各种语言和文本内容。
p {
text-wrap: balance;
}
浏览器支持:
light-dark()
light-dark() 函数能够根据当前颜色方案自动选择两种颜色中的一种进行输出,从而实现颜色的自适应显示。该函数接受两个颜色值作为其参数。根据正在使用的颜色方案,它将输出第一个或第二个颜色参数。
light-dark(<color>, <color>);
根据规范,如果使用的颜色方案是light或未知,则该函数计算为第一种颜色的计算值;如果使用的颜色方案是dark,则计算为第二种颜色的计算值。
使用的颜色方案不仅是用户选择的亮暗模式,还需要根据color-scheme属性的值确定使用的颜色方案。color-scheme属性可以指示元素使用哪种颜色方案进行渲染,这个方案会与用户的偏好进行协商,最终确定使用的颜色方案。因此,在使用light-dark()函数时,还需要在CSS中包含对应的color-scheme声明,以确保函数能够正确工作。
:root {
color-scheme: light dark;
}
:root {
--text-color: light-dark(#333, #ccc);
}
对于上面的代码,在浅色模式下返回第一个值,在深色模式下返回第二个值。
注意:light-dark() 函数的设计初衷是为了提供一个简单的中间解决方案,仅支持亮色和暗色的切换,并仅适用于颜色值。
浏览器支持:
图片
:has() 选择器
在过去的 20 年里,开发者们一直期待在 CSS 中加入“父选择器”。随着在 Chrome 105 中引入:has()选择器,这一愿望终于得以实现。
例如,可以使用.card:has(img.hero)选择那些包含英雄图像子元素的.card元素。
.card:has(.card__media) {
grid-template-areas:
"title"
"blurb"
"author"
"media";
padding-bottom: 0;
}
浏览器支持:
容器查询
容器查询可以根据元素的父元素或祖先元素的大小来设置元素的样式。这与传统的媒体查询有所不同,媒体查询是基于整个浏览器视口的大小来设置样式的。
要想设置容器查询,需要给被查询的元素设置 container-type 属性来指定容器的类型。语法如下:
container-type: inline-size;
例如:
.card-layout {
container-type: inline-size;
}
@container (min-width: 768px) {
.card {
display: grid;
font-size: 3rem;
color: blue;
}
}
这样,当容器 card-layout 的宽度大于 768px 时,其子元素 card 就使用 grid 布局。
浏览器支持:
图片