新增第222个小实例:灵动的流边开关切换效果
This commit is contained in:
parent
b06dd04178
commit
a3e2325a87
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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>
|
|
@ -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'));
|
Loading…
Reference in New Issue