前言:点击一下,世界就为你“炸”开?
你有没有见过这样的网页——五张美到窒息的风景图并排而立,仿佛在安静地等待一个信号。
然后,你轻轻一点,其中一张突然“呼吸”起来,猛地扩张,霸占视野,标题缓缓浮现,仿佛在说:“终于等到你,我的主角。”
欢迎来到 “探索世界”(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');
})
})
逐行解析:
document.querySelectorAll('.panel'):获取页面中所有.panel元素,返回一个类似数组的 NodeList。forEach:遍历每个卡片元素。addEventListener('click', ...):为每个卡片绑定“点击”事件。document.querySelector('.active'):查找当前带有.active类的卡片(如果有)。classList.remove('active'):如果存在激活项,先移除其激活状态,确保同一时间只有一个卡片是放大的。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 的加持,让样式代码更加可维护、可复用、更具编程性。
这不仅仅是一个“点击放大”的效果,它是一种设计思维:
让用户每一次点击,都像推开一扇门,进入一个全新的世界。
代码很短,效果很炸。
现在,轮到你了——
是继续看别人的世界,还是用这三行代码,打造属于你的“爆炸式”交互? 🌄💥