岁华过半休惆怅,且对西风贺立秋。 ——范成大立秋二绝
杭州的天儿是怎样个事儿啊这些天,热了一个夏天也就算了,上周天秋都立了,雨也下了,老话说的都是天高气爽,怎样温度还上去了呢!!
看看近来温度
这种气候的周末,不才也只能躺在床上吹着空调,然后赞叹一下外卖小哥的艰辛,趁便照顾一下他们的生意了。
……
闲来无事左手冰棍右手抖音消磨时间,忽然一条内容案牍为这个丝滑的UI交互我能玩一天
的一个前端动效映入眼帘。
嗯?什么交互动效能玩一天,让我瞧瞧:
嗷~这不就那啥嘛,先如此如此……再这般这般……不就行了?
习惯性的点开谈论区,公然,大家都非常的热心呢,主动给设计者照料后事……
这样一个丝滑的交互能不能玩一天,不才不知道。
但是,把他写出来,不才能写一天,而这,就叫做工作量,而不才最喜欢的就是添加(凑)工作量。
0. 万事开头难
这一个动效乍一看好像其实并没有觉得很凌乱,但是假设往细了看(多细?)
……
好像其实也没有很凌乱。
先聊聊思路:(仅供参考,总所周知,黑猫白猫都是好猫,能完结就行,不拘泥与不才这一种方法)
- 首要看静态的东西,我们可以把这个动效拆成
容器
,按钮
和滑块
三个部分,包含各类样式; -
容器在被点击时需求添加
css透视
和必定幅度的Y轴旋转
,构构成被点击下陷的感觉,仔细调查发现不同按钮点击后两头容器旋转的偏移量还不尽相同,所以我们还需求动态批改他的原点
; -
按钮则是包含相关内容的一个盒子,有选中和未选中的两种不同情况,再仔细点呢,盒子内的内容在切换情况时还有必定量的缩放
scale
,缩放详细为:- 被选中时,先稍微多放大一些,再缩小为选中的放大情况坚持;
- 未选中时,先稍微多缩小一些,再放大为未选中的正常情况坚持;
-
滑块就比较简单了,单纯的静态样式加上点今后滑动到对应按钮下方,稍微麻烦一点就是这个点击后的过渡效果(滑动)需求起始和结尾稍慢,中心较快的速度,或许需求用贝塞尔函数来完结,关于
css贝塞尔函数
现已忘记的同学可以再去温习一遍。cubic-bezier
多说无益,不才先干为敬!
1. 静态页面
首要当然是我们的静态页面了,动效再怎样动,也得先有能动的玩意儿是波。
容器
我们先随意点,先画个容器,起个名儿就叫btnWrapper
吧,因为滑块用肯定定位,所以容器得先相对定位,一同给定宽高:
<div id="btnWrapper">
</div>
#btnWrapper {
position: relative;
width: 380px;
height: 80px;
padding: 12px 16px;
border-radius: 12px; // 圆角
overflow: hidden; // 超出躲藏
background-color: #e2e6eb; // 背景色
box-shadow: -10px -10px 15px #f5f9fd, 10px 10px 15px #d8dbe5; // 阴影(阴影可以添加多条,别告诉我你不知道!!)
}
就这样一个简单的容器盒子就画好了(别问我没有设计图颜色和尺度都是怎样来的,截图+取色吸的)
滑块
然后就是滑块,因为滑块并没有凌乱的逻辑,也不需求寄存一些其他元素,所以不才选择的是用伪元素
来完结,这就没有结构(html)代码了。
尺度图解:
直接上才艺~
#btnWrapper::before {
content: "";
position: absolute;
left: 16px; // 要考虑到父盒子的内边距
top: 12px; // 同上
width: calc(50% - 16px - 8px);
height: calc(100% - 24px);
border-radius: 12px;
box-shadow: inset 8px 8px 6px #d9dce6,
inset -5px -5px 15px #f5f9fd,
inset -5px -5px 15px #f5f9fd,
inset 7px 7px 6px #d9dce6;
}
emmm….吸色好像不太靠谱?勉勉强强吧
按钮
按钮呢因为他本身没有轮廓样式,所以我是直接把它理解为均分了容器的两个通明盒子,然后内部元素不确定,用弹性布局让按钮的子元素都居中就行。
但是因为按钮要跟滑块堆叠,并且居于滑块之上,所以按钮也需求浮动起来,每个都占50%宽度,这儿不才选择让他俩都float: left;
,假设还想继续运用肯定定位也是可以完结的,装备不同left
即可,这儿就不再赘述了。
一同按钮还有选中情况,
<div id="btnWrapper">
<div class="btn active">按钮1</div>
<div class="btn">按钮2</div>
</div>
.btn {
float: left;
display: flex; // 弹性布局
align-items: center; // 列居中
justify-content: center; // 行居中
width: 50%;
height: 100%;
padding: inherit; // 偷闲内边距继承了父元素,也可以自己批改
color: #aaa; // 默许文字颜色是灰色,随意选了个色号
cursor: pointer; // 鼠标经过图标为小手
}
.active {
color: #111; // 选中颜色
}
毕竟的制品就是
嘿你别说,还挺有模有样呢?假设想再给按钮里添加类似抖音里那个图片,也是可以的,不才就不写了哈。
2. 滑块动效
静态页面却是写完了,但这并不是不才的政策,不才的政策是期望它动起来,如标题相同玩一整天的丝滑。
而效果图中不才一眼看见动幅最大的就是这个滑块了,所以抉择先让滑块动起来,其实让滑块动起来非常简单,批改滑块的left
值即可,再添加过渡效果让滑块愈加丝滑~
这儿动态批改样式,我选择运用css变量
,经过js
给容器添加和批改css变量
来到达动态批改样式的效果。
**来吧,展示!**
#btnWrapper::before {
content: "";
/* ...其他特点 */
left: var(--groove-left); // 运用css变量
/* ...其他特点 */
transition: left 1s cubic-bezier(0.82, 0.12, 0.18, 0.88); // 添加过渡效果,贝塞尔曲线
}
// 获取容器元素
let wrapper = document.getElementById('btnWrapper');
// 先给容器设置一个css变量并附初始值
wrapper.style.setProperty('--groove-left', '12px');
// 获取按钮元素
let btns = document.getElementsByClassName('btn');
for (let i = 0; i < btns.length; i++) {
// 给每个按钮添加点击事件
btns[i].addEventListener('click', function (e) {
// 点击后,批改css变量的值
wrapper.style.setProperty('--groove-left', `calc(12px + ${i * 50}%)`);
})
}
定心,经过不才严密(随意)核算,那个贝塞尔曲线应该差不太多,如有差错还请自行调整,留言给不才,究竟咱没有真实设计图…
这样点击后,滑块就会自己滑来滑去啦!
3. 按钮动效
那,滑块都自己动起来了,你按钮不得自己动?
自己动?
动?
懂了!直接在点击里批改每个按钮的类名不就行了?
没错,但是如前面0. 万事开头难不才提到的,按钮不单纯仅仅改动了颜色,还改动了字体的缩放大小,并且选中非选中情况都有一个缩放的颤动。
所以不才抉择,写两个动画(详细可以依据自己的感觉批改,仅仅不才的感觉就到这了)。
talk is cheap,show me the code!(不才想起来了!)
<div id="btnWrapper">
<div class="btn active">按钮1</div>
<div class="btn">按钮2</div>
</div>
/* 按钮选中缩放动画 */
@keyframes txtEnterScale {
0% {
transform: scale(1);
}
80% {
transform: scale(1.15);
}
100% {
transform: scale(1.1);
}
}
/* 按钮未选中缩放动画 */
@keyframes txtOutScale {
0% {
transform: scale(1.1);
}
80% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
.btn {
/* ...其他特点 */
transition: color 0.4s linear; // 添加文字颜色过渡效果
animation: txtOutScale 0.6s linear; // 添加按钮未选中动画,留心时间
/* ...其他特点 */
}
.active {
color: #111;
transform: scale(1.1); // 批改选中情况原始缩放为1.1
animation: txtEnterScale 0.4s linear; // 添加按钮选中动画,留心时间
}
let wrapper = document.getElementById('btnWrapper');
wrapper.style.setProperty('--groove-left', '12px');
let btns = document.getElementsByClassName('btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function (e) {
wrapper.style.setProperty('--groove-left', `calc(12px + ${i * 50}%)`);
resetBtn(btns); // 初始化全部按钮类名
setTimeout(() => {
btns[i].className = 'btn active'; // 给被点击的按钮添加选中样式,留心不要丢了原始样式
}, 500);
})
}
// 初始化全部按钮类名的方法
function resetBtn(btns) {
for (let i = 0; i < btns.length; i++) {
setTimeout(() => {
btns[i].className = 'btn';
}, 100)
}
}
先别划走!!!这儿讲一下为什么需求留心时间,因为不才留心到,并不是一经点击按钮的动画就开端了,选中情况的类名,是在滑块简直完全滑到对应按钮背面,按钮的缩放情况和颜色才开端变化,同理未选中情况也类似,仅仅时间稍微提早。
哼哼,一个小细节。
所以不才的处理方式是在点击后给添加类名的代码添加一个定时器,有无更好的方法麻烦奉告不才。
文字的缩放和不才的小细节,能看出来吗?
4. 容器动效
又凑了这么多字了,工作量嘎嘎上去了……
哎哎哎客官别着急走哇,咱这还有个最大的容器还没动起来呢,错了错了,不才直接来。
整体容器
顺着不才前面的思路,首要先给他添加上css透视
和Y轴旋转
,然后我们先看看效果怎么,为了做出近大远小的视觉效果,我们首要要给容器的父元素添加透视点,为了不耽搁客官老爷们的时间,不明白概念的同学就劳烦百度一下或许直接传送门先了解一番。perspective
body {
perspective: 500px; // 添加透视
}
#btnWrapper {
/* ...其他特点 */
transform-origin: center; // 添加转化原点
transition: transform 0.4s cubic-bezier(0, 0, 0.48, 1); // 添加过渡效果
}
.rotateWrap {
transform: rotateY(var(--wraper-rotate)); // 添加Y轴旋转
}
let wrapper = document.getElementById('btnWrapper');
// ...其他代码
let btns = document.getElementsByClassName('btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function (e) {
// ...其他代码
wrapper.style.setProperty('--wraper-rotate', `${i === 0 ? -8 : 8}deg`);
wrapper.className = 'rotateWrap';
setTimeout(() => {
wrapper.className = ''
}, 550); // 留心小细节!!!
})
}
// ...其他代码
相同,这个被点击后的动效也有亿点点小细节,就是这个时间,不才不便多说
寄几零五八,骚年!
亿点细节
看了目前制品图,是不是感觉跟上面那个玩一天丝滑的按钮大差不差,但是总感觉哪里有点不相同呢?
没错,不才也看出来,感觉人家那个非常的活络,而不才这个就像被钉住了相同。
没戳!!就是这个Y轴旋转
量的问题,不才前面也提到了,解决方法就是需求动态批改css转化
的原点。
当点击
按钮1
时,将原点设置为按钮2
半侧的某个点,反之亦然
嗦干酒干,这儿不才用到的仍是css变量
的方法
#btnWrapper {
/* ...其他特点 */
/* transform-origin: center; */
transform-origin: var(--wraper-origin); // 批改为动态运用css变量
transition: transform 0.4s cubic-bezier(0, 0, 0.48, 1);
}
let wrapper = document.getElementById('btnWrapper');
// ...其他代码
let btns = document.getElementsByClassName('btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function (e) {
// ...其他代码
wrapper.style.setProperty('--wraper-origin', `${i === 0 ? '80% top' : '20% top'}`); // 动态批改原点方位
wrapper.style.setProperty('--wraper-rotate', `${i === 0 ? -8 : 8}deg`); // 按钮1为-8,按钮2为8
wrapper.className = 'rotateWrap'; // 点击添加类名
setTimeout(() => {
wrapper.className = ''; // 铲除类名
}, 550); // 留心小细节!!!
})
}
// ...其他代码
好!这样不才经过肉眼调查然后复原的丝玩天动效就做好啦,先喽一眼毕竟制品图:
什么?你说好像没啥差异?
信任不才,必定有差异的,记住今天的关键词,仔细。
#. 意犹未尽
本来捏,今天的水文到这就结束了
但是,不才在玩这个动效的时候,感觉两个按钮单纯的切换来切换去,是不是太过于单调了?(主要是工作量还没混够)
所以,不才准备给这两个按钮加点功用。
直接展示,贴代码(有点长,但是是悉数的代码):
<div id="btnWrapper">
<div class="btn active">开灯</div>
<div class="btn">关灯</div>
</div>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #edf1f4;
perspective: 500px;
--c-wrap-shadow1: #f5f9fd;
--c-wrap-shadow2: #d8dbe5;
--c-wrap-bg: #e2e6eb;
--c-btn-shadow1: #d9dbe6;
--c-btn-shadow2: #f5f9fd;
--c-txt1: #aaa;
--c-txt2: #111;
transition: background-color 0.4s linear;
}
.dark {
background-color: #333;
--c-wrap-shadow1: #292929;
--c-wrap-shadow2: #202020;
--c-wrap-bg: #505050;
--c-btn-shadow1: #323232;
--c-btn-shadow2: #444;
--c-txt1: #888;
--c-txt2: #fff;
}
#btnWrapper {
position: relative;
width: 380px;
height: 80px;
padding: 12px 16px;
margin: 300px auto 0;
border-radius: 12px;
overflow: hidden;
background-color: var(--c-wrap-bg);
box-shadow: -10px -10px 15px var(--c-wrap-shadow1), 10px 10px 15px var(--c-wrap-shadow2);
transform-origin: var(--wraper-origin);
transition: transform 0.4s cubic-bezier(0, 0, 0.48, 1), box-shadow 0.4s linear, background-color 0.4s linear;
}
.rotateWrap {
transform: rotateY(var(--wraper-rotate));
}
#btnWrapper::before {
content: "";
position: absolute;
left: var(--groove-left);
top: 12px;
width: calc(50% - 16px - 8px);
height: calc(100% - 24px);
border-radius: 12px;
box-shadow: inset 8px 8px 6px var(--c-btn-shadow1), inset -5px -5px 15px var(--c-btn-shadow2), inset -5px -5px 15px var(--c-btn-shadow2), inset 7px 7px 6px var(--c-btn-shadow1);
transition: left 1s cubic-bezier(0.82, 0.12, 0.18, 0.88), box-shadow 0.4s linear;
}
.btn {
float: left;
display: flex;
align-items: center;
justify-content: center;
width: 50%;
height: 100%;
padding: inherit;
color: var(--c-txt1);
transition: color 0.4s linear;
animation: txtOutScale 0.6s linear;
cursor: pointer;
}
.active {
color: var(--c-txt2);
transform: scale(1.1);
animation: txtEnterScale 0.4s linear;
}
@keyframes txtEnterScale {
0% {
transform: scale(1);
}
80% {
transform: scale(1.15);
}
100% {
transform: scale(1.1);
}
}
@keyframes txtOutScale {
0% {
transform: scale(1.1);
}
80% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
let wrapper = document.getElementById('btnWrapper');
wrapper.style.setProperty('--groove-left', '12px');
let btns = document.getElementsByClassName('btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function (e) {
ThemeChange(i === 1);
resetBtn(btns);
wrapper.style.setProperty('--groove-left', `calc(12px + ${i * 50}%)`);
wrapper.style.setProperty('--wraper-origin', `${i === 0 ? '75% top' : '25% top'}`);
wrapper.style.setProperty('--wraper-rotate', `${i === 0 ? -8 : 8}deg`);
wrapper.className = 'rotateWrap';
setTimeout(() => {
btns[i].className = 'btn active';
}, 500);
setTimeout(() => {
wrapper.className = ''
}, 550);
})
}
// 重置按钮类名
function resetBtn(btns) {
for (let i = 0; i < btns.length; i++) {
setTimeout(() => {
btns[i].className = 'btn';
}, 100)
}
}
// 改动主题
function ThemeChange(bol) {
let body = document.body;
body.className = bol ? 'dark' : ''
}
波澜起伏,欲扬先抑,先看效果,再谈原理:
其实就是字面意思,混工作量。
切换主题思路:
把需求切换的颜色都提出来用css变量替代,并添加上过渡效果(不然看起来很突兀)
在不同主题类名里批改css变量
点击按钮,将类名添加到body上(有的也添加到html上,横竖就是添加到你运用主题的最上层元素)
因为给body添加了类名权重更高,所以会覆盖body以元素名定义的css变量,经过css过渡今后,就完结了丝滑的换肤效果。
今天混工作量就到此为止吧,再混就不礼貌了~
秋都立完了,劳烦秋天急忙到来吧,我要出去玩!!!
你怎样还在看?