😀CSS 实现 iPhone 14 Pro 灵动岛
前言
最近 iPhone 14 Pro 的灵动岛很火,我个人觉得这个设计算是挺巧妙的,用软件层面掩盖硬件层面的缺陷
今天我们就来用CSS
来还原一下灵动岛吧,就以来电状态下的灵动岛为例尝试进行还原
手机本体
我们首先查看一下手机的宽高比参数
可以看到是71.5:147.5
,假设我们将高度定为600px
,那么换算到对应的宽度就是291px
,然后手机颜色我们就用新出的暗紫色吧,根据广告图慢慢调整样式即可
<!-- 手机本体 -->
<div class="iPhone-14-pro">
<!-- 手机屏幕 -->
<div class="screen">
<div class="wrapper">
<!-- 灵动岛 -->
<div class="dynamic-island"></div>
</div>
</div>
</div>
/* 手机本体 */
.iPhone-14-pro {
position: relative;
width: 291px;
height: 600px;
background-color: #998c9e;
border-radius: 48px;
overflow: hidden;
}
/* 手机屏幕 */
.iPhone-14-pro .screen {
position: absolute;
inset: 2px;
background-color: black;
border-radius: 48px;
/* 边框 */
padding: 10px;
}
/* 屏幕内容 */
.iPhone-14-pro .screen .wrapper {
width: 100%;
height: 100%;
border-radius: 40px;
background: url(https://cdn.ytechb.com/wp-content/uploads/2022/09/iPhone-14-Pro-Wallpaper-purple.webp);
background-size: cover;
}
目前效果如下图所示:
有点内味儿了吧?(要是以后的iPhone真的就长这样多好)
手机两侧的按键
现在我们只还原了手机本体,但是两侧的按键还没弄上去,iPhone
的左侧从上到下依次是静音键、音量增加键、音量减小键,右侧则是一个电源键,我们通过绝对定位的方式将它们加上
<!-- 手机本体 -->
<div class="iPhone-14-pro">
<!-- 手机屏幕 -->
<div class="screen">
<div class="wrapper">
<!-- 灵动岛 -->
<div class="dynamic-island"></div>
</div>
</div>
<!-- 静音键 -->
<div class="mute-btn"></div>
<!-- 音量增大键 -->
<div class="volume-up-btn"></div>
<!-- 音量减小键 -->
<div class="volume-down-btn"></div>
<!-- 电源键 -->
<div class="power-btn"></div>
</div>
/* 静音键 */
.mute-btn {
position: absolute;
top: 100px;
left: -3px;
height: 20px;
width: 3px;
background: radial-gradient(#ccc, #666, #222);
}
/* 音量增大键 */
.volume-up-btn {
position: absolute;
top: 145px;
left: -3px;
height: 40px;
width: 3px;
background: radial-gradient(#ccc, #666, #222);
}
/* 音量减小键 */
.volume-down-btn {
position: absolute;
top: 205px;
left: -3px;
height: 40px;
width: 3px;
background: radial-gradient(#ccc, #666, #222);
}
/* 电源键 */
.power-btn {
position: absolute;
top: 175px;
right: -3px;
height: 70px;
width: 3px;
background: radial-gradient(#ccc, #666, #222);
}
现在的效果如下:
灵动岛
常规状态下的灵动岛
接下来到我们的重头戏了,先把一个常规状态下的灵动岛给弄出来
/* 灵动岛 */
.dynamic-island {
position: absolute;
width: 85px;
height: 25px;
left: 50%;
top: 20px;
transform: translateX(-50%);
background: black;
border-radius: 20px;
}
鼠标悬浮时的灵动岛
这里为了方便,我们用鼠标悬浮来模拟收到来电的状态,然后点击后灵动岛会变大,并且能够看到来电人的头像
首先先实现一下鼠标悬浮后灵动岛的特效,也就是岛的宽度变长,并且出现来电信息和挂断或者接听电话的按钮
先把html
结构调整一下
<!-- 手机本体 -->
<div class="iPhone-14-pro">
<!-- 手机屏幕 -->
<div class="screen">
<div class="wrapper">
<!-- 灵动岛 -->
<div class="dynamic-island">
<!-- 来电人信息 -->
<div class="caller">
<!-- 头像 -->
<div class="avatar"></div>
<!-- 信息 -->
<div class="info">
<span>iPhone</span>
<p>库克</p>
</div>
</div>
<!-- 接听和挂断电话按钮 -->
<div class="actions">
<!-- 挂断电话 -->
<div class="refuse">
<ion-icon name="call"></ion-icon>
</div>
<!-- 接听电话 -->
<div class="answer">
<ion-icon name="call"></ion-icon>
</div>
</div>
</div>
</div>
</div>
<!-- 静音键 -->
<div class="mute-btn"></div>
<!-- 音量增大键 -->
<div class="volume-up-btn"></div>
<!-- 音量减小键 -->
<div class="volume-down-btn"></div>
<!-- 电源键 -->
<div class="power-btn"></div>
</div>
然后添加上相关初始状态的样式和过渡动画
/* 灵动岛 */
.dynamic-island {
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
width: 85px;
height: 25px;
left: 50%;
top: 20px;
padding: 0 10px;
transform: translateX(-50%);
background: black;
border-radius: 20px;
transition: 0.5s ease-in-out;
cursor: default;
user-select: none;
}
/* 来电人信息 */
.dynamic-island .caller {
color: white;
visibility: hidden;
opacity: 0;
transition: 0.5s;
}
.dynamic-island .caller .avatar {
display: none;
}
.dynamic-island .caller .info span {
display: none;
}
.dynamic-island .caller .info p {
font-size: 8px;
}
/* 接听和挂断电话按钮 */
.dynamic-island .actions {
display: flex;
gap: 12px;
visibility: hidden;
opacity: 0;
transition: 0.5s;
}
.dynamic-island .actions .refuse {
color: #ff4438;
transform: rotate(135deg);
}
.dynamic-island .actions .answer {
color: #30d258;
}
/* 鼠标悬浮时灵动岛的状态 */
.dynamic-island:hover {
width: 200px;
}
.dynamic-island:hover .caller {
visibility: visible;
opacity: 1;
}
.dynamic-island:hover .actions {
visibility: visible;
opacity: 1;
}
具体思路就是先定义悬浮之前,各个元素的初始样式,有的元素在未展开时是不需要显示的,我们就要将它们的visibility
设置为hidden
,又因为要考虑到元素出现和消失有一个过渡的效果,所以初始时还要将opacity
设置为0,即不可见,然后再在悬浮时将其设置为1,让其可见度变为完全可见,这样transition
就能有一个过渡动画的效果了
现在效果如下:
点击灵动岛后展开详情
点击后整个灵动岛处于一个展开的状态,这里就涉及到状态的变更了,由hover
状态变更为展开状态,由于悬浮的逻辑我们可以通过css
的伪选择器:hover
去实现,就不需要用到js
去记录状态
但是展开这个状态不能用纯css
实现了,涉及到点击事件的处理,所以我们得编写一个简单的js
去记录展开状态,并且css
要编写展开状态下的样式,尽量让逻辑和样式之间的职责划分清楚,这样后面遇到问题的概率也会小一些,即便遇到了问题也会相对好维护
(() => {
const oDynamicIsland = document.getElementById("dynamic-island");
const oScreenWrapper = document.getElementById("screen-wrapper");
const init = () => {
bindEvent();
};
const bindEvent = () => {
// 点击灵动岛时将灵动岛状态变更为展开状态 -- 通过添加一个 .expanded 类名来记录展开状态
oDynamicIsland.addEventListener("click", (e) => {
// 阻止事件冒泡到屏幕区域的点击事件监听器中
// 否则每次点击都会被外层的屏幕元素的点击事件监听器移除 .expanded 类名
e.stopPropagation();
oDynamicIsland.classList.add("expanded");
});
// 点击屏幕内其他区域时取消灵动岛的展开状态
oScreenWrapper.addEventListener("click", () => {
oDynamicIsland.classList.remove("expanded");
});
};
init();
})();
状态的记录逻辑已经解决了,接下来我们只用专心编写展开状态下的样式即可,不需要再理会js
部分,这也是职责划分清楚的一个好处
/* 灵动岛展开状态 */
.dynamic-island.expanded {
width: 200px;
height: 50px;
}
.dynamic-island.expanded .caller {
visibility: visible;
opacity: 1;
}
.dynamic-island.expanded .caller .avatar {
width: 30px;
height: 30px;
margin-right: 8px;
}
.dynamic-island.expanded .caller .info span {
visibility: visible;
line-height: 8px;
opacity: 1;
}
.dynamic-island.expanded .actions {
visibility: visible;
opacity: 1;
transition-delay: 0;
}
.dynamic-island.expanded .actions .refuse {
color: white;
background-color: #ff4438;
}
.dynamic-island.expanded .actions .answer {
color: white;
background-color: #30d258;
}
具体的样式代码不复杂,就不过多解释了,直接看看效果吧
加上息屏特效
当鼠标离开屏幕区域时,我们让屏幕变黑,模拟一个息屏的效果,这个很简单,只需要给wrapper
设置默认的opacity
为0,而当鼠标悬浮时改为1,再加上transition
即可
.iPhone-14-pro .screen .wrapper {
width: 100%;
height: 100%;
border-radius: 40px;
background: url(https://cdn.ytechb.com/wp-content/uploads/2022/09/iPhone-14-Pro-Wallpaper-purple.webp);
background-size: cover;
opacity: 0;
transition: 0.5s;
}
.iPhone-14-pro .screen .wrapper:hover {
opacity: 1;
}
最终效果如下:
转载自:https://juejin.cn/post/7151426528725696525