我正在参加「掘金·启航计划」
前言
最近苹果发布了iphone 14
系列的新品,说起内容的话,大家印象最深的应该就是苹果所谓的“灵动岛”了吧,毕竟发布会上看着还是挺炫酷的呢,就像下面这样:
看完大家都控制不了自己的手了吧?
买来结果变成了这样:
网络上顿时遍地吐槽,色差严重,虚假宣传......
其实在晚上不开灯的时候用效果真的挺好的。👻
好了,咱们言归正传吧,别管啥色差严不严重,今天就用CSS动画
实现一个Smart Island
的效果,顺便补习一下CSS动画
。
如果有掘友不了解CSS动画,可以看看这个文章,大佬写的,个人觉得非常棒👍 深入浅出 CSS 动画 · Issue #141 · chokcoco/iCSS
实现Smart Island
新建smart-island.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>smart-island</title>
</head>
<body>
<div id="phone">
<div class="island"></div>
</div>
</body>
</html>
iPhone壳样式
body设置为flex布局,并设置里面的内容居中显示:
body {
display: flex;
justify-content: center;
}
id为phone
这个div
,我准备使用一张iPhone
的外壳背景图片,搞一个iPhone
的样子。
#phone {
width: 374px;
height: calc(844px - 16px);
text-align: center;
background-size: contain;
background-repeat: no-repeat;
background-image: url(https://www.apple.com.cn/v/iphone-14-pro/a/images/overview/dynamic-island/dynamic_hw__btl4fomgspyu_large.png);
}
✅看下现在的样子:
覆盖iPhone的药丸
想要让iPhone的药丸动起来,那么我们就得自己实现一个药丸覆盖图片中的,再为它添加动画效果。把class为island
的div
设置成药丸的样子,这个很简单了,设置一下宽高,边框圆角,颜色即可。
.island {
width: 104px;
display: inline-block;
height: 29px;
border-radius: 18px;
background-color: black;
margin-top: 28px;
}
✅完了就像下面这样:
变长动画
做完上面的操作,接着我们就可以来实现变长的动画了。
变长嘛,就是把宽度弄宽呗,简单:
.longer {
animation: longer 800ms ease-in-out forwards;
}
@keyframes longer {
0% {
}
100% {
width: 208px;
}
}
再界面上添加一个按钮,用来给药丸添加class。
<div>
<button onclick="addClass('longer')">longer</button>
<div>
实现addClass
const isLand = document.querySelector('.island')
function addClass(cls) {
isLand.classList.add(cls)
}
简单设置下按钮的样式
button{
background: #fff;
border: 1px solid #e0e0e0;
line-height: 20px;
border-radius: 4px;
}
✅好了,看下效果吧:
还可以,但是我们可以仔细的看下苹果的那个视频,它变长的时候是先变长,快结束时又变长了一点点,然后又缩回来了。调整下:
@keyframes longer {
0% {
}
60% {
width: 208px;
}
80% {
transform: scaleX(1.06);
}
100% {
transform: scaleX(1);
width: 208px;
}
}
✅再看看效果:
瞬间感觉更有逼格了。
分离动画
分离动画就是药丸后面分离出一个小圆那个动画。那我们是不是需要实现一个小圆,再给它添加动画效果❓
没错,那可以有两种方案:
- 给island节点设置
after
伪类,将伪类设置为小圆 - 新增一个节点,设置为小圆
笔者这里使用伪类去实现:
.island::after {
height: 29px;
content: ' ';
width: 29px;
right: 0;
position: absolute;
border-radius: 50%;
background-color: black;
}
这里别忘了给island
添加position: relative;
这个时候我们看不到效果,因为小圆和药丸叠在一起了,接下来添加动画。药丸不动,只要有个缩放的效果即可,改变小圆的right
,让小圆可见。
/* 分离 */
/* 药丸 */
.separate {
animation: separate-left 800ms ease-in-out forwards;
}
/* 小圆 */
.separate::after {
animation: separate-right 800ms ease-in-out forwards;
}
@keyframes separate-left {
0% {
width: 208px;
}
40% {
transform: scaleX(1.06);
width: 104px;
}
100% {
transform: scaleX(1);
width: 104px;
}
}
@keyframes separate-right {
0% {
right: 0;
}
40% {
transform: scaleX(1.06);
}
100% {
transform: scaleX(1);
right: -35px;
}
}
✅看下效果:
合并动画
合并动画就是把小圆和药丸合并到一起,恢复分离前的样子,那不就是把小圆的right改回来么,so easy😂
/* 合并 */
.merge {
animation: merge-left 800ms ease-in-out forwards;
}
.merge::after {
animation: merge-right 800ms ease-in-out forwards;
}
@keyframes merge-left {
0% {
}
40% {
transform: scaleX(1.06);
}
100% {
transform: scaleX(1);
}
}
@keyframes merge-right {
0% {
right: -35px;
}
40% {
transform: scaleX(1.06);
}
100% {
transform: scaleX(1);
right: 29px;
}
}
✅看看效果
变大动画-1
为啥是-1
,因为变大的动画有两种,一个高度小一些,一个高度大一些。我们先来实现小一些这个动画。很简单了,就是改变宽度高度还有边框圆角。
/* larger-s */
.larger-s {
animation: larger-s 800ms ease-in-out forwards;
}
@keyframes larger-s {
0% {
}
60% {
width: 298px;
height: 44px;
border-radius: 20px;
}
80% {
transform: scaleX(1.04);
}
100% {
transform: scaleX(1);
width: 298px;
height: 44px;
border-radius: 20px;
}
}
✅看看效果
变大动画-2
✅看看效果
/* larger-l */
.larger-l {
animation: larger-l 800ms ease-in-out forwards;
}
@keyframes larger-l {
0% {
}
60% {
width: 298px;
height: 160px;
border-radius: 40px;
}
80% {
transform: scaleX(1.04);
}
100% {
transform: scaleX(1);
width: 298px;
height: 160px;
border-radius: 40px;
}
}
把动画连起来播放
截至目前,我们实现了五个简单的动画效果,点击一个按钮执行一个,感觉傻傻的,我们把五个动画连起来播放,一个接一个的播放。
const isLand = document.querySelector('.island')
let index = 0
const animations = ['longer', 'separate', 'merge', 'larger-s', 'larger-l']
function start() {
if(index > animations.length - 1) return
isLand.classList.add(animations[index])
index++
}
isLand.addEventListener('animationend', (e) => {
if(e.target === isLand && !['separate-right', 'merge-right'].includes(e.animationName)) {
setTimeout(() => {
start()
}, 1000)
}
})
现在只需要一个开始按钮了。
<button onclick="start()">START▶️</button>
✅最终效果
✅完整代码
总结
本文通过CSS
动画实现了类似iPhone
灵动岛的动画效果,虽然看着复杂,但把动画拆分开来,还是非常简单的,就是一些宽高、圆角的改变,左右移动(当然还存在一些瑕疵)。和那些CSS
动画高手玩的花样根本不是一个级别。不过用来练习CSS
动画还是非常好的,代码熟练度都是敲出来的。学技术还是不能停留在看,还是得多实践。
show me the code