作者:Rahul Chhodde✏️
本机对话框和弹出框元素在现代前端 Web 开发中拥有自己明确定义的角色。众所周知,对话框元素可以与用户通信并收集他们的输入,而弹出窗口更适合向用户提供低优先级的辅助信息。
在上一篇关于对话框与弹出窗口的文章中,我们讨论了这两个元素都有其专用的 JavaScript API,使我们能够充分利用这两个元素。
在向这些元素添加动画和交互性时,JavaScript 库比 CSS 更受青睐。此类元素通常需要最少的动画,并且仅使用庞大的动画库来添加一些简单的效果可能会显着增加应用程序上不必要的负载。
为了解决这个问题,本文将帮助您使用纯 CSS 技术将动画效果编码到对话框和弹出窗口中。我们将介绍 CSS 关键帧和新引入的 @starting-style at-rule,这两者都可用于创建具有改进性能的微妙动画。
对话框和弹出窗口是覆盖元素,这意味着它们在浏览器渲染的最顶层进行操作。如前所述,这些元素还依赖于专用 API 来管理其显示和/或模式。
让我们看看使用传统 CSS 转换技术制作对话框和弹出窗口动画时面临的挑战。
CSS 通常不允许您将离散属性(如显示)从一个值转换为另一个值。这意味着用于创建过渡的标准不透明度 0 到 100% 的方法也不起作用,因为显示属性不允许在值切换之间存在延迟以完成过渡。
如果您检查对话框和弹出框元素的计算显示属性,您会注意到浏览器如何智能地管理它,而无需我们做任何额外的工作,除了使用相应元素的 API 提供的方法之外:
注意:由于某种原因,DevTools 中弹出框元素的计算显示不会自动更新。您必须选择不同的节点,然后重新选择弹出窗口节点才能查看更新的值。
如上所示,对话框和弹出框元素在屏幕上的可见性由浏览器内部使用 CSS 显示属性来处理。
以下演示说明了对话框和弹出框元素对显示属性的依赖性如何使标准 CSS 转换方法对它们无效:
查看 CodePen 上的 Rahul (@_rahul) 的笔不透明度/可见性过渡不适用于对话框和弹出窗口。
在专注于不透明度和变换以创建过渡之前,我们应该首先考虑显示属性,它控制关联元素在屏幕上的显示方式。
overlay 元素的另一个问题是缺乏初始样式,这对于确保动态添加到 DOM 的元素的正确过渡或通过 display 属性动态控制其可见性至关重要。
假设我们的元素在网页上呈现时应该淡入。在这种情况下,我们需要将元素的初始不透明度设置为零,然后在页面上完全呈现后将其转换为 100%。我们通常可以使用的唯一初始状态是元素的当前状态,如果提供零的不透明度,将使元素在屏幕上消失。
为了将其转变为功能效果,我们可以使用 JavaScript 添加编程延迟、类切换或 CSS 关键帧动画来模拟类似过渡的效果。
在接下来的部分中,我们将探讨如何解决显示属性无法支持过渡以及渲染前缺乏初始元素样式的问题。
如上所述,对话框和弹出框元素依赖于 display 属性来保证其在屏幕上的可见性,这使得它们几乎不可能使用 CSS 过渡来制作动画。
显示属性具有离散性质,这意味着它在值之间突然变化。例如,它可能从块更改为无,而不考虑转换持续时间中定义的延迟。这是因为这些值之间不存在逻辑中间状态,正如我们在接受不透明度、宽度、高度等附加值的属性中看到的那样。
为了使离散属性与CSS过渡兼容,引入了一个名为transition-behavior的新过渡属性,它允许您使过渡以某种方式表现,特别是对于没有数字形式的附加值的离散元素,像素或百分比。
允许离散行为不是在值之间平滑转换,而是推迟从一个离散值到另一个离散值的更改,直到指定的转换持续时间过去:
.dynamic-display { transition: opacity 0.5s, translate ..., display 0.5s allow-discrete; ... }
在上面的代码片段中,允许离散行为确保显示值将等待半秒(如转换持续时间指定),而不是突然切换。
切换离散值的延迟允许具有附加值的其他属性的转换有足够的时间来完成其工作:
查看 CodePen 上 Rahul (@_rahul) 实施的 Pen 允许离散转换行为。
通过允许离散过渡行为,我们现在了解了如何将退出过渡添加到动态管理渲染或显示的元素。但是,如果没有预渲染样式,入口过渡将不起作用。接下来的几节将探讨一些添加入口过渡的技术。
到目前为止,我们已经学习了如何将退出转换合并到动态添加和管理的元素中,现在我们将相同的技术应用于对话框和弹出窗口。
让我们首先声明进入和退出动画,并研究 CSS 关键帧如何有效地为任何元素(无论其显示如何)添加某种过渡入口点。
使用 CSS 关键帧来模仿元素的起始样式很简单。我们将从添加元素的进入和退出动画以及对话框元素的背景开始。
让我们添加一些 CSS 关键帧来为元素创建微妙的淡入和淡出动画。请注意,我们需要分别定义元素及其各自背景(伪元素)的关键帧:
/* Keyframes for dialog and popover elements */ @keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } } @keyframes fadeOut { from { opacity: 1 } to { opacity: 0 } } /* Keyframes for the backdrop pseudo-element */ @keyframes backdropFadeIn { from { background: hsl(0 0% 0% / 0%) } to { background: hsl(0 0% 0% / 50%) } } @keyframes backdropFadeOut { from { background: hsl(0 0% 0% / 50%) } to { background: hsl(0 0% 0% / 0%) } }
我们现在可以在元素的不同部分使用上述动画关键帧。我们还可以使用 [open] 属性和 :popover-open 伪类来定位对话框和弹出窗口的打开状态,如以下代码片段所示:
.my-dialog { animation: fadeOut 0.5s forwards; &::backdrop { animation: backdropFadeOut 0.5s forwards; } &[open] { animation: fadeIn 0.5s forwards; &::backdrop { animation: backdropFadeIn 0.5s forwards; } } } .my-popover { animation: fadeOut 0.5s forwards; &:popover-open { animation: fadeIn 0.5s forwards; } }
如果我们组合上述代码片段并在对话框和弹出框元素中使用它们,结果将类似于下面共享的演示。这种技术对于进入动画来说非常有用,但它完全跳过了退出动画部分:
查看 CodePen 上 Rahul (@_rahul) 的 Pen HTML5 对话框和带有 CSS 关键帧的弹出窗口输入动画。
如果您关注微交互,您会注意到打开对话框时淡入进入动画效果很好,但在关闭或取消对话框时,淡出退出动画似乎不起作用工作。让我们在下一节中了解原因。
退出动画在上面的演示中不起作用的原因是由于弹出窗口和对话框 API 导致元素的计算显示发生突然变化。我们之前讨论了过渡行为属性如何帮助我们管理离散的 CSS 属性和过渡。让我们尝试在这种情况下使用过渡行为属性,看看它是否可以解决问题。
查看 CodePen 上 Rahul (@_rahul) 的 Pen HTML5 对话框以及带有 CSS 关键帧的弹出窗口进入和退出动画。
幸运的是,向显示和覆盖属性添加允许离散行为已经解决了退出动画问题。进入和退出动画现在都可以正常工作。
在生产中,这种方法最终会产生更大的代码,每个动画声明块都有两到三个特定于供应商的变体。我们在这里实现的效果并不太复杂,如果没有对话框和弹出框元素,可以使用 CSS 过渡来实现。
CSS 关键帧最适合创建关键帧动画,但它们不提供预渲染优化,尽管它们看起来似乎如此。然而,通过新引入的@starting-style at-rule,我们可以在不使用CSS关键帧动画的情况下实现基于过渡的效果。
之前我们讨论了依赖 DOM 的元素如何需要一个初始样式过渡的起点,而这正是新的 @starting-style CSS at-rule 所提供的。
@starting-style at-rule 是 CSS Transition Level 2 功能,用于声明过渡元素上属性的起始值,从第一次样式更新开始。
The following syntax allows you to specify a starting point for the styles of a given element from which the transition will pick up and operate. The properties included within the selectors of this at-rule should be the ones that will be involved in the associated transitions:
@starting-style { .selector { opacity: 0; ... } }
Try re-rendering the element dynamically in the below demo by pressing the trigger button, and see how straightforward it is to create an entry point for the transitions with @starting-style before the associated elements are rendered:
See the Pen HTML5 Dialog and popover entry & exit animations w/ CSS keyframes by Rahul (@_rahul) on CodePen.
The @starting-style feature is expected to gain solid support across major web browsers, and currently, it is well-supported on Chromium and Webkit-based browsers. See the latest support here.
Following the pattern above, we can add subtle animations to dialog and popover elements using the allow-discrete transition behavior and @starting-style.
Before moving ahead, let’s first ensure that we use the allow-discrete behavior for the transition of display and overlay properties. This can be done explicitly by defining the transition-behavior property inside the selectors, or you can combine it in the transition property alongside other transitions as shown below:
.my-dialog, .my-popover { transition: opacity 0.5s, translate 0.5s, overlay 0.5s allow-discrete, display 0.5s allow-discrete; &::backdrop { transition: background 0.5s, overlay 0.5s allow-discrete, display 0.5s allow-discrete; } }
To handle the initial styles for the open state, we should add a @starting-style block and add the properties that are responsible for our transition effect. You don't need to include the display and overlay properties here, as those are already managed by the dialog and popover APIs behind the scenes:
@starting-style { .my-dialog[open], .my-popover:popover-open { opacity: 0; transform: translateY(-1em); } .my-dialog[open]::backdrop { background-color: hsl(0 0 0 / 0%); } }
With dialogs and popovers, we have the API advantage to ourselves, which means we can use certain attributes and pseudo-classes like dialog[open] and :popover-open to target the open states:
.my-dialog[open], .my-popover:popover-open { opacity: 1; transform: translateY(0); } .my-dialog[open]::backdrop { background-color: hsl(0 0 0 / 10%); }
Lastly, you can give the original elements styles that correspond to a closing transition, which is basically the closing state. In other words, keep the dialog element faded out and slid up by default:
.my-dialog, .my-popover { opacity: 0; translate: 0 -1em; } .my-dialog { &::backdrop { background-color: transparent; } }
The following demo reflects the outcomes of applying allow-discrete behavior to transitions, defining initial styles with @starting-style, and styling for both open and closed states. Now both the entry and exit animations work smoothly with our dialog and popover elements, and we used less code compared to CSS keyframes:
See the Pen HTML5 Dialog and popover entry & exit animations w/ @starting-style by Rahul (@_rahul) on CodePen.
If you want to remember this sequence of states we covered when constructing the above example, refer to the below graphic which visually illustrates the changes and communication between states:
Let’s take it one step further by implementing different animations for dialog elements. The baseline remains the same: only the properties related to transformations and transitions will change.
The idea behind creating a stylish rotating dialog box involves playing with the opacity and a couple of CSS transformation properties:
.my-dialog { transition: opacity 0.5s, translate 0.5s, rotate 0.5s, overlay 0.5s allow-discrete, display 0.5s allow-discrete; }
The starting styles for the open state of the dialog include the following:
Here’s the code:
@starting-style { .my-dialog[open] { opacity: 0; translate: -50% -100%; rotate: -180deg; } }
The closed state resembles the starting styles but with altered translations and rotations to reflect an opposite movement when exiting the dialog element:
.my-dialog { /* Previous styles */ opacity: 0; translate: 50% 100%; rotate: 180deg; }
The dialog element in the open state has 100 percent opacity, no translation on either axis, and no rotation, effectively positioning it at the center of the screen:
.my-dialog[open] { opacity: 1; translate: 0 0; rotate: 0deg; }
The final output looks something like below:
See the Pen HTML5 Dialog and popover rotating animation by Rahul (@_rahul) on CodePen.
To create a bouncing effect without using CSS keyframe animations, we can utilize a Bezier curve as the transition-timing function for the transformation and opacity transitions of our dialog. We’ll use the scale transformation for this effect.
Feel free to experiment with different x1, y1, x2, and y2 values for the Bezier curve, and implement them in your animation projects:
.my-dialog { transition: opacity 0.4s cubic-bezier(0.4, 1.6, 0.4, 0.8), scale 0.4s cubic-bezier(0.4, 1.6, 0.4, 0.8), overlay 0.4s allow-discrete, display 0.4s allow-discrete; }
Now we can easily determine the starting styles and the open and closed states. The initial styles for the open and closed states will be the same — the dialog box will be scaled down to zero and completely transparent.
In the open state, the dialog box will have 100 percent opacity and be scaled to one. The rest of the transition effects will be handled by the Bezier curve-powered transitions:
@starting-style { .my-dialog[open] { opacity: 0; scale: 0; } } .my-dialog { opacity: 0; scale: 0; &[open] { opacity: 1; scale: 1; } }
Here’s how this looks in action:
See the Pen HTML5 Dialog and popover bouncing animation by Rahul (@_rahul) on CodePen.
We will use the same Bezier curve in this animation to keep things simple, but the effect will be different compared to the previous one. The idea is to translate the dialogue along the y-axis instead of scaling it, as we did with the last effect:
.my-dialog { transition: opacity 0.5s cubic-bezier(0.4, 1.6, 0.4, 0.8), translate 0.5s cubic-bezier(0.4, 1.6, 0.4, 0.8), overlay 0.5s allow-discrete, display 0.5s allow-discrete; }
The idea is to keep the dialog way up the viewport on the y-axis initially. Then, we will transform the dialog to zero when it is opened and finally translate it down the axis:
@starting-style { .my-dialog[open] { opacity: 0; translate: 0 -200%; } } .my-dialog { opacity: 0; translate: 0 200%; &[open] { opacity: 1; translate: 0 0; } }
Instead of applying a 100 percent positive or negative translation, I doubled it to create the impression of urgency in the dialog box. See it in action below:
See the Pen HTML5 Dialog and popover slide-up-down bouncing animation by Rahul (@_rahul) on CodePen.
The above effects suit the dialog elements well, but they won’t look great with the popover elements. This section is dedicated to some nice popover animation effects, which make the popover look like popovers and nothing more.
This effect resembles the popover effect we created initially. In that example, the popover appeared from the top and slide-fading down the y-axis, which isn’t what you’d expect from a popup in the bottom-right corner of the viewport.
Let’s rectify that by adding the same translation on the y-axis for the starting styles and the close state. Everything else remains unchanged:
See the Pen Rising up and down popover animation by Rahul (@_rahul) on CodePen.
The process of creating a growing and shrinking effect is simple and involves the use of scale transformation with a twist.
By setting the transformation origin to the absolute bottom-right, we can ensure that the popover expands from the right, aligning with its current position at the bottom-right.
See the Pen Growing and shrinking popover animation by Rahul (@_rahul) on CodePen.
This technique is commonly used to create animations for notification toasts. To achieve this effect, you simply need to translate the popover element 100 percent to the right, putting it out of the viewport. Then, in the open state, you can translate it back to zero to complete the effect.
See the Pen Slide in out from right popover animation by Rahul (@_rahul) on CodePen.
We learned about incorporating CSS transition-based animations in dialog and popover elements using pure CSS. We discussed the complexities and issues of traditional transitioning with overlay elements, and then we addressed these problems one by one using CSS keyframes and, more importantly, the @starting-style at-rule, which is specially developed for transitions.
However, the @starting-style feature is fairly new and not yet available globally. Consequently, using CSS keyframes and the Web Animation API is an option that makes sense in production and provides more granular control over adding animation effects.
Having said that, I recommend the @starting-style approach once it gets adopted widely to keep things simple and lightweight with CSS transition applications.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
以上是使用 CSS @starting-style 动画对话框和弹出框元素的详细内容。更多信息请关注PHP中文网其他相关文章!