JavaScript30 为Wes Bos推出的一项为期30天的挑战,旨在帮助人们用纯JavaScript来实现效果,初学者若想在JS方面快速精进,不妨一试。本题为第五题。
实现效果
点击任意一张图片,图片放大X倍,同时图片上下两方飞入文字,再点击已经展开的图片后,图片被压缩,同时顶端和底部的文字飞出。查看我的代码和demo。
页面基础布局
观察文档初始布局:在类名为.panels
的父元素div
之下,有5个类名为.panel
的子div
,这5个div
内均包含3个p
标签。文档内也已提供CSS
样式及动画,做题者只需稍许补充并添加监听事件即可。
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
......
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
复制代码
解题思路
CSS部分:
在本章中,大量运用flex
布局知识,如需补课,推荐查阅阮一峰大神的博客:
- 设置父元素也就是最大的容器
.panels
为display:flex
,默认项目沿主轴方向,从左到右排列; - 设置每个子元素即容器成员
.panel
为display:flex
,并设置属性:- 设置
flex: 1
,flex
为flex-grow
,flex-shrink
和flex-basis
的简写; - 设置
justify-content: center
,该属性规定项目在主轴上的对齐方式,center
即沿主轴居中排列。 - 设置
flex-direction: column
,该属性定义主轴的方向(即项目的排列方向),column
表示主轴为垂直方向,起点在上沿; - 设置
align-items: center
,即项目在交叉轴上居中对齐。
- 设置
- 设置点击图片后,文字的移动样式;
- 设置点击图片后,图片的
flex
值。
JS部分:
- 获取所有类名为
.panel
的元素; - 为其添加
click
监听事件,触发调用函数,添加或去除样式,实现图片拉伸或压缩的效果; - 为其添加
transitionend
监听事件,触发调用函数,添加或去除样式,实现顶端及底部文字飞入或飞出效果。
CSS部分代码
html {
box-sizing: border-box;
background: #ffc600;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.panels {
min-height: 100vh;
overflow: hidden;/*内容溢出元素框时,内容会被修剪,并且其余内容不可见 */
display: flex;
}
.panel {
background: #6B0F9C;
box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1);
color: white;
text-align: center;
align-items: center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
background 0.2s;
font-size: 20px;
background-size: cover;
background-position: center;
flex: 1;
justify-content: center;
display: flex;
flex-direction: column;
}
.panel1 {background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);}
......
.panel5 {background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500);
}
/* Flex Items */
.panel>* {
margin: 0;
width: 100%;
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
.panel>*:first-child {
transform: translateY(-100%);
}
.panel.open-active>*:first-child {
transform: translateY(0);
}
.panel>*:last-child {
transform: translateY(100%);
}
.panel.open-active>*:last-child {
transform: translateY(0);
}
.panel p {
text-transform: uppercase;
font-family: 'Amatic SC', cursive;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
flex: 5;
font-size: 40px;
}
复制代码
JavaScript完整代码:
const Panels = document.querySelectorAll('.panel');
function clearOpen() {
Panels.forEach(panel => panel.classList.remove('open'))
}
function toggelOpen() {
this.classList.toggle('open');
}
function toggleActive(e) {
if (e.propertyName.includes('flex')) {
this.classList.toggle('open-active')
}
}
Panels.forEach(panel => panel.addEventListener('click', toggelOpen));
Panels.forEach(panel => panel.addEventListener('transitionend', toggleActive));
复制代码
知识点
Flex布局:
采用 Flex 布局的元素,称为 Flex 容器。它的所有子元素自动成为容器成员,称为 Flex 项目。容器默认存在两根轴:水平的主轴main axis
和垂直的交叉轴cross axis
。
Flex容器属性:
flex-direction
属性决定主轴的方向(即项目的排列方向);flex-wrap
属性定义,如果一条轴线排不下,如何换行;flex-flow
属性是flex-direction
属性和flex-wrap
属性的简写形式;justify-content
属性定义了项目在主轴上的对齐方式。align-items
属性定义项目在交叉轴上如何对齐。align-content
属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
Flex项目属性:
order
属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。flex-grow
属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。flex-shrink
属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。flex-basis
属性定义了在分配多余空间之前,项目占据的主轴空间。flex
属性是flex-grow
,flex-shrink
和flex-basis
的简写,默认值为0 1 auto
。align-self
属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items
属性。
介绍完基础知识再看本题就清晰很多了,本题嵌套了三个Flex容器,分别为:
.panels
:使得子元素.panel
横向等分排列;.panel
:使得其中的<p>
纵向等分排列;p
:使得其中的文字垂直水平居中分布。 不过以上对Flex的介绍还不算详细,若想深入了解,可查阅这里。
translateY
为实现顶端及底部文字飞入飞出的效果,题中使用了一个用法,即transform: translateY,可实现元素在Y轴方向上移动。