“点我!我就炸给你看”——用Stylus和JS打造会呼吸的视觉魔法

92 阅读9分钟

前言:点击一下,世界就为你“炸”开?

你有没有见过这样的网页——五张美到窒息的风景图并排而立,仿佛在安静地等待一个信号。
然后,你轻轻一点,其中一张突然“呼吸”起来,猛地扩张,霸占视野,标题缓缓浮现,仿佛在说:“终于等到你,我的主角。”

欢迎来到 “探索世界”(Explore The World) 的视觉剧场!
这不是魔法,但比魔法更酷——这是 Stylus 的诗意编码JavaScript 的精准指挥 共同上演的一场交互协奏曲。

在这里,CSS 不再是呆板的样式表,而是会“呼吸”的动态艺术;JavaScript 也不是冰冷的逻辑,而是点燃交互的那根火柴。
接下来,我们将拆开这个小而美的作品,看看它是如何用 一行 flex、一个 click、一段 transition,让静态页面瞬间“活”过来的。

准备好了吗?点击即将开始,世界正在加载…… 🌍✨


一、项目概览与准备

项目概览:

  • 一个居中显示的横向卡片布局。
  • 五张背景图不同的卡片,分别代表“Wild Forest”、“Sunny Beach”、“City on Winter”、“Mountains - Clouds”等场景。
  • 默认状态下,所有卡片宽度相等。
  • 当你点击任意一张卡片时,它会瞬间“放大”,占据绝大部分空间,其他卡片则收缩变小。
  • 同时,被点击卡片底部的文字标题会从透明状态淡入显示。
  • 当你点击另一张卡片时,当前放大的卡片会收缩回去,新点击的卡片则放大,实现平滑的“焦点切换”效果。
  • 在小屏幕(如手机)上,页面自动适配,只显示前三张卡片。

这整个过程就像一个“呼吸”的界面,焦点在不同世界间流转,极具沉浸感!


项目准备:

首先是我们的项目结构,如图所示

与之前不同的是,项目结构中有一个我们比较陌生的文件——style.styl,这是我们今天的重点

 Stylus 是一门富有表现力的 CSS 预处理器,语法简洁优雅,支持变量、函数、混合(mixins)、嵌套等特性,能大幅提升 CSS 的编写效率和可维护性。

但是浏览器只能直接解析css,stylus是一种预处理器,所有需要编译成css

所以我们需要做以下准备:

先建一个style.styl文件,运行以下命令后,项目中会出现一个style.css文件,在style.styl中写样式时,Stylus会同步编译style.css文件

# 全局安装 Stylus
npm i -g stylus

# 编译 stylus 文件为 css
stylus style.styl -o style.css

# 边写边编译(开发利器)
stylus style.styl -o style.css -w

完成以上步骤后我们就可以开始写程序啦!


二、HTML 结构:一切的起点

首先是我们项目的起点——HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./style.css">
</head>
<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('https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
        <h3>Wild Forest</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80')">
        <h3>Sunny Beach</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1351&q=80')">
        <h3>City on Winter</h3>
      </div>
      <div class="panel" style="background-image: url('https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80')">
        <h3>Mountains - Clouds</h3>
      </div>

    </div>
    <script src="./common.js"></script>
</body>
</html>
  • .container:外层容器,包裹所有卡片。
  • .panel:每张卡片,通过 style="background-image: ..." 内联方式设置背景图。
  • <h3> :卡片上的文字标题。

三、CSS 样式内容及深度解析

样式内容

经过我们的项目准备后,书写一段 style.styl 文件内容,对应编译出的 style.css 文件内容如下:

* {
  margin: 0;
  padding: 0;
}
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  overflow: hidden;
}
.container {
  display: flex;
  width: 90vw;
}
.container .panel {
  height: 80vh;
  border-radius: 50px;
  color: #fff;
  cursor: pointer;
  flex: 0.5;
  margin: 10px;
  position: relative;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  transition: all 700ms ease-in;
}
.container .panel h3 {
  font-size: 24px;
  position: absolute;
  left: 20px;
  bottom: 20px;
  margin: 0;
  opacity: 0;
  transition: all 300ms ease-in 400ms;
}
.container .panel.active {
  flex: 5;
}
.container .panel.active h3 {
  opacity: 1;
}
@media (max-width: 480px) {
  .container {
    width: 100vw;
  }
  .panel:nth-of-type(4),
  .panel:nth-of-type(5) {
    display: none;
  }
}

深度解析

1. 全局重置
* {
  margin: 0;
  padding: 0;
}
  • 作用:清除所有元素的默认外边距和内边距,确保页面在不同浏览器下表现一致。

2. 页面整体布局(body
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  overflow: hidden;
}
  • display: flex; :开启弹性布局(Flexbox),这是现代布局的核心。
  • justify-content: center; :主轴(水平)居中。
  • align-items: center; :侧轴(垂直)居中。
    效果.container 整个容器在视口中水平垂直居中。
  • height: 100vh; :高度占满整个视口。
  • overflow: hidden; :隐藏溢出内容,防止布局变化时出现滚动条。

弹性布局详细解析请看文章:


3. 卡片容器(.container
.container {
  display: flex;
  width: 90vw;
}
  • display: flex; :子元素(.panel)将水平排列。
  • width: 90vw; :宽度占视口宽度的 90%,留出边距。

4. 卡片基础样式(.panel
.container .panel {
  height: 80vh;
  border-radius: 50px;
  color: #fff;
  cursor: pointer;
  flex: 0.5;
  margin: 10px;
  position: relative;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  transition: all 700ms ease-in;
}
  • height: 80vh; :高度为视口高度的 80%。
  • border-radius: 50px; :圆角,让卡片更柔和。
  • color: #fff; :文字白色,确保在深色背景上清晰可见。
  • cursor: pointer; :鼠标悬停时显示手型,提示可点击。
  • flex: 0.5; :这是关键!初始时每张卡片的 flex 值为 0.5,表示它们平等地“争夺”剩余空间,但权重较小。
  • margin: 10px; :卡片间留出空隙。
  • position: relative; :为内部的 <h3> 绝对定位提供参考。
  • background-* :背景图覆盖整个卡片,居中显示,不重复。
  • transition: all 700ms ease-in;过渡动画!任何属性变化都将在 700 毫秒内以“先慢后快”的缓动效果完成,让放大缩小如丝般顺滑。

5. 文字标题样式(.panel h3
.container .panel h3 {
  font-size: 24px;
  position: absolute;
  left: 20px;
  bottom: 20px;
  margin: 0;
  opacity: 0;
  transition: all 300ms ease-in 400ms;
}
  • position: absolute; :脱离文档流,定位在卡片左下角。
  • opacity: 0; :初始完全透明,不可见。
  • transition: all 300ms ease-in 400ms; :变化持续 300ms,缓动效果,并且延迟 400ms 执行。这确保了文字在卡片放大“稳定”后再淡入,节奏感十足。

6. 激活状态(.panel.active
.container .panel.active {
  flex: 5;
}
.container .panel.active h3 {
  opacity: 1;
}
  • flex: 5; :当卡片被点击并添加 .active 类后,其 flex 值从 0.5 变为 5,权重暴增,于是它“抢”走了绝大部分空间,实现放大效果。
  • opacity: 1; :文字标题变为完全不透明,配合过渡延迟,实现优雅的淡入。

7. 响应式设计(@media
@media (max-width: 480px) {
  .container {
    width: 100vw;
  }
  .panel:nth-of-type(4),
  .panel:nth-of-type(5) {
    display: none;
  }
}
  • 媒体查询:当屏幕宽度小于等于 480px(典型手机尺寸),应用此规则。
  • width: 100vw; :容器占满全屏。
  • display: none; :隐藏第 4 和第 5 张卡片,避免小屏幕上内容过于拥挤。

四、JavaScript 交互逻辑(common.js

JavaScript 的作用是监听用户点击,并动态切换 .active 类。

// 1. 获取所有 .panel 元素
const panels = document.querySelectorAll('.panel');

// 2. 遍历每个卡片
panels.forEach(function(panel) {
  // 3. 为每个卡片添加点击事件监听
  panel.addEventListener('click', function() {
    // 4. 查找当前已激活的卡片
    const cur = document.querySelector('.active');
    if (cur) {
        // 5. 如果存在,移除其 .active 类
        cur.classList.remove('active');
    }
    // 6. 给当前点击的卡片添加 .active 类
    panel.classList.add('active');
  })
})

逐行解析:

  1. document.querySelectorAll('.panel') :获取页面中所有 .panel 元素,返回一个类似数组的 NodeList。
  2. forEach:遍历每个卡片元素。
  3. addEventListener('click', ...) :为每个卡片绑定“点击”事件。
  4. document.querySelector('.active') :查找当前带有 .active 类的卡片(如果有)。
  5. classList.remove('active') :如果存在激活项,先移除其激活状态,确保同一时间只有一个卡片是放大的
  6. classList.add('active') :给当前点击的卡片加上 .active 类,触发 CSS 中定义的放大和文字显示效果。

五、Stylus 的潜力:让 CSS 更“聪明”

项目使用了 Stylus 预处理器。我们看到的是编译后的 CSS,原始style.styl文件内容如下

*
    margin 0
    padding 0

  body
    display flex
    justify-content center
    align-items center
    height 100vh
    overflow hidden

.container
    display flex
    width 90vw //vw等比例宽度单位
    .panel
        height 80vh
        border-radius 50px
        color #fff
        cursor pointer
        flex 0.5
        margin 10px
        position relative
        background-size cover
        background-position center
        background-repeat no-repeat
        transition all 700ms ease-in
        h3
            font-size 24px
            position absolute
            left 20px
            bottom 20px
            margin 0
            opacity 0
            transition all 300ms ease-in 400ms
          &.active
            flex 5
            h3 
              opacity 1

@media (max-width: 480px) {
    .container{
        width: 100vw
        }
    .panel:nth-of-type(4),
    .panel:nth-of-type(5){
        display: none;
        }
}

Stylus 带来了什么优势?

与编译完成后的 style.css 文件相比,style.styl 的内容更加简洁,Stylus 极大增强了 CSS 的编程能力

  • 变量(Variables) :颜色、尺寸、动画参数集中管理,修改一处,全局生效。
  • 嵌套(Nesting).panel 内部直接写 h3&.active,结构清晰,减少重复选择器。
  • 模块化:可将变量、混合等拆分成多个 .styl 文件,通过 @import 引入。
  • 自动前缀:Stylus 可配置自动为 CSS 属性添加浏览器前缀(如 -webkit-),无需手动处理兼容性。

一句话就是:Stylus 让 CSS 更像一门真正的编程语言,写起来更快、更聪明、更少出错。


六、总结:一场视觉与交互的协奏曲

HTML 是骨架,CSS 是皮肤,JavaScript 是心跳。这个“探索世界”项目完美诠释了 HTML + CSS + JavaScript 三剑客的协作:

  • HTML 搭建了内容骨架。
  • CSS (Stylus) 赋予了视觉美感和流畅的动画过渡。
  • JavaScript 注入了交互灵魂,让页面“活”了起来。

在这个项目里,我们看到的不只是五张图的切换,而是一次视觉与交互的完美共舞

  • 一个 flex: 0.5 到 flex: 5 的变化,让焦点瞬间突出;
  • 一段 transition 延迟动画,让标题如晨雾般缓缓升起;
  • 一行 addEventListener,就让整个页面有了“回应”的能力。

通过 flex 属性的动态变化和 transition 的巧妙运用,实现了极具张力的布局切换。而 Stylus 的加持,让样式代码更加可维护、可复用、更具编程性

这不仅仅是一个“点击放大”的效果,它是一种设计思维
让用户每一次点击,都像推开一扇门,进入一个全新的世界。

代码很短,效果很炸。
现在,轮到你了——
是继续看别人的世界,还是用这三行代码,打造属于你的“爆炸式”交互? 🌄💥