前言
大家好啊!今天我要和大家分享一个非常酷炫的前端小项目 —— 展开卡片效果。这个项目来自 GitHub 上超火的"50 Projects in 50 Days"系列。看起来简单,但里面却暗藏玄机,包含了很多前端开发的精髓。让我们一起来扒一扒这个项目背后的故事!
初见效果:这不就是几张卡片吗?
乍一看,这就是五张排列在一起的图片卡片。但是!当你点击任意一张卡片时,魔法就开始了 —— 被点击的卡片会优雅地展开,同时显示出隐藏的标题,而其他卡片则会自动收缩。这种流畅的动画效果,就像变魔术一样,让人忍不住想多点几下!
解剖项目:原来你是这样的骨架!
1. HTML:BEM命名法的艺术
<div class="container">
<div class="qq-panel qq-panel--active">
<h3 class="qq-panel__title">Explore The World</h3>
</div>
</div>
看到这个HTML结构,有经验的开发者会立刻认出:这是BEM命名法!如果你还不知道BEM,那就让我用一个生动的比喻来解释:
想象你在组织一个家庭聚会(Block),家里有爸爸、妈妈、孩子(Element),他们可能在看电视、做饭或者睡觉(Modifier)。在BEM中:
- Block(块):就像"qq-panel",是一个独立的组件
- Element(元素):像"qq-panel__title",是组件中的子元素
- Modifier(修饰符):比如"qq-panel--active",表示组件的特定状态
2. CSS:Flex布局的艺术
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
}
这段CSS代码简直就是居中布局的艺术!它用Flex布局轻松实现了完美居中,就像是把一幅画挂在墙正中央一样精确。让我们来解读一下这些神奇的属性:
display: flex:开启了flex布局模式,就像打开了一个新世界的大门align-items: center:垂直居中,就像电梯停在正中间的楼层justify-content: center:水平居中,就像把沙发放在客厅正中央height: 100vh:使用视口高度,确保在任何设备上都能占满整个屏幕(因为我们可能在不同设备打开,这个就保证沾满你的设备的屏幕)
align-items: center和justify-content: center 是基于display: flex生效的,一但没有弹性布局这个属性,这两个属性不会生效,在子元素生效,让子元素居中
3. 弹性布局的艺术
.container {
display: flex;
width: 90vw;
}
.qq-panel {
flex: 0.5;
transition: all 700ms ease-in;
}
.qq-panel--active {
flex: 5;
}
这里用到了Flex布局的一个绝妙特性:flex属性!它就像是在分蛋糕,默认情况下每个面板占据0.5份,当被激活时突然变成了5份!这种动态变化配合transition过渡效果,简直就是视觉享受。
display: flex;当有子元素有很多的情况下, 父元素和子元素们之间的布局方案 子元素们不会换行 也就是像上图一样在一行显示
4. 动画效果的艺术
.qq-panel__title {
opacity: 0;
}
.qq-panel--active .qq-panel__title {
opacity: 1;
transition: opacity 0.3s ease-in 0.4s;
}
这段代码展示了前端动画的精髓。标题默认是隐藏的(opacity: 0),只有在面板被激活时才渐渐显示出来。特别注意那个0.4s的延迟,这就像是让标题演员等待正确的出场时机,让整个动画效果更加自然流畅。
5. JavaScript:交互的艺术
const panels = document.querySelectorAll('.qq-panel')
panels.forEach(panel => {
panel.addEventListener('click', () => {
removeActiveClasses()
panel.classList.add('qq-panel--active')
})
})
这段JavaScript代码看似简单,但处理用户交互的方式非常优雅。点击后,我们就把这个类加到点击元素上面,它就像一个训练有素的舞台监督:当观众(用户)点击某个演员(面板)时,首先让所有演员回到基础位置,然后让被选中的演员站到聚光灯下。
完整代码
html部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="common.css" />
<title>Expanding Cards</title>
</head>
<body>
<div class="container">
<div class="qq-panel qq-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 class="qq-panel__title">Explore The World</h3>
</div>
<div class="qq-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 class="qq-panel__title">Wild Forest</h3>
</div>
<div class="qq-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 class="qq-panel__title">Sunny Beach</h3>
</div>
<div class="qq-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 class="qq-panel__title">City on Winter</h3>
</div>
<div class="qq-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 class="qq-panel__title">Mountains - Clouds</h3>
</div>
</div>
<script src="common.js"></script>
</body>
</html>
css部分
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
/* 弹性布局 pad 手机 尺寸不同 布局 flex */
display: flex;
/* 垂直方向居中 */
align-items: center;
/* 水平方向居中 */
justify-content: center;
/* 相对单位 view height 一屏高度100vh,等比例划分,一屏全部沾满*/
height: 100vh;
/* 超出隐藏 */
overflow: hidden;
}
.container {
/* 弹性布局 格式化上下文 */
display: flex;
/* 90% view width 视口宽度 */
width: 90vw;
}
.qq-panel {
height: 80vh;
border-radius: 50px;
color: #fff;
cursor: pointer;
margin: 10px;
position: relative;
flex: 0.5;
/* 过度效果 元素样式改变后 all代表某一个样式改变 700ms表示时间 ease-in 表示一个变换的速度 */
transition: all 700ms ease-in;
}
.qq-panel__title {
font-size: 24px;
position: absolute;
bottom: 20px;
left: 20px;
opacity: 0;
}
.qq-panel--active {
flex: 5;
}
.qq-panel--active .qq-panel__title {
opacity: 1;
/* 从不可见到可见花了0.3秒 在0.4秒钟慢慢出现 */
/* transition: all 700ms ease-in;我们上面展开时间用了0.7秒 */
transition: opacity 0.3s ease-in 0.4s;
}
js部分
const panels = document.querySelectorAll('.qq-panel')
panels.forEach(panel => {
panel.addEventListener('click', () => {
removeActiveClasses()
panel.classList.add('qq-panel--active')
})
})
function removeActiveClasses() {
panels.forEach(panel => {
panel.classList.remove('qq-panel--active')
})
}
响应式设计的艺术
这个项目在响应式设计上也很用心。使用vw(视口宽度)和vh(视口高度)这样的相对单位,确保在不同尺寸的设备上都能完美展示。这就像是一件能自动调整尺寸的魔法衣服,无论是谁穿都合身!
用户体验的艺术
整个项目处处体现了对用户体验的追求:
- 平滑的过渡动画让交互感觉自然
- 恰到好处的延迟让动画节奏感十足
- 简洁的界面设计不会让用户感到困惑
总结:前端开发就是艺术
这个看似简单的展开卡片效果,其实融合了现代前端开发的多个重要概念:
- BEM命名规范的结构组织
- Flex布局的灵活运用
- CSS过渡动画的精确控制
- JavaScript交互的优雅处理
- 响应式设计的周到考虑