forked from soyaine/JavaScript30
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
660 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# 05 Flex 实现可伸缩的图片墙 中文指南 | ||
|
||
> 作者:©[缉熙Soyaine](https://github.com/soyaine) | ||
> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 5 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*) | ||
## 实现效果 | ||
|
||
 | ||
|
||
点击五张图片中的任意一张时,图片展开,同时图片中心的文字上下分别移入文字。点击已经展开的图片,图片被压缩,同时图片中上下两端的文字被挤走。若图片加载不出来[请点链接。](https://d17oy1vhnax1f7.cloudfront.net/items/3J2r2G0p0C0h0q2c3R3p/Screen%20recording%202016-12-30%20at%2005.33.01%20PM.gif) | ||
|
||
## 初始文档 | ||
|
||
首先描述一下文档的 DOM 结构:以 `.panels` 为父 `div` 之下,有 5 个类名为 `.panel` 的 `div`,这 5 个各含有 3 个子 `p` 标签。 | ||
|
||
``` | ||
.panels div | ||
├── .panel, .panel1 div | ||
| ├── p | ||
| ├── p | ||
| └── p | ||
├── .panel, .panel2 div | ||
| ├── p | ||
| ├── p | ||
| └── p | ||
├── .panel, .panel3 div | ||
| ├── p | ||
| ├── p | ||
| └── p | ||
├── .panel, .panel4 div | ||
| ├── p | ||
| ├── p | ||
| └── p | ||
└── .panel, .panel5 div | ||
├── p | ||
├── p | ||
└── p | ||
``` | ||
|
||
display: flex; | ||
|
||
border: 1px solid #f00; | ||
|
||
## 延伸思考 | ||
|
||
在 index-FINISHED.html 的解决方案中,用了两种 `class` 值来分别控制 `div` 元素和 `p` 元素的动画,这就会造成一个问题,当快速点击两下时,会出现相反的组合(图片缩小 + 上下文字出现)。 | ||
|
||
那为什么还要将文字的移动动画用 `.open-actived` 这个类来控制,同时还多加上了一个 `transitionend` 的事件监听,而不是直接用 `.open` 控制文字的移动,并且只采用一个 `click` 事件监听呢? | ||
|
||
我试了一下,发现如果将要触发的文字移动(`transform`)用 `.open` 来控制,那么会出现有点不协调的状况。 | ||
|
||
要找到问题所在,可以先研究一下动画效果,由于录 GIF 很容易掉帧,最好打开网页来看细节。 | ||
|
||
当拉伸图片时,首先往里压缩(阶段①),然后再展开(阶段②),而文字是阶段②出现的;而当压缩图片时,也是同样的道理,先微微拉开一点(阶段①),然后再往里缩(阶段②),文字也是在阶段②才往上移动的,这样就形成了一种被 pia 飞的效果。 | ||
|
||
这就解释了为什么我改动之后出现了不协调,此时看到的动画,像是文字主导了图片的压缩伸展,原因就是文字动画的时机不太对,找到了这个原因,就很好解决了。(见 [index-SOYAINE2.html]()) | ||
|
||
```css | ||
.panel > * { | ||
/* ... */ | ||
transition:transform 0.5s 0.7s; | ||
} | ||
|
||
.panel.open p:first-child { | ||
transform: translateY(0); | ||
} | ||
|
||
.panel.open p:last-child { | ||
transform: translateY(0); | ||
} | ||
``` | ||
|
||
```js | ||
const panels = document.querySelectorAll('.panel'); | ||
|
||
function toggleOpen(e) { | ||
this.classList.toggle('open'); | ||
} | ||
|
||
panels.forEach( panel => panel.addEventListener('click', toggleOpen, false)); | ||
``` | ||
|
||
让 `p` 标签的文字动画效果延迟一下,添加 `transition` 属性的 `delay` 值,使其到图片变换的阶段②再发生,此处我选用了图片的动画最长时间 0.7s,圆满解决。 | ||
|
||
这样也就可以回答我最开始的疑问,为何要多添加一个 [`transitioned` 的事件监听](https://developer.mozilla.org/zh-CN/docs/Web/Events/transitionend),这个事件会在 `transition` 结束之后被触发,所以目的是先让图片的压缩拉伸完成,再移动文字。 | ||
|
||
也就是说,如果出去字体大小的变化,具体的动画细节其实是这样的: | ||
- 图片展开:微微压缩一段距离 -> 展开图片 -> 文字向中心移动 | ||
- 图片压缩:微微展开一段距离 -> 压缩图片 -> 文字向上下移动 | ||
|
||
##### 挑战 5 Pass ~\(≧▽≦)/~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Flex Panels 💪</title> | ||
<link href='https://fonts.googleapis.com/css?family=Amatic+SC' rel='stylesheet' type='text/css'> | ||
</head> | ||
<body> | ||
<style> | ||
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); } | ||
.panel2 { background-image:url(https://source.unsplash.com/1CD3fd8kHnE/1500x1500); } | ||
.panel3 { background-image:url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); } | ||
.panel4 { background-image:url(https://source.unsplash.com/ITjiVXcwVng/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; | ||
} | ||
|
||
.cta { | ||
color:white; | ||
text-decoration: none; | ||
} | ||
|
||
</style> | ||
|
||
|
||
<div class="panels"> | ||
<div class="panel panel1"> | ||
<p>Hey</p> | ||
<p>Let's</p> | ||
<p>Dance</p> | ||
</div> | ||
<div class="panel panel2"> | ||
<p>Give</p> | ||
<p>Take</p> | ||
<p>Receive</p> | ||
</div> | ||
<div class="panel panel3"> | ||
<p>Experience</p> | ||
<p>It</p> | ||
<p>Today</p> | ||
</div> | ||
<div class="panel panel4"> | ||
<p>Give</p> | ||
<p>All</p> | ||
<p>You can</p> | ||
</div> | ||
<div class="panel panel5"> | ||
<p>Life</p> | ||
<p>In</p> | ||
<p>Motion</p> | ||
</div> | ||
</div> | ||
|
||
<script> | ||
const panels = document.querySelectorAll('.panel'); | ||
|
||
function toggleOpen() { | ||
console.log('Hello'); | ||
this.classList.toggle('open'); | ||
} | ||
|
||
function toggleActive(e) { | ||
console.log(e.propertyName); | ||
if (e.propertyName.includes('flex')) { | ||
this.classList.toggle('open-active'); | ||
} | ||
} | ||
|
||
panels.forEach(panel => panel.addEventListener('click', toggleOpen)); | ||
panels.forEach(panel => panel.addEventListener('transitionend', toggleActive)); | ||
</script> | ||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Flex Panels 💪</title> | ||
<link href='https://fonts.googleapis.com/css?family=Amatic+SC' rel='stylesheet' type='text/css'> | ||
</head> | ||
<body> | ||
<style> | ||
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; | ||
align-items: center; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
|
||
.panel1 { background-image:url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); } | ||
.panel2 { background-image:url(https://source.unsplash.com/1CD3fd8kHnE/1500x1500); } | ||
.panel3 { background-image:url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); } | ||
.panel4 { background-image:url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); } | ||
.panel5 { background-image:url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); } | ||
|
||
.panel > * { | ||
margin:0; | ||
width: 100%; | ||
transition:transform 0.5s; | ||
/* 帮助辨认元素所在的区域*/ | ||
/* border: 1px solid #f00;*/ | ||
flex: 1 0 auto; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
} | ||
|
||
.panel > *:first-child { | ||
transform: translateY(-100%); | ||
} | ||
|
||
.panel.open-actived > *:first-child { | ||
transform: translateY(0); | ||
} | ||
|
||
.panel > *:last-child { | ||
transform: translateY(100%); | ||
} | ||
|
||
.panel.open-actived > *: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; | ||
} | ||
|
||
.cta { | ||
color:white; | ||
text-decoration: none; | ||
} | ||
|
||
</style> | ||
|
||
|
||
<div class="panels"> | ||
<div class="panel panel1"> | ||
<p>Hey</p> | ||
<p>Let's</p> | ||
<p>Dance</p> | ||
</div> | ||
<div class="panel panel2"> | ||
<p>Give</p> | ||
<p>Take</p> | ||
<p>Receive</p> | ||
</div> | ||
<div class="panel panel3"> | ||
<p>Experience</p> | ||
<p>It</p> | ||
<p>Today</p> | ||
</div> | ||
<div class="panel panel4"> | ||
<p>Give</p> | ||
<p>All</p> | ||
<p>You can</p> | ||
</div> | ||
<div class="panel panel5"> | ||
<p>Life</p> | ||
<p>In</p> | ||
<p>Motion</p> | ||
</div> | ||
</div> | ||
|
||
<script> | ||
const panels = document.querySelectorAll('.panel'); | ||
|
||
function toggleOpen() { | ||
this.classList.toggle('open'); | ||
} | ||
|
||
function toggleWord(e) { | ||
console.log(e.propertyName); | ||
if(e.propertyName.includes('flex')){ | ||
this.classList.toggle('open-actived'); | ||
} | ||
|
||
} | ||
|
||
panels.forEach(panel => panel.addEventListener('click', toggleOpen)); | ||
panels.forEach(panel => panel.addEventListener('transitionend', toggleWord)); | ||
</script> | ||
|
||
</body> | ||
</html> |
Oops, something went wrong.