从原生 CSS 到 Stylus:用弹性布局实现交互式图片面板

作为前端开发者,我们常常需要在代码效率和视觉效果之间找到平衡。今天我想分享一个基于弹性布局 (Flexbox) 的交互式图片面板实现,并且会对比原生 CSSStylus 预处理器的写法差异,看看 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的作用:

  1. 视觉上的初始状态

    页面加载完成后,用户第一眼看到的界面需要有一个 "突出显示" 的面板(第一个面板更宽、标题更明显),告诉用户 "这是当前选中的状态"。active 类就是通过 CSS 来实现这种视觉差异的( style.css 里有 .panel.active 的样式定义)。

  2. 配合 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: 0padding: 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 的优势显而易见:

  1. 简洁的语法 :去掉了{};,代码更清爽,减少冗余输入
  2. 嵌套结构:通过缩进表示层级关系,更符合 HTML 的结构逻辑,避免重复书写父选择器
  3. 父选择器引用 :使用&轻松表示父选择器,处理伪类、状态类更方便(如&.active
  4. 编程能力:支持变量、函数、混合等特性(本次示例未完全展示),适合大型项目

编译后的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;
  1. 面板尺寸变化:transition: all 700ms ease-in

    • all:所有属性变化都应用过渡
    • 700ms:过渡持续时间
    • ease-in:过渡速度曲线(先慢后快)
  2. 标题显示延迟:transition: opacity 300ms ease-in 400ms

    • 只针对opacity属性
    • 持续 300ms,延迟 400ms 执行(等面板展开后再显示标题)

四.总结

通过这个交互式图片面板的实现,我们不仅掌握了弹性布局的核心用法,还体验了 Stylus 预处理器带来的开发效率提升。关键知识点包括:

  • 弹性布局的容器与项目关系,主轴与交叉轴的对齐方式
  • 利用flex属性实现元素的空间分配
  • 过渡动画让交互更流畅自然
  • 媒体查询实现响应式布局
  • Stylus 的简洁语法和嵌套结构提升代码可维护性
相关推荐
Zyx20075 小时前
Stylus 进阶:从“能用”到“精通”,打造企业级 CSS 架构(下篇)
前端·css
黄毛火烧雪下5 小时前
Angular 入门项目
前端·angular
用户4099322502125 小时前
快速入门Vue3,插值、动态绑定和避坑技巧你都搞懂了吗?
前端·ai编程·trae
CondorHero5 小时前
Environment 源码解读
前端
残冬醉离殇5 小时前
别再傻傻分不清!从axios、ElementPlus深入理解SDK与API的区别
前端
CodeSheep5 小时前
稚晖君官宣,全球首个0代码机器人创作平台来了!
前端·后端·程序员
向上的车轮5 小时前
Actix Web 入门与实战
前端·rust·actix web
Mintopia6 小时前
🧬 生物识别 + AIGC:Web端个性化服务的技术协同路径
前端·javascript·aigc
Mintopia6 小时前
🧠 Next.js 安全防线:从 CSRF 到 XSS 的黑魔法防护 🌐⚔️
前端·javascript·全栈