作为前端开发者,我们常常需要在代码效率和视觉效果之间找到平衡。今天我想分享一个基于弹性布局 (Flexbox) 的交互式图片面板实现,并且会对比原生 CSS 与 Stylus 预处理器的写法差异,看看 Stylus 如何让我们的样式代码更优雅、更高效。
效果展示
先来看最终实现的效果:一个包含多张图片的面板组件,默认显示一个突出的主面板,点击任意面板时,该面板会平滑过渡到突出状态,同时显示标题文字,其他面板则收缩。在移动设备上会自动适配,只显示部分面板避免拥挤。

核心技术栈
- HTML5 结构搭建
- 弹性布局 (Flexbox) 实现响应式排列
- CSS 过渡 (transition) 实现平滑动画
- Stylus 预处理器 提升开发效率
一. HTML 结构设计
首先搭建基础 HTML 结构,使用语义化标签组织内容:
html
<body>
<div class="container">
<div class="panel active" style="background-image: url('https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
<h3>Explore The World</h3>
</div>
<div class="panel" style="background-image: url('')">
<h3>Wild Forest</h3>
</div>
<div class="panel" style="background-image: url('')">
<h3>Sunny Beach</h3>
</div>
<div class="panel" style="background-image: url('')">
<h3>City on Winter</h3>
</div>
<div class="panel" style="background-image: url('')">
<h3>Mountains - Clouds</h3>
</div>
</div>
<script src="./common.js"></script>
</body>
active的作用:
-
视觉上的初始状态
页面加载完成后,用户第一眼看到的界面需要有一个 "突出显示" 的面板(第一个面板更宽、标题更明显),告诉用户 "这是当前选中的状态"。
active类就是通过 CSS 来实现这种视觉差异的(style.css里有.panel.active的样式定义)。 -
配合 JavaScript 的逻辑
后续 JS 代码逻辑是 "点击某个面板时,先移除其他面板的
active类,再给当前面板添加active类"。如果一开始所有面板都没有active类,那么页面刚打开时,所有面板都会是 "普通状态",没有任何突出的面板,体验不够好。
二. JavaScript 交互逻辑
实现点击切换功能的核心逻辑:
javascript
// document 文档对象 顶级的 dom树
// dom 事件监听
const panels = document.querySelectorAll('.panel');
panels.forEach(function(panel){
// 事件的监听需要在具体的元素上
panel.addEventListener('click',function(){
// 第一步:找到当前已经激活的面板
const cur = document.querySelector('.active');
//第二步:如果存在激活的面板,就取消它的激活状态
if(cur){
cur.classList.remove('active');
}
// 第三步:给当前点击的面板添加激活状态
panel.classList.add('active');
})
})
document.querySelectorAll('.panel'):在页面中查找所有class 名为panel的元素,然后把找到的这些元素存到变量panels里,panels就像一个 "列表",里面装着所有符合条件的面板。panels.forEach(...):遍历panels列表里的每个面板,对每个面板执行后面的函数。panel.addEventListener('click', function(){ ... }): 给当前这个panel(面板)添加一个 "点击事件监听":当用户点击这个面板时,就会执行后面的函数。.active是一个类名(在 CSS 里定义了激活状态的样式)。
三. 样式实现:从 CSS 到 Stylus
1. 基础重置:* { margin: 0; padding: 0; }
*代表页面上的所有元素(比如 div、h3、body 等)。margin: 0和padding: 0:清除所有元素默认的 "外边距" 和 "内边距"。(浏览器会给元素自带一些默认间距,比如 h3 标签会有上下边距,这段代码能让页面布局更可控,不受默认样式干扰。)
2. 页面整体布局:body { ... }
Stylus 优化实现
首先确保已安装 Stylus:
bash
npm i -g stylus
编译 Stylus 为 CSS:
bash
# 单次编译
stylus style.styl -o style.css
# 实时监听编译(开发时推荐)
stylus style.styl -o style.css -w
Stylus 带来的优势
对比两种写法,Stylus 的优势显而易见:
- 简洁的语法 :去掉了
{}和;,代码更清爽,减少冗余输入 - 嵌套结构:通过缩进表示层级关系,更符合 HTML 的结构逻辑,避免重复书写父选择器
- 父选择器引用 :使用
&轻松表示父选择器,处理伪类、状态类更方便(如&.active) - 编程能力:支持变量、函数、混合等特性(本次示例未完全展示),适合大型项目
编译后的CSS:
css
body {
display: flex; /* 启用弹性布局 */
flex-direction: row; /* 子元素水平排列(默认值,可省略) */
justify-content: center; /* 子元素在水平方向居中 */
align-items: center; /* 子元素在垂直方向居中 */
height: 100vh; /* 页面高度等于屏幕高度(100%视窗高度) */
overflow: hidden; /* 隐藏页面滚动条,防止内容超出屏幕时滚动 */
}
- 作用:让整个页面的内容(也就是
.container容器)居中显示在屏幕正中间,且占满整个屏幕,没有滚动条。
1. display: flex;
-
作用 :将元素定义为弹性容器,其直接子元素(比如你代码中的
.container)自动成为弹性项。 -
注意:
- 弹性容器的子元素默认不再遵循传统的块级 / 行内元素布局规则(比如块级元素默认占满一行的特性会失效)。
- 弹性容器会 "包裹" 子元素,但默认不会换行(子元素会尽量挤在一行)。
2. flex-direction: row;(决定主轴方向)
Flex 布局有两根轴:主轴 (main axis)和交叉轴 (cross axis),交叉轴永远垂直于主轴。flex-direction 用来设置主轴的方向,决定子元素的排列方向。
-
row(默认值):主轴为水平方向,子元素从左到右排列(你的代码用的就是这个)。 -
column:主轴为垂直方向,子元素从上到下排列。
3. justify-content: center;(主轴对齐方式)
控制弹性项在主轴 上的对齐方式(关键:主轴方向由 flex-direction 决定)。
常用取值:
-
flex-start(默认):子元素靠主轴起点对齐(比如row方向就是靠左)。 -
center:子元素在主轴上居中对齐 (你的代码用的这个,所以.container在页面水平方向居中)。
4. align-items: center;(交叉轴对齐方式)
控制弹性项在交叉轴上的对齐方式(交叉轴方向与主轴垂直)。
常用取值:
stretch(默认):子元素拉伸至填满交叉轴方向的容器高度(如果子元素没设置高度,会占满容器高度)。center:子元素在交叉轴上居中对齐 (你的代码用的这个,所以.container在页面垂直方向居中)。
5. 容器样式:.container { ... }
css
.container {
display: flex; /* 容器内的面板水平排列(弹性布局) */
width: 90vw; /* 容器宽度是屏幕宽度的90%(vw是视窗宽度单位) */
}
- 作用:让 5 个图片面板在容器内并排显示,容器整体宽度占屏幕的 90%(左右留一点空隙)。
6. 普通面板样式:.container .panel { ... }
css
.container .panel {
height: 80vh; /* 面板高度是屏幕高度的80% */
border-radius: 50px; /* 面板边角圆角(让 corners 圆润) */
color: #fff; /* 文字颜色为白色(配合图片背景更清晰) */
cursor: pointer; /* 鼠标移上去显示"手型"光标,提示可点击 */
flex: 0.5; /* 弹性比例:每个面板默认占相同的小比例(初始状态都比较窄) */
margin: 10px; /* 面板之间的间距(左右各10px,所以相邻面板间距20px) */
position: relative; /* 相对定位:为了让内部的h3标题用绝对定位(相对于面板定位) */
background-size: cover; /* 背景图拉伸/缩放至覆盖整个面板(不拉伸变形) */
background-position: center; /* 背景图居中显示 */
background-repeat: no-repeat; /* 背景图不重复平铺 */
transition: all 700ms ease-in; /* 所有样式变化时,用700毫秒(0.7秒)平滑过渡 */
}
- 作用:定义了每个图片面板的默认样式 ------ 窄窄的、圆角、白色文字、背景图居中铺满,且鼠标移上去能点击。
7. 面板标题样式:.container .panel h3 { ... }
css
.container .panel h3 {
font-size: 24px; /* 标题文字大小 */
position: absolute; /* 绝对定位:相对于面板(父元素)定位 */
left: 20px; /* 距离面板左边20px */
bottom: 20px; /* 距离面板底部20px */
margin: 0; /* 清除h3默认的边距 */
opacity: 0; /* 初始状态完全透明(看不见) */
transition: opacity 300ms ease-in 400ms; /* 透明度变化时,300毫秒过渡,延迟400毫秒执行 */
}
- 作用:标题默认在面板左下角,但完全隐藏(透明),只有面板激活时才显示。
8. 激活状态的面板样式:.container .panel.active { ... }
css
.container .panel.active {
flex: 5; /* 弹性比例变大:激活的面板会占据更多宽度(比默认的0.5大很多) */
}
- 作用:这是核心!当面板被点击(JS 添加
active类)时,它的宽度会大幅增加(从窄变宽),突出显示。
9. 激活状态的标题样式:.container .panel.active h3 { ... }
css
.container .panel.active h3 {
opacity: 1; /* 完全不透明(显示标题) */
}
- 作用:面板激活时,标题从透明变为可见(配合前面的过渡动画,会慢慢显示出来)。
10. 响应式布局(小屏幕适配):@media (max-width: 480px) { ... }
css
@media (max-width: 480px) { /* 当屏幕宽度小于等于480px(手机屏幕)时生效 */
.container {
width: 100vw; /* 容器占满整个屏幕宽度 */
}
.panel:nth-of-type(4),
.panel:nth-of-type(5) { /* 第4个和第5个面板 */
display: none; /* 隐藏,只显示前3个,避免手机上太挤 */
}
}
- 作用:适配手机等小屏幕设备,避免面板太多挤在一起,只显示前 3 个,提升体验。
11. 过渡动画 (transition) 详解
案例中使用了两处过渡动画:
css
transition: opacity 300ms ease-in ;
transition: opacity 300ms ease-in 400ms;
-
面板尺寸变化:
transition: all 700ms ease-inall:所有属性变化都应用过渡700ms:过渡持续时间ease-in:过渡速度曲线(先慢后快)
-
标题显示延迟:
transition: opacity 300ms ease-in 400ms- 只针对
opacity属性 - 持续 300ms,延迟 400ms 执行(等面板展开后再显示标题)
- 只针对
四.总结
通过这个交互式图片面板的实现,我们不仅掌握了弹性布局的核心用法,还体验了 Stylus 预处理器带来的开发效率提升。关键知识点包括:
- 弹性布局的容器与项目关系,主轴与交叉轴的对齐方式
- 利用
flex属性实现元素的空间分配 - 过渡动画让交互更流畅自然
- 媒体查询实现响应式布局
- Stylus 的简洁语法和嵌套结构提升代码可维护性