引言
今天在CodePen
[codepen.io] 上面发现了一个使用css实现的一个手风琴效果, 感觉蛮有意思的,于是自己尝试了一下,发现不是特别难, 在编码前把思路整理好,再去实现,就会发现轻松许多.
先看一下效果吧,大家提前玩玩,然后我们在一步一步去实现这个效果. 本文会将一些基础的css顺带讲解到, 并将一些写css相关的设计网站资源进行分享.
效果:
实现
盒子背景是一个渐变色
. 并且宽和高是浏览器视口的宽高
,那么我们就可以编写我们的css代码.然后手风琴的盒子相对于浏览器视口是水平垂直居
中的
- 子元素相对父元素水平垂直居中,可以使用
flex布局
. 关于flex 的详细讲解可以查看这篇文章,通过图片和代码的结合,很容易理解. ==> juejin.cn/post/720689…. - 渐变颜色 , 推荐一个网站,里面有很多预设渐变颜色,而且很好看 ==> www.grabient.com/
- 浏览器视口的宽高 用 c3新增的宽高大小
vh
和vw
,- 1vh 相当于 浏览器视口高度的1%
- 1vw 相当于 浏览器视口宽度的1%
代码语言:javascript
复制
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
/* 子元素水平垂直居中 */
display: flex;
justify-content: center;
align-items: center;
/* 浏览器视口的宽高 */
width: 100vw;
height: 100vh;
/* 渐变 */
background-color: #8EC5FC;
background-image: linear-gradient(62deg, #8EC5FC 0%, #E0C3FC 100%);
}</code></pre></div></div><p>这时候打开页面是这样的.</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:100%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1723207223393652621.webp" /></div></div></div></figure><p>然后我们接着来, 4张图片位于一个大盒子里面.</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:100%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1723207223843941003.webp" /></div></div></div></figure><ul class="ul-level-0"><li>active 类用来将图片撑开的, 后面会通过js 动态添加 和 删除</li><li>--url 是我们定义的一个css变量,这里定义的话 值就是每个图片在目录的相对路径</li></ul><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"> <div class="contain">
<div class="option active" style="--url:url(./image/1.jpg);" id="item1">
</div>
<div class="option" style="--url:url(./image/2.jpg);" id="item2">
</div>
<div class="option" style="--url:url(./image/3.jpg);" id="item3">
</div>
<div class="option" style="--url:url(./image/4.jpg);" id="item4">
</div>
</div></code></pre></div></div><p>然后编写css代码</p><ul class="ul-level-0"><li>默认图片(也就是<code>option</code> 类)的盒子宽度是很小的, 然后图片居中,平铺,不重复.</li><li>添加.active 类的话, 图片盒子的宽度会<code>自动撑开外面大盒子剩余空间</code> ==> <code>flex-grow</code>属性</li></ul><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"> body .contain .option {
min-width: 80px;
margin: 10px;
background: var(--url);
/* 图片覆盖 平铺*/
background-size: cover;
/* 图片位置 */
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
border-radius: 10px;
transition: 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95);
}
.active {
flex-grow: 10;
}</code></pre></div></div><p><code>flex-grow</code> 是一个CSS属性,用于指定Flexbox布局中flex子项的增长因子。当父容器的空间大于子项所需的总空间时,<code>flex-grow</code> 决定了如何分配额外的空间。</p><ul class="ul-level-0"><li><code>flex-grow</code> 的值是一个正数,表示子项的增长能力。</li><li>如果所有子项的 <code>flex-grow</code> 总和为1,那么每个子项将根据其 <code>flex-grow</code> 值占剩余空间的比例增长。</li><li>如果 <code>flex-grow</code> 值大于1,子项将尝试占据更多空间,相对于 <code>flex-grow</code> 值较小的子项。</li></ul><p>例如,如果有三个子项,它们的 <code>flex-grow</code> 分别为1、2和3,那么当父容器有额外空间时,第二个子项将尝试占据第一个子项两倍的空间,第三个子项将尝试占据第一个子项三倍的空间。</p><blockquote><p> 在该页面中,主要的作用就是,设置active类的元素, 即设置flex-grow 的元素的宽度将会自动占据父容器余下的空间.
前面的铺垫已经做好了. 剩下就该js出手了.主要需要完成的事情有:
- 点击哪个盒子,哪个盒子添加
.active
类, 其他元素如果有.active
,就要移除.
这里我们可以采取事件委托的方式处理点击事件.
事件委托是一种事件处理模式,其中一个事件监听器被添加到父元素上,用于处理其子元素的事件。这样做的好处包括:
- 减少内存使用:不需要为每个子元素单独添加事件监听器,只需为父元素添加一个即可。
- 动态内容:如果子元素是动态添加或删除的(例如通过用户交互或Ajax),事件委托仍然有效,因为新的子元素会自动继承父元素的事件监听器。
- 简化代码:事件委托可以减少代码的复杂性,因为你不需要管理多个事件监听器
代码语言:javascript复制 const contain = document.querySelector('.contain') // 手风琴盒子
const optionList = document.querySelectorAll('.option') // 图片盒子类数组
contain.addEventListener('click', (e) => {
// 当e.target 是.option的时候,就会触发,否则不会触发
if (e.target.classList.contains('option')) {
optionList.forEach(el => {
el.classList.remove('active');
});
e.target.classList.add('active');
e.stopPropagation(); // 阻止事件冒泡
}
});</code></pre></div></div><h3 id="971436" name="%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81">完整代码</h3><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
/* 子元素水平垂直居中 */
display: flex;
justify-content: center;
align-items: center;
/* 浏览器视口的宽高 */
width: 100vw;
height: 100vh;
/* 渐变 */
background-color: #8EC5FC;
background-image: linear-gradient(62deg, #8EC5FC 0%, #E0C3FC 100%);
}
body .contain {
display: flex;
width: 800px;
height: 400px;
/* 子元素的间距 */
}
body .contain .option {
min-width: 80px;
margin: 10px;
background: var(--url);
/* 图片覆盖 平铺*/
background-size: cover;
/* 图片位置 */
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
border-radius: 10px;
transition: 0.5s cubic-bezier(0.05, 0.61, 0.41, 0.95);
}
.active {
flex-grow: 10;
}
</style>
</head>
<body>
<div class="contain">
<div class="option active" style="--url:url(./image/1.jpg);" id="item1">
</div>
<div class="option" style="--url:url(./image/2.jpg);" id="item2">
</div>
<div class="option" style="--url:url(./image/3.jpg);" id="item3">
</div>
<div class="option" style="--url:url(./image/4.jpg);" id="item4">
</div>
</div>
<script>
const contain = document.querySelector('.contain')
const optionList = document.querySelectorAll('.option')
contain.addEventListener('click', (e) => {
// 当e.target 是.option的时候,就会触发,否则不会触发
if (e.target.classList.contains('option')) {
optionList.forEach(el => {
el.classList.remove('active');
});
e.target.classList.add('active');
e.stopPropagation(); // 阻止事件冒泡
}
});
</script>
</body>
</html>