🚀 用Stylus + Flexbox:打造令人惊艳的现代网页(附源码)
🌟 请看效果图:
✨ 告别繁琐CSS,开启高效样式编写新纪元
🌟 开篇:当CSS遇见编程思维
想象一下这样的场景:
- 你是否曾因修改主题色而在几十个文件中来回切换?
- 是否被复杂的布局代码折磨得头晕眼花?
- 是否渴望像写JavaScript一样优雅地编写样式?
今天,让我们一起打破常规! 我将带你领略Stylus与Flexbox的完美结合,让你以前所未有的效率和优雅,打造出令人惊艳的响应式网页!
🎨 第一部分:Stylus - CSS的华丽蜕变
💫 重新定义样式编写
Stylus不是简单的工具,而是思维方式的升级:
- 🎯 极简语法:像写诗一样写样式,告别{ } ; 的束缚
- 🎨 设计系统化:用变量和函数构建可维护的设计体系
- 🔥 智能高效:嵌套、混合让代码复用变得轻而易举
🚀 三分钟快速上手
体验从传统到现代的飞跃:
- ⚡ 安装即用:一行命令开启全新世界
- 安装stylus插件,在vscode搜索框里下载stylus插件
npm i -g stylus全局安装stylus包
- 🔄 实时编译:代码变化即时呈现,开发体验满分
stylus style.styl -o style.css -w输入这个命令,输出compiled style.css和watching style.styl说明运行成功,进入监听模式,就可以实时的把.styl文件转换成.css文件,让浏览器解析。
- 🎪 玩法多样:支持多种编写风格,总有一款适合你
🏗️ 第二部分:Flexbox - 布局艺术的革命
💡 重新思考网页布局
Flexbox带来的不仅是技术,更是布局哲学:
- 🧩 直觉式布局:告别float和position的魔法数字
- 📱 移动端首选:天然适配多设备场景
- 🎭 弹性思维:让元素在容器中自由呼吸
🌈 五大核心应用场景
- ✨ 完美居中 - 一行代码实现水平垂直居中
- 在弹性布局里设置了两个自带的居中方法
justify-content: center水平居中,主轴对齐align-items: cente,垂直居中,侧轴对齐
- 在弹性布局里设置了两个自带的居中方法
body
display: flex
justify-content: center//水平居中
align-items: center//垂直居中
height: 100vh
flex-direction: column
overflow: hidden//超出就隐藏
- 效果如图,先将元素水平移到中间,在将元素垂直移到中间
- 📊 等分布局 - 智能分配空间,无需复杂计算
flex:将图片等分布局,其核心作用是让所有设置了flex: 1的子元素平均分配父容器的可用空间,具体拆解如下:- 所有图片容器平均分配父容器的宽度(或高度,取决于
flex-direction:row/cloums) :父容器.container总宽度为90vw,有 5 个.panel,则每个.panel初始宽度为90vw / 5 = 18vw(忽略边距margin的话)。 - 空间动态分配:当父容器尺寸变化(如窗口缩放),可用空间会自动重新分配,每个
.panel始终保持相同的比例。 flex: 1通过统一子元素的「拉伸 / 收缩比例」和「初始基准尺寸」,实现了弹性容器内子元素的等比例空间分配,让图片容器在父容器中始终保持均匀分布。
.container//父元素
display flex
justify-content flex-start
flex-direction row
width 90vw
.panel//.container的子元素
height 50vh
border-radius 50px
color #9e3333
// cursor pointer
// flex 1
margin 10px
position relative
background-size cover
background-position center
transition all 700ms ease-in
4. 🎯 顺序控制 - 自由调整元素显示顺序
- 以
.panel为例:.container是.panel的父容器,其所有子元素的order属性初始默认值均为 0。我们可以通过调整子元素的order值,来改变它们在布局中的排列顺序——数值越小的元素将排在越前面,所有子元素会按照order值从小到大的顺序重新排列 - 当我把第一个子元素的
order值设为 2,而其他子元素未设置(默认为 0),排序后,由于 0 小于 2,第一个元素便会排到所有默认元素之后,具体效果见下图
.container
display: flex
justify-content: flex-start
flex-direction: columns
width 90 vw
.panel:nth-child(1)
order: 2
- 改变
nth-child(1)的order值之前:
- 改变
nth-child(1)的order值之后:
5. 📐 对齐艺术 - 精细控制每个元素的位置
🎪 第三部分:交互体验的魔法时刻
⚡ 动态交互新体验
当静态样式遇见动态交互:
- 🎮 状态管理:用类名切换实现复杂交互效果
- 🌊 流畅动画:精心设计的过渡让用户体验升级
- 🎯 用户引导:通过视觉变化引导用户操作
🔗 JavaScript无缝集成
样式与逻辑的完美共舞:
🔥 交互逻辑深度解析:如何用少量JS实现精准的流向控制
各位掘友,在之前的分享中我们已经搭建了完美的CSS架构,今天重点聊聊 JavaScript 如何与 CSS 无缝配合,实现精致的交互效果。
💡 设计思路:最小化 JS,最大化效果
我的核心理念是:让 CSS 负责表现,让 JS 只做最关键的状态管理。下面这段代码虽然简短,但蕴含了不少设计巧思。
🎯 核心代码解析
const panels = document.querySelectorAll('.panel')
panels.forEach(function(panel){
panel.addEventListener('mouseover',function(){
const cur = document.querySelector('.active');
if(cur){
cur.classList.remove('active');
}
panel.classList.add('active')
})
})
🔍 技术细节深度剖析
1. 精准的 DOM 元素获取
document.querySelectorAll('.panel') 这里我特意使用了类选择器而非标签选择器,为什么?
设计考量:
- 🎯 更好的语义化:
.panel明确表达了这是一个 UI 组件 - 🔧 更高的可维护性:未来如果面板元素类型变化,无需修改 JS
- 📦 更强的复用性:任何元素只要添加
.panel类就能获得相同交互
2. 事件委托的取舍
很多掘友可能会问:为什么不用事件委托?这里我做了 刻意选择:
// 为什么不这样写?
container.addEventListener('mouseover', function(e) {
if(e.target.classList.contains('panel')) {
// ...逻辑处理
}
})
我的考量:
- ⚡ 性能平衡:面板数量固定且不多,直接绑定不会造成性能问题
- 🧩 逻辑清晰:每个面板独立管理自己的状态,符合组件化思想
- 🎪 避免事件冒泡的复杂性:减少
e.target和e.currentTarget的混淆
3. 状态管理的精妙设计
核心状态切换逻辑:
const cur = document.querySelector('.active');
if(cur){
cur.classList.remove('active');
}
panel.classList.add('active')
为什么这样设计?
- 🔄 原子性操作:先查询 → 再清除 → 最后设置,确保状态切换的完整性
- 🚀 性能优化:只在需要时才查询当前激活项,避免维护额外的状态变量
- 🎨 CSS 驱动:所有动画效果都在 CSS 中定义,JS 只负责状态切换,符合关注点分离原则
🚀 实际开发中的演进思考
第一版:基础功能
// 最简单的实现
panels.forEach(panel => {
panel.addEventListener('mouseover', () => {
panels.forEach(p => p.classList.remove('active'));
panel.classList.add('active');
});
});
第二版:性能优化
// 减少不必要的 DOM 操作
panels.forEach(panel => {
panel.addEventListener('mouseover', () => {
const activePanel = document.querySelector('.panel.active');
if(activePanel && activePanel !== panel) {
activePanel.classList.remove('active');
}
panel.classList.add('active');
});
});
最终版:代码健壮性
// 添加边界条件检查
panels.forEach(panel => {
panel.addEventListener('mouseover', function() {
const cur = document.querySelector('.active');
// 避免重复激活
if(cur === this) return;
if(cur){
cur.classList.remove('active');
}
this.classList.add('active');
});
});
💡 经验总结
1. CSS-in-JS 的另一种理解
我们不需要复杂的 CSS-in-JS 库,通过 classList API 就能实现样式与逻辑的完美结合。
2. 渐进增强的实践
即使 JavaScript 加载失败或执行出错,基础的 CSS 悬停效果仍然可用,这是真正的渐进增强。
3. 性能与可读性的平衡
在代码可读性和性能之间找到平衡点,避免过度优化。
🎯 进一步优化的思考
如果项目需要更复杂的交互,我会考虑:
- 加入触摸事件支持:
touchstart移动端适配 - 键盘导航:方向键切换面板
- 动画生命周期:在动画开始/结束时执行特定逻辑
🤔 互动讨论
掘友们,你们在项目中是如何处理类似交互的?有没有更好的实现思路?欢迎在评论区交流讨论!
如果觉得这篇文章对你有帮助,请点赞 👍 收藏 ⭐️ 关注 👏 三连支持一下!
你的支持是我持续分享的动力!
记住:优秀的开发者不是写更多代码,而是用更优雅的方式解决问题。 Stylys和Flexbox就是你通往优雅开发之路的通行证!
准备好了吗? 让我们开始这段奇妙的旅程,一起探索现代前端开发的无限可能! 🎊
- 源码
const panels = document.querySelectorAll('.panel')
// console.log(panels,
// panels[0],
// typeof panels[0],
// Object.prototype.toString.call(panels[0])
// )
panels.forEach(function(panel){
//事件监听需要在具体元素上
panel.addEventListener('mouseover',function(){
//点击当前项,添加active类名
const cur = document.querySelector('.active');
if(cur){
cur.classList.remove('active');
}
panel.classList.add('active')
})})
*
margin 0
padding 0
.inner
color #000
body
display: flex
justify-content: center//水平居中
align-items: center//垂直居中
height: 100vh
flex-direction: column
overflow: hidden//超出就隐藏
.container
display: flex
justify-content: flex-start
flex-direction: columns
width 90 vw
.panel
height 50vh
border-radius: 50px
color#fff
// cursor pointer
flex 1
margin 10px
position relative
background-size: cover
background-position: center
transition: all 700ms ease-in
h3
font-size 24px
position absolute//绝对定位
left: 20px
right: 20px
bottom: 0
opacity 0
transition: opacity 300ms ease-in 400ms
&.active
flex: 6
h3
opacity 1
// .panel:nth-child(1)
// order: 2
@media(max-width: 500px){
.container{
width 100vw
}
.panel:nth-of-type(4),
.panel:nth-of-type(5){
display: none
}
}
* {
margin: 0;
padding: 0;
}
.inner {
color: #000;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
overflow: hidden;
}
.container {
display: flex;
justify-content: flex-start;
flex-direction: columns;
width: 90vw;
}
.container .panel {
height: 50vh;
border-radius: 50px;
color: #fff;
flex: 1;
margin: 10px;
position: relative;
background-size: cover;
background-position: center;
transition: all 700ms ease-in;
}
.container .panel h3 {
font-size: 24px;
position: absolute;
left: 20px;
right: 20px;
bottom: 0;
opacity: 0;
transition: opacity 300ms ease-in 400ms;
}
.container .panel.active {
flex: 6;
}
.container .panel.active h3 {
opacity: 1;
}
@media (max-width: 500px) {
.container {
width: 100vw;
}
.panel:nth-of-type(4),
.panel:nth-of-type(5) {
display: none;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CARDS</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 1</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 2</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 3</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 4</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 5</h3>
</div>
</div>
<script src="./common.js"></script>
</body>
</html>