新增第222个小实例:灵动的流边开关切换效果

This commit is contained in:
DESKTOP-BM6RJU5\wyanh 2023-06-17 17:18:37 +08:00
parent b06dd04178
commit a3e2325a87
4 changed files with 336 additions and 0 deletions

View File

@ -229,6 +229,7 @@
219. HTML5+CSS3+JS小实例右键菜单
220. HTML5+CSS3小实例环绕小球弹性loading动画
221. HTML5+CSS3+JS小实例文字阴影还能这么玩
222. HTML5+CSS3+JS小实例灵动的流边开关切换效果
#### 赞赏作者
![image](https://gitee.com/wyanhui02/html_css_demo/raw/master/images/%E8%B5%9E%E8%B5%8F%E4%BD%9C%E8%80%85/%E8%B5%9E%E8%B5%8F%E7%A0%81.jpg)

95
code/222/222.css Normal file
View File

@ -0,0 +1,95 @@
*{
/* 初始化 */
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 定义CSS自定义变量 */
:root{
/* 打开状态下圆形的颜色 */
--on-color:#0085ff;
/* 关闭状态下圆形的颜色 */
--off-color:#cce7ff;
/* 开关按钮的背景颜色 */
--toggle-bg:#1f3447;
/* 圆与边缘的间距 */
--toggle-gap:10px;
/* 开关按钮的宽度 */
--toggle-width:160px;
/* 动画过渡时的颜色 */
--transition-color:var(--off-color);
}
body{
/* 100%窗口宽高 */
min-height: 100vh;
/* 弹性布局 居中演示 */
display: flex;
justify-content: center;
align-items: center;
background-color: #00182e;
}
.toggle{
position: relative;
cursor: pointer;
background: var(--toggle-bg);
width: var(--toggle-width);
height: calc(var(--toggle-width) / 2);
border-radius: 160px;
overflow: hidden;
display: flex;
align-items: center;
box-shadow: inset 0 0 2px #7c7e8855;
-webkit-tap-highlight-color: transparent;
}
.toggle input{
/* 隐藏复选框 */
appearance: none;
}
/* 复选框选中时svg线条的过渡颜色设为打开的颜色深蓝 */
.toggle input:checked ~ svg .outline{
--transition-color: var(--on-color);
}
/* 开、关两个圆的统一样式 */
.off-knob,
.on-knob{
position: absolute;
height: calc(100% - var(--toggle-gap) * 2);
/* 保持宽高比为1:1 */
aspect-ratio: 1;
border-radius: 50%;
box-shadow: 0 1px 1px rgba(10,19,39,0.1),
0 4px 12px rgba(10,19,39,0.03),
0 4px 20px rgba(10,19,39,0.04);
transform-origin: center center;
}
/* 关的状态 */
.off-knob{
left: var(--toggle-gap);
background: var(--off-color);
}
/* 开的状态 */
.on-knob{
right: var(--toggle-gap);
background: var(--on-color);
transform: scale(0.1) translateX(430px);
}
svg{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
/* 流边线条 */
.outline{
width: 100%;
height: 100%;
stroke: var(--transition-color);
stroke-dasharray: 2 125;
stroke-dashoffset: 32.5;
stroke-opacity: 0;
}
/* 模糊效果 */
.outline--blur{
filter: blur(0.8px);
}

36
code/222/222.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>灵动的流边开关切换效果</title>
<link rel="stylesheet" href="222.css">
</head>
<body>
<label class="toggle">
<input type="checkbox">
<div class="off-knob"></div>
<div class="on-knob"></div>
<svg viewBox="0 0 44 22" xmlns="http://www.w3.org/2000/svg">
<rect
class="outline"
fill="none"
rx="11"
stroke-linejoin="round"
stroke-linecap="round"
stroke-width="1"
/>
<rect
class="outline outline--blur"
fill="none"
rx="11"
stroke-linejoin="round"
stroke-linecap="round"
stroke-width="1"
/>
</svg>
</label>
</body>
</html>
<script src="222.js"></script>

204
code/222/222.js Normal file
View File

@ -0,0 +1,204 @@
// 动画时长(毫秒)
const DURATION=500;
// 初始化开关按钮
function initToggle(toggle){
// 要操作的元素
const input=toggle.querySelector('input');
const offKnob=toggle.querySelector('.off-knob');
const onKnob=toggle.querySelector('.on-knob');
const outline=toggle.querySelector('.outline');
const outlineBlur=toggle.querySelector('.outline--blur');
input.addEventListener('click',(e)=>{
// 获取正在运行的动画
const animating=toggle.getAnimations({subtree:true}).some((animation)=>animation.playState!=='finished');
// 是否正在运行动画
if(animating){
e.preventDefault();
return;
}
if(input.checked){
// 开的状态
animateOffKnobCheck();
animateOnKnobCheck();
animateOutlineCheck();
return;
}
animateOffKnobUncheck();
animateOnKnobUncheck();
animateOutlineUncheck();
})
// 定义动画
// 打开时浅色圆的动画
function animateOffKnobCheck(){
offKnob.animate(
[
{
transform:'scale(1) translateX(0)'
},{
transform:'scale(0.1)',
background:'var(--on-color)',
offset:0.6
},{
transform:'scale(0.1) translateX(-430px)',
background:'var(--on-color)'
}
],
{
duration:DURATION,
easing:'cubic-bezier(0.44,-0.44,0.98,0.89)',
fill:'forwards'
}
);
}
// 关闭时浅色圆的动画
function animateOffKnobUncheck(){
offKnob.animate(
[
{
transform:'scale(0.1) translateX(-430px)',
background:'var(--off-color)'
},{
transform:'scale(0.1) translateX(0)',
background:'var(--off-color)',
offset:0.4
},{
transform:'scale(1.05) translateX(0)',
background:'var(--off-color)',
offset:0.7
},{
transform:'scale(0.87) translateX(0)',
background:'var(--off-color)',
offset:0.8
},{
transform:'scale(1.01) translateX(0)',
background:'var(--off-color)',
offset:0.9
},{
transform:'scale(1) translateX(0)',
background:'var(--off-color)'
}
],
{
duration:DURATION,
easing:'ease-out',
delay:DURATION * 2,
fill:'forwards'
}
);
}
// 打开时深色圆的动画
function animateOnKnobCheck(){
onKnob.animate(
[
{
background:'var(--on-color)'
},{
transform:'translateX(0) scale(0.1)',
background:'var(--on-color)',
offset:0.4
},{
transform:'translateX(0) scale(1.05)',
background:'var(--on-color)',
offset:0.7
},{
transform:'translateX(0) scale(0.87)',
background:'var(--on-color)',
offset:0.8
},{
transform:'translateX(0) scale(1.01)',
background:'var(--on-color)',
offset:0.9
},{
transform:'translateX(0) scale(1)',
background:'var(--on-color)'
}
],
{
duration:DURATION,
easing:'ease-out',
delay:DURATION + DURATION,
fill:'forwards'
}
);
}
// 关闭时深色圆的动画
function animateOnKnobUncheck(){
onKnob.animate(
[
{
transform:'scale(0.1) translateX(0)',
background:'var(--off-color)',
offset:0.6
},{
transform:'scale(0.1) translateX(430px)',
background:'var(--off-color)'
}
],
{
duration:DURATION,
easing:'cubic-bezier(0.44,-0.44,0.98,0.89)',
fill:'forwards'
}
);
}
// 打开时流边线条的动画
function animateOutlineCheck(){
const keyframes=[
{
strokeOpacity:1
},{
strokeOpacity:1,
offset:0.99
},{
strokeOpacity:0,
strokeDashoffset:89
}
];
const options={
duration:DURATION,
easing:'cubic-bezier(0.55,0.055,0.9,1)',
delay:DURATION,
fill:'forwards'
};
outline.animate(keyframes,options);
outlineBlur.animate(keyframes,options);
}
// 关闭时流边线条的动画
function animateOutlineUncheck(){
const keyframes=[
{
strokeOpacity:1,
strokeDashoffset:89
},{
strokeOpacity:1,
strokeDashoffset:32.5,
offset:0.99
},{
strokeOpacity:0,
strokeDashoffset:32.5
}
];
const options={
duration:DURATION,
easing:'cubic-bezier(0.55,0.055,0.9,1)',
delay:DURATION,
fill:'forwards'
};
outline.animate(keyframes,options);
outlineBlur.animate(keyframes,options);
}
}
// 初始化
initToggle(document.querySelector('.toggle'));