使用 CSS 和 JavaScript 实现 iPhone 14 Pro “灵动岛” 动画
背景:
在我十八岁的最后一天 9.8 号,苹果公司召开秋季新品发布会,推出了 iPhone 14 等一系列产品。
和大家一样,第一眼看到的时候我也觉得很惊艳,这丝滑带来的高级感,好灵动 😮!觉得厨子真是巧夺天工。作为一名前端学习者,我最感兴趣的当然是灵动岛动画的实现。经过一定的了解和学习后,那么让我们进入今天的主题,如何用 CSS 和 Javascript 来浅浅灵动一下。
相关的前端知识
本次分享会涉及前端三大件:
- HTML 定义了网页的内容
- CSS 描述了网页的布局
- JavaScript 控制了网页的行为
让我们开始吧!
首先我们先用 HTML 和 CSS 做出手机的静态页面
HTML 代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="./style.css" /> <title>Document</title> </head>
<body> <div class="iphone14Pro"> <div class="dynamic-island"></div> </div> </body> </html>
|
CSS 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| .iphone14Pro { position: relative; margin: auto; width: 974px; height: 876px; overflow: hidden; background-image: url(https://www.apple.com.cn/v/iphone-14-pro/a/images/overview/dynamic-island/dynamic_hw__btl4fomgspyu_large.png); }
.dynamic-island { width: 320px; margin-top: 72px; margin: 72px auto 0; background-color: red; height: 80px; border-radius: 40px; position: relative; } 此处因为类名为dynamic-island的div内没有内容,所以用::before 和::after没有区别 .dynamic-island::before { position: absolute; content: " "; right: 0; width: 80px; height: 100%; border-radius: 80px; background-color: #272729; }
|
这样我们就得到了半部 iPhone 14Pro啦!(bushi)
距离灵动岛只需要一点点 CSS 和 JS 就可以啦 😁
一些 Web 动画的知识
目前 web 动画在实现时主要有两种方式:CSS/ JS
但是独立使用任何一个都不是最好的解决方案
只用 CSS3 时,我们不好控制整个动画进程,如简单的点击触发动画,循环轮播整套动画等。
只用 JS ,由于 JS 单线程的特性,所以在动画渲染时,性能不如 CSS3,可能出现不流畅情况。
在 CSS3 中,也有两种实现灵动岛这样的过渡动画的方法: animation 和 transition
animation 属性
animatio 是很多属性的简写,我们简单介绍了解一下:
1 2
| animation: name duration timing-function delay iteration-count direction fill-mode;
|
name:
代表一个由@keyframes
定义的动画序列的名字
duration :
动画持续时间
timing-function :
定义动画在每一动画周期中执行的节奏:CSS ease 各种效果的区别(ease 与 ease-in-out 的区别)_哔哩哔哩_bilibili
delay :
动画开始前的延迟时间
iteration-count:
动画播放次数
direction:
指示动画是否反向播放
fill-mode:
设置动画在执行之前和之后对应的样式选择
transition 属性
transition 也是是很多属性的简写,我们简单介绍了解一下:
transition-property:
指定应用过渡属性的名称, 默认为 all
transition-duration:
过度动画的持续时间
transition-timing-function:
和上面的 timing-function 类似
transition-delay:
和上面的 delay 类似
下面我们会分别使用这两个属性来实现动画效果
灵动岛的具体实现
我们今天简单实现下面四个常用的动画效果
1.后台有音乐,倒计时,开热点,打电话等活动时,灵动岛变成长条
① 我们先用 transition 属性来实现
只需要在灵动岛的 CSS 上加上一个 transition 属性
1 2 3 4 5 6
| transition: width cubic-bezier(0.3, 2, 1.0, 0.9) 0.8s;
然后我们用鼠标停在灵动岛模拟后台有活动 .dynamic-island:hover {width: 50vw;} 让我们看看效果
|
② 我们用 animation 属性来实现
因为是给鼠标悬停时加动画,animation 的属性加在 hover 的伪类选择器上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| .dynamic-island:hover { animation: long 800ms ease-in-out forwards;
} 下面我们定义long 的动画 @keyframes long { 0% { }
60% { width: 50vw; }
80% { transform: scaleX(1.04); }
100% { transform: scaleX(1); width: 50vw; } }
|
我们不难发现,要让动画有 “灵动感” ,动画的结尾往往表现出一种”超过预期大小,然后又缩回“ 的效果,在一伸一缩之中给了观众灵动的感觉。
对比 transition 和 animation
他们虽然都能做出“灵动感”,但是前者的实现会相对困难一些,虽然代码看起来简洁,但是贝塞尔曲线来描述理想的动画并没有直接用 animation 的定义关键帧写起来直观~
所以下面的动画效果都用 animation 实现(肯定不是我偷懒 🙃)
2.后台同时有两个活动的时候,灵动岛会分成两个部分(三个以上只显示两个)
我们用点击灵动岛给灵动岛加类名的方式,让灵动岛在不同动画间切换
灵动岛分成两部分的原理是把里面藏着的一个小球弹出,本身也做一个放大缩小的动感
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| .divide { animation: divide-left 800ms ease-in-out forwards; }
@keyframes divide-left { 0% { }
40% { transform: scaleX(1.1); }
100% { transform: scaleX(1); } }
.divide::before { animation: divide-right 800ms ease-in-out forwards; }
@keyframes divide-right { 0% { }
40% { transform: scaleX(1.1); }
100% { transform: scaleX(1); right: -100px; } }
|
3.关掉只剩一个后台任务时,两部分融合
与分开成两部分类似,逆过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| .fusion { animation: fusion-left 800ms ease-in-out forwards; }
@keyframes fusion-left { 0% { }
40% { transform: scaleX(1.1); }
100% { transform: scaleX(1); } }
.fusion::before { animation: fusion-right 800ms ease-in-out forwards; }
@keyframes fusion-right { 0% { right: -100px; }
40% { transform: scaleX(1.1); }
100% { transform: scaleX(1); right: 0; } }
|
4.长按唤出小组件的状态
原理是把灵动岛变宽变长,小圆球设置为 display:none
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| .bigger { animation: bigger 800ms ease-in-out forwards; }
@keyframes bigger { 0% { }
60% { width: 81vw; height: 400px; border-radius: 100px; }
80% { transform: scaleX(1.04); }
100% { width: 81vw; height: 400px; border-radius: 100px; transform: scaleX(1); } }
.bigger::before { display: none; }
|
JS 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const dynamicIsland = document.querySelector(".dynamic-island");
let index = 0; animationList = ["long", "normal", "divide", "fusion", "bigger", "reset"];
dynamicIsland.addEventListener("click", () => { setTimeout(() => { if (index <= animationList.length - 1) { dynamicIsland.classList.add(animationList[index]); index++; } else { index = 0; dynamicIsland.classList.remove( "long", "normal", "divide", "fusion", "bigger", "reset" ); dynamicIsland.classList.add(animationList[index]); } }, 100); });
|
总结:
iPhone 14 Pro 好不好用,灵动岛的实际体验到底好不好,我并没有资格评价(没用过)
但是灵动岛这种新颖的交互方式和大胆创新的勇气,都值得我们学习
好了今天分享就到这里,相信大家就算没有买 iPhone 14,也灵动了一把 😊。
代码地址:Mingaaaaaaa/Dynamic-Island (github.com)