过渡
元素可以淡出、菜单可以滑入、颜色可以从一种变为另一种,实现这些效果最简单的方式是过渡(transitions)。
从这边到那边
简单示例
<button class="button-demo">Hover over me</button>
<style scoped>
.button-demo {
background-color: hsl(180, 50%, 50%);
border: 0;
color: white;
font-size: 1rem;
padding: 0.3em 1em;
transition-property: all;
transition-duration: 0.5s;
}
.button-demo:hover {
background-color: hsl(0, 50%, 50%);
border-radius: 1em;
}
</style>
在 Markdown 正文内直接植入上述 HTML 片段,VitePress 可以正常渲染页面。
小结
过渡共有四个属性,均以
transistion-
开头transition-property
transition-duration
transition-timing-function
transition-delay
开发时一般使用简写语法
csstransition: <transition-property> <transition-duration> <transition-timing-function> <transition-delay>;
过渡有两种方法触发
- 状态变化
: hover
- JavaScript 添加或删除影响元素样式的某个类
- 状态变化
transition
属性一般添加在元素选择器,不是添加在伪类选择器;需要过渡变化的属性则添加在伪类选择器如果需要为不同的属性分别设置不同的过渡,可以添加多个过渡规则,以逗号分隔
定时函数
linear
ease
ease-in
ease-out
ease-in-out
简单示例
<div class="container">
<div class="box"></div>
</div>
<style scoped>
.container {
position: relative;
height: 30px;
}
.box {
position: absolute;
left: 0;
height: 30px;
width: 30px;
background-color: hsl(130, 50%, 50%);
transition: all 1s linear;
}
.container:hover .box {
left: 400px;
}
</style>
小结
出于性能考虑,应该避免对 left
属性使用过渡效果,更换使用 transform
变化。
贝塞尔曲线
待学习
非动画属性
方案一:点击按钮时在父容器上 添加/删除 is-open
类,使兄弟元素的显示状态在 display: none;
和 display: block;
之间切换。
<!-- dropdownMenu2.html -->
<div class="dropdown" aria-haspopup="true">
<button class="dropdown__toggle" type="button">Menu</button>
<div class="dropdown__drawer">
<ul class="menu" role="menu">
<li role="menuitem">
<a href="/features">Item 1</a>
</li>
<li role="menuitem">
<a href="/pricing">Item 2</a>
</li>
<li role="menuitem">
<a href="/blog">Item 3</a>
</li>
<li role="menuitem">
<a href="/about">Item 4</a>
</li>
</ul>
</div>
</div>
<p><a href="/read-more">Read more</a></p>
<script type="module">
var toggle = document.getElementsByClassName("dropdown__toggle")[0];
var dropdown = toggle.parentElement;
toggle.addEventListener("click", function () {
dropdown.classList.toggle("is-open");
});
</script>
/* dropdownMenu2.css */
@layer global, modules;
@layer global {
body {
font-family: Helvetica, Arial, sans-serif;
}
}
@layer modules {
.dropdown {
--border-color: oklch(61% 0.08 314deg);
--text-color: oklch(39% 0.06 314deg);
--text-color-focused: oklch(39% 0.2 314deg);
--background-color: white;
--highlight-color: oklch(95% 0.01 314deg);
}
.dropdown__toggle {
display: block;
padding: 0.5em 1em;
border: 1px solid var(--border-color);
color: var(--text-color);
background-color: var(--background-color);
font: inherit;
text-decoration: none;
transition: background-color 0.2s linear;
}
.dropdown__toggle:hover {
background-color: var(--highlight-color);
}
.dropdown__drawer {
position: absolute;
display: none;
background-color: var(--background-color);
width: 10em;
}
.dropdown.is-open .dropdown__drawer {
display: block;
}
.menu {
padding-left: 0;
margin: 0;
list-style: none;
}
.menu > li + li > a {
border-top: 0;
}
.menu > li > a {
display: block;
padding: 0.5em 1em;
color: var(--text-color);
background-color: var(--background-color);
text-decoration: none;
transition: all 0.2s linear;
border: 1px solid var(--border-color);
}
.menu > li > a:hover {
background-color: var(--highlight-color);
color: var(--text-color-focused);
}
}
显示效果
VitePress 内相关演示效果未能实现,请在 Codepen 内调试。
实现分析
Here’s a step-by-step explanation of how the dropdown menu works, showing how the HTML, CSS, and JavaScript interact:
HTML Structure
- The dropdown menu is built with a container
<div class="dropdown">
. - Inside, there’s a button (
<button class="dropdown__toggle">Menu</button>
) that the user clicks to toggle the menu. - The menu itself is in
<div class="dropdown__drawer">
, which contains a list of menu items.
- The dropdown menu is built with a container
CSS Styling and Visibility Control
.dropdown__drawer
is hidden by default withdisplay: none;
.- When the parent
.dropdown
has the class.is-open
, the CSS rule.dropdown.is-open .dropdown__drawer { display: block; }
makes the menu visible. - Other CSS rules style the button and menu for appearance and interactivity.
JavaScript Interactivity
- The script selects the toggle button and its parent dropdown container.
- When the button is clicked, it toggles the class
is-open
on the.dropdown
element. - This class toggle triggers the CSS to show or hide the menu.
How It All Works Together
Initial State:
- The menu is hidden (
display: none;
).
- The menu is hidden (
User Clicks "Menu":
- JavaScript adds the
is-open
class to.dropdown
.
- JavaScript adds the
CSS Reacts:
- The selector
.dropdown.is-open .dropdown__drawer
applies, making the menu visible (display: block;
).
- The selector
User Clicks Again:
- JavaScript removes the
is-open
class, hiding the menu.
- JavaScript removes the
Summary:
- HTML provides the structure.
- CSS controls visibility and style based on the presence of the
is-open
class. - JavaScript toggles the
is-open
class in response to user clicks, connecting the HTML and CSS for interactive behavior.
方案缺点:display
属性只能从 none
到 block
切换,无法过渡。
方案二: 换用 opacity
属性,可以过渡。
使用新的 CSS 代码如下:
/* dropdownMenu4.css */
@layer global, modules;
@layer global {
body {
font-family: Helvetica, Arial, sans-serif;
}
}
@layer modules {
.dropdown {
--border-color: oklch(61% 0.08 314deg);
--text-color: oklch(39% 0.06 314deg);
--text-color-focused: oklch(39% 0.2 314deg);
--background-color: white;
--highlight-color: oklch(95% 0.01 314deg);
}
.dropdown__toggle {
display: block;
padding: 0.5em 1em;
border: 1px solid var(--border-color);
color: var(--text-color);
background-color: var(--background-color);
font: inherit;
text-decoration: none;
transition: background-color 0.2s linear;
}
.dropdown__toggle:hover {
background-color: var(--highlight-color);
}
.dropdown__drawer {
position: absolute;
/* display: none; */
background-color: var(--background-color);
width: 10em;
visibility: hidden;
opacity: 0;
transition: visibility 0s linear 0.2s, opacity 0.2s linear;
}
.dropdown.is-open .dropdown__drawer {
/* display: block; */
opacity: 1;
visibility: visible;
transition-delay: 0s;
}
.menu {
padding-left: 0;
margin: 0;
list-style: none;
}
.menu > li + li > a {
border-top: 0;
}
.menu > li > a {
display: block;
padding: 0.5em 1em;
color: var(--text-color);
background-color: var(--background-color);
text-decoration: none;
transition: all 0.2s linear;
border: 1px solid var(--border-color);
}
.menu > li > a:hover {
background-color: var(--highlight-color);
color: var(--text-color-focused);
}
}
实现分析
Based on the code in dropdownMenu2.html
and dropdownMenu4.css
, here is a detailed step-by-step explanation of the menu toggling and transition effects.
Core Logic
The entire effect is controlled by adding and removing the class .is-open
from the main <div class="dropdown">
container. A small piece of JavaScript listens for a click on the "Menu" button and toggles this class on its parent element. The CSS then uses this class to apply different styles to the menu drawer (<div class="dropdown__drawer">
), triggering the transitions.
The key CSS properties involved are opacity
(transparency) and visibility
(whether the element is rendered and can be interacted with).
Action: Opening the Menu (Hidden to Visible)
This is the sequence of events when the user clicks the "Menu" button to show the dropdown.
Initial State (.dropdown__drawer
):
opacity: 0
(completely transparent)visibility: hidden
(not visible, not interactive)
Step 1: User Click
- The user clicks the
<button class="dropdown__toggle">
. - The JavaScript adds the
is-open
class to the parent<div class="dropdown">
.
Step 2: CSS Rules Change
- The element now matches the selector
.dropdown.is-open .dropdown__drawer
. - This new rule sets the target state for the drawer:
opacity: 1
visibility: visible
transition-delay: 0s
(This is crucial, as it overrides any delay from the base style).
Step 3: Transitions Kick In
The browser now transitions the properties from their initial state to the new target state.
Immediately (at 0 seconds):
- The
transition-delay: 0s
from the.is-open
rule overrides the 0.2s delay on thevisibility
property. visibility
changes fromhidden
tovisible
instantly. The menu drawer now occupies space in the layout and its content becomes interactive.- The
opacity
transition begins. It starts changing from 0 towards 1.
- The
From 0 to 0.2 seconds:
- The
opacity
continues to animate from 0 to 1 over a duration of 0.2 seconds. The menu smoothly fades into view.
- The
At 0.2 seconds:
- The
opacity
transition is complete. The menu is now fully visible (opacity: 1
).
- The
Summary of Opening Effect: The menu instantly appears in the document flow and starts fading in at the same time. The fade-in animation is smooth and lasts for 0.2 seconds.
Action: Closing the Menu (Visible to Hidden)
This is the sequence of events when the user clicks the "Menu" button again to hide the dropdown.
Initial State (.dropdown.is-open .dropdown__drawer
):
opacity: 1
(fully visible)visibility: visible
(rendered and interactive)
Step 1: User Click
- The user clicks the
<button class="dropdown__toggle">
. - The JavaScript removes the is-open class from the
<div class="dropdown">
.
Step 2: CSS Rules Revert
- The element no longer matches the
.dropdown.is-open .dropdown__drawer
selector. - It reverts to its base style defined in
.dropdown__drawer
. - The target state for the drawer is now:
opacity: 0
visibility: hidden
- The transition properties from the base rule are now used:
transition: visibility 0s linear 0.2s, opacity 0.2s linear;
Step 3: Transitions Kick In
The browser transitions the properties from the visible
state back to the hidden
state.
Immediately (at 0 seconds):
- The
opacity
transition begins. It has a duration of 0.2s and a delay of 0s. The menu starts to fade from 1 (opaque) towards 0 (transparent). - The
visibility
property does not change yet. It has a transition-delay of 0.2s, so it waits. The menu remains visible and interactive while it's fading out.
- The
From 0 to 0.2 seconds:
- The
opacity
continues to animate from 1 down to 0. The menu smoothly fades out.
- The
At 0.2 seconds:
- The
opacity
transition is complete. The menu is now fully transparent (opacity: 0
). - The 0.2s delay on the
visibility
transition is over. Thevisibility
property instantly switches fromvisible
tohidden
. The element is removed from the document's visual flow and is no longer interactive.
- The
Summary of Closing Effect: The menu smoothly fades out over 0.2 seconds. Crucially, it only gets set to visibility: hidden
after the fade-out animation is complete. This allows the user to see the entire fade-out effect. If visibility
changed instantly, the menu would disappear abruptly, and the opacity
transition would not be seen.
扩展学习
- Transition Dropdown Menu CSS
- immediately invoked function expression (IIFE)
过渡到自动高度
代码不起作用是因为一个值不能从长度 0
过渡到 auto
,因为不知道渲染以后真正的高度是多少。所以需要通过 JavaScript 来获取菜单精确的像素高度。
<body>
<div class="dropdown" aria-haspopup="true">
<button class="dropdown__toggle">Menu</button>
<div class="dropdown__drawer">
<ul class="menu" role="menu">
<li role="menuitem"><a href="/features">Features</a></li>
<li role="menuitem"><a href="/pricing">Pricing</a></li>
<li role="menuitem"><a href="/support">Support</a></li>
<li role="menuitem"><a href="/about">About</a></li>
</ul>
</div>
<p><a href="/read-more">Read more</a></p>
<script type="text/javascript">
(function () {
var toggle = document.getElementsByClassName("dropdown__toggle")[0];
var dropdown = toggle.parentElement;
var drawer = document.getElementsByClassName("dropdown__drawer")[0];
var height = drawer.scrollHeight;
toggle.addEventListener("click", function (e) {
e.preventDefault();
dropdown.classList.toggle("is-open");
if (dropdown.classList.contains("is-open")) {
drawer.style.setProperty("height", height + "px");
} else {
drawer.style.setProperty("height", "0");
}
});
})();
</script>
</div>
</body>