一个漂亮的 switch 组件
# 图例


# github link
# props
- value
- true/false,默认值 false,代表为 light
- onChange
- style
- className
# 实现代码
点击 展开/收起 jsx 代码
/**
* @des 日夜切换 switch width默认是180,因为switch的宽高是180*100
*/
import React, { useEffect, useRef, useState } from 'react';
import './index.css';
const DayNightSwitch = (props) => {
const { value, onChange, style, className } = props;
const [isClicked, setIsClicked] = useState(false);
const isDark = value === false;
/** dom 节点 */
const contanierRef = useRef(null);
const mainButtonRef = useRef(null);
const daytimeBackgrondRef = useRef(null);
const cloudRef = useRef(null);
const cloudListRef = useRef(null);
const cloudLightRef = useRef(null);
const componentsRef = useRef(null);
const moonRef = useRef(null);
const starsRef = useRef(null);
const starRef = useRef(null);
useEffect(() => {
if (contanierRef.current) {
mainButtonRef.current =
contanierRef.current?.querySelector('.main-button');
daytimeBackgrondRef.current =
contanierRef.current?.querySelectorAll('.daytime-backgrond');
cloudRef.current = contanierRef.current?.querySelector('.cloud');
cloudListRef.current =
contanierRef.current?.querySelectorAll('.cloud-son');
cloudLightRef.current =
contanierRef.current?.querySelector('.cloud-light');
componentsRef.current =
contanierRef.current?.querySelector('.components');
moonRef.current = contanierRef.current?.querySelectorAll('.moon');
starsRef.current = contanierRef.current?.querySelector('.stars');
starRef.current = contanierRef.current?.querySelectorAll('.star');
}
}, []);
useEffect(() => {
if (contanierRef?.current && mainButtonRef.current) {
if (value) {
handleComponentsClick('light');
} else {
handleComponentsClick('dark');
}
}
}, [value, contanierRef.current]);
// 点击事件
const handleComponentsClick = (targetTheme) => {
if (targetTheme === 'light') {
mainButtonRef.current.style.transform = 'translateX(0)';
mainButtonRef.current.style.backgroundColor = 'rgba(255, 195, 35,1)';
mainButtonRef.current.style.boxShadow =
'3px 3px 5px rgba(0, 0, 0, 0.5), inset -3px -5px 3px -3px rgba(0, 0, 0, 0.5), inset 4px 5px 2px -2px rgba(255, 230, 80,1)';
daytimeBackgrondRef.current[0].style.transform = 'translateX(0)';
daytimeBackgrondRef.current[1].style.transform = 'translateX(0)';
daytimeBackgrondRef.current[2].style.transform = 'translateX(0)';
cloudRef.current.style.transform = 'translateY(10px)';
cloudLightRef.current.style.transform = 'translateY(10px)';
componentsRef.current.style.backgroundColor = 'rgba(70, 133, 192,1)';
moonRef.current[0].style.opacity = '0';
moonRef.current[1].style.opacity = '0';
moonRef.current[2].style.opacity = '0';
starsRef.current.style.transform = 'translateY(-125px)';
starsRef.current.style.opacity = '0';
onChange(true);
} else {
mainButtonRef.current.style.transform = 'translateX(110px)';
mainButtonRef.current.style.backgroundColor = 'rgba(195, 200,210,1)';
mainButtonRef.current.style.boxShadow =
'3px 3px 5px rgba(0, 0, 0, 0.5), inset -3px -5px 3px -3px rgba(0, 0, 0, 0.5), inset 4px 5px 2px -2px rgba(255, 255, 210,1)';
daytimeBackgrondRef.current[0].style.transform = 'translateX(110px)';
daytimeBackgrondRef.current[1].style.transform = 'translateX(80px)';
daytimeBackgrondRef.current[2].style.transform = 'translateX(50px)';
cloudRef.current.style.transform = 'translateY(80px)';
cloudLightRef.current.style.transform = 'translateY(80px)';
componentsRef.current.style.backgroundColor = 'rgba(25,30,50,1)';
moonRef.current[0].style.opacity = '1';
moonRef.current[1].style.opacity = '1';
moonRef.current[2].style.opacity = '1';
starsRef.current.style.transform = 'translateY(-62.5px)';
starsRef.current.style.opacity = '1';
onChange(false);
}
setIsClicked(true);
setTimeout(function () {
setIsClicked(false);
}, 500);
};
// 移出事件
const handleComponentsMouseOut = () => {
if (isClicked) {
return;
}
if (isDark) {
mainButtonRef.current.style.transform = 'translateX(110px)';
daytimeBackgrondRef.current[0].style.transform = 'translateX(110px)';
daytimeBackgrondRef.current[1].style.transform = 'translateX(80px)';
daytimeBackgrondRef.current[2].style.transform = 'translateX(50px)';
starRef.current[0].style.top = '11px';
starRef.current[0].style.left = '39px';
starRef.current[1].style.top = '39px';
starRef.current[1].style.left = '91px';
starRef.current[2].style.top = '26px';
starRef.current[2].style.left = '19px';
starRef.current[3].style.top = '37px';
starRef.current[3].style.left = '66px';
starRef.current[4].style.top = '21px';
starRef.current[4].style.left = '75px';
starRef.current[5].style.top = '51px';
starRef.current[5].style.left = '38px';
} else {
mainButtonRef.current.style.transform = 'translateX(0px)';
daytimeBackgrondRef.current[0].style.transform = 'translateX(0px)';
daytimeBackgrondRef.current[1].style.transform = 'translateX(0px)';
daytimeBackgrondRef.current[2].style.transform = 'translateX(0px)';
cloudListRef.current[0].style.right = '-20px';
cloudListRef.current[0].style.bottom = '10px';
cloudListRef.current[1].style.right = '-10px';
cloudListRef.current[1].style.bottom = '-25px';
cloudListRef.current[2].style.right = '20px';
cloudListRef.current[2].style.bottom = '-40px';
cloudListRef.current[3].style.right = '50px';
cloudListRef.current[3].style.bottom = '-35px';
cloudListRef.current[4].style.right = '75px';
cloudListRef.current[4].style.bottom = '-60px';
cloudListRef.current[5].style.right = '110px';
cloudListRef.current[5].style.bottom = '-50px';
cloudListRef.current[6].style.right = '-20px';
cloudListRef.current[6].style.bottom = '10px';
cloudListRef.current[7].style.right = '-10px';
cloudListRef.current[7].style.bottom = '-25px';
cloudListRef.current[8].style.right = '20px';
cloudListRef.current[8].style.bottom = '-40px';
cloudListRef.current[9].style.right = '50px';
cloudListRef.current[9].style.bottom = '-35px';
cloudListRef.current[10].style.right = '75px';
cloudListRef.current[10].style.bottom = '-60px';
cloudListRef.current[11].style.right = '110px';
cloudListRef.current[11].style.bottom = '-50px';
}
};
// 移入事件
const handleComponentsMouseMove = () => {
if (isClicked) return;
if (isDark) {
mainButtonRef.current.style.transform = 'translateX(100px)';
daytimeBackgrondRef.current[0].style.transform = 'translateX(100px)';
daytimeBackgrondRef.current[1].style.transform = 'translateX(73px)';
daytimeBackgrondRef.current[2].style.transform = 'translateX(46px)';
starRef.current[0].style.top = '10px';
starRef.current[0].style.left = '36px';
starRef.current[1].style.top = '40px';
starRef.current[1].style.left = '87px';
starRef.current[2].style.top = '26px';
starRef.current[2].style.left = '16px';
starRef.current[3].style.top = '38px';
starRef.current[3].style.left = '63px';
starRef.current[4].style.top = '20.5px';
starRef.current[4].style.left = '72px';
starRef.current[5].style.top = '51.5px';
starRef.current[5].style.left = '35px';
} else {
mainButtonRef.current.style.transform = 'translateX(10px)';
daytimeBackgrondRef.current[0].style.transform = 'translateX(10px)';
daytimeBackgrondRef.current[1].style.transform = 'translateX(7px)';
daytimeBackgrondRef.current[2].style.transform = 'translateX(4px)';
cloudListRef.current[0].style.right = '-24px';
cloudListRef.current[0].style.bottom = '10px';
cloudListRef.current[1].style.right = '-12px';
cloudListRef.current[1].style.bottom = '-27px';
cloudListRef.current[2].style.right = '17px';
cloudListRef.current[2].style.bottom = '-43px';
cloudListRef.current[3].style.right = '46px';
cloudListRef.current[3].style.bottom = '-39px';
cloudListRef.current[4].style.right = '70px';
cloudListRef.current[4].style.bottom = '-65px';
cloudListRef.current[5].style.right = '109px';
cloudListRef.current[5].style.bottom = '-54px';
cloudListRef.current[6].style.right = '-23px';
cloudListRef.current[6].style.bottom = '10px';
cloudListRef.current[7].style.right = '-11px';
cloudListRef.current[7].style.bottom = '-26px';
cloudListRef.current[8].style.right = '18px';
cloudListRef.current[8].style.bottom = '-42px';
cloudListRef.current[9].style.right = '47px';
cloudListRef.current[9].style.bottom = '-38px';
cloudListRef.current[10].style.right = '74px';
cloudListRef.current[10].style.bottom = '-64px';
cloudListRef.current[11].style.right = '110px';
cloudListRef.current[11].style.bottom = '-55px';
}
};
return (
<div
className={`day-night-switch-contanier ${className ? className : ''}`}
ref={contanierRef}
style={style}
>
<div
className="components"
onClick={() => handleComponentsClick(isDark ? 'light' : 'dark')}
onMouseOut={handleComponentsMouseOut}
onMouseMove={handleComponentsMouseMove}
>
<div className="main-button">
<div className="moon"></div>
<div className="moon"></div>
<div className="moon"></div>
</div>
<div className="daytime-backgrond"></div>
<div className="daytime-backgrond"></div>
<div className="daytime-backgrond"></div>
<div className="cloud">
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
</div>
<div className="cloud-light">
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
<div className="cloud-son"></div>
</div>
<div className="stars">
<div className="star big">
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
</div>
<div className="star big">
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
</div>
<div className="star medium">
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
</div>
<div className="star medium">
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
</div>
<div className="star small">
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
</div>
<div className="star small">
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
<div className="star-son"></div>
</div>
</div>
</div>
</div>
);
};
export default DayNightSwitch;
点击 展开/收起 css 代码
.day-night-switch-contanier {
width: 180px;
height: 70px;
}
.day-night-switch-contanier .components {
position: fixed;
width: 180px;
height: 70px;
background-color: rgba(70, 133, 192, 1);
border-radius: 100px;
box-shadow: inset 0 0 5px 3px rgba(0, 0, 0, 0.5);
overflow: hidden;
transition: 0.7s;
transition-timing-function: cubic-bezier(0, 0.5, 1, 1);
cursor: pointer;
}
.day-night-switch-contanier .components .main-button {
margin: 7.5px 0 0 7.5px;
width: 55px;
height: 55px;
background-color: rgba(255, 195, 35, 1);
border-radius: 50%;
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5), inset -3px -5px 3px -3px rgba(0, 0, 0, 0.5),
inset 4px 5px 2px -2px rgba(255, 230, 80, 1);
transition: 1s;
transition-timing-function: cubic-bezier(0.56, 1.35, 0.52, 1);
}
.day-night-switch-contanier .components .moon {
position: absolute;
background-color: rgba(150, 160, 180, 1);
box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 0, 0.3);
border-radius: 50%;
transition: 0.5s;
opacity: 0;
}
.day-night-switch-contanier .components .moon:nth-child(1) {
top: 7.5px;
left: 25px;
width: 12.5px;
height: 12.5px;
}
.day-night-switch-contanier .components .moon:nth-child(2) {
top: 20px;
left: 7.5px;
width: 20px;
height: 20px;
}
.day-night-switch-contanier .components .moon:nth-child(3) {
top: 32.5px;
left: 32.5px;
width: 12.5px;
height: 12.5px;
}
.day-night-switch-contanier .components .daytime-backgrond {
position: absolute;
border-radius: 50%;
transition: 1s;
transition-timing-function: cubic-bezier(0.56, 1.35, 0.52, 1);
}
.day-night-switch-contanier .components .daytime-backgrond:nth-child(2) {
top: -20px;
left: -20px;
width: 110px;
height: 110px;
background-color: rgba(255, 255, 255, 0.2);
z-index: -2;
}
.day-night-switch-contanier .components .daytime-backgrond:nth-child(3) {
top: -32.5px;
left: -17.5px;
width: 135px;
height: 135px;
background-color: rgba(255, 255, 255, 0.1);
z-index: -3;
}
.day-night-switch-contanier .components .daytime-backgrond:nth-child(4) {
top: -45px;
left: -15px;
width: 160px;
height: 160px;
background-color: rgba(255, 255, 255, 0.05);
z-index: -4;
}
.day-night-switch-contanier .components .cloud,
.cloud-light {
transform: translateY(10px);
transition: 1s;
transition-timing-function: cubic-bezier(0.56, 1.35, 0.52, 1);
}
.day-night-switch-contanier .components .cloud-son {
position: absolute;
background-color: #fff;
border-radius: 50%;
z-index: -1;
transition: transform 6s, right 1s, bottom 1s;
}
.day-night-switch-contanier .components .cloud-son:nth-child(6n + 1) {
right: -20px;
bottom: 10px;
width: 50px;
height: 50px;
}
.day-night-switch-contanier .components .cloud-son:nth-child(6n + 2) {
right: -10px;
bottom: -25px;
width: 60px;
height: 60px;
}
.day-night-switch-contanier .components .cloud-son:nth-child(6n + 3) {
right: 20px;
bottom: -40px;
width: 60px;
height: 60px;
}
.day-night-switch-contanier .components .cloud-son:nth-child(6n + 4) {
right: 50px;
bottom: -35px;
width: 60px;
height: 60px;
}
.day-night-switch-contanier .components .cloud-son:nth-child(6n + 5) {
right: 75px;
bottom: -60px;
width: 75px;
height: 75px;
}
.day-night-switch-contanier .components .cloud-son:nth-child(6n + 6) {
right: 110px;
bottom: -50px;
width: 60px;
height: 60px;
}
.day-night-switch-contanier .components .cloud {
z-index: -2;
}
.day-night-switch-contanier .components .cloud-light {
position: absolute;
right: 0px;
bottom: 25px;
opacity: 0.5;
z-index: -3; /*transform: rotate(-5deg);*/
}
.day-night-switch-contanier .components .stars {
transform: translateY(-125px);
z-index: -2;
transition: 1s;
transition-timing-function: cubic-bezier(0.56, 1.35, 0.52, 1);
}
.day-night-switch-contanier .components .big {
--size: 7.5px;
}
.day-night-switch-contanier .components .medium {
--size: 5px;
}
.day-night-switch-contanier .components .small {
--size: 3px;
}
.day-night-switch-contanier .components .star {
position: absolute;
width: calc(2 * var(--size));
height: calc(2 * var(--size));
}
.day-night-switch-contanier .components .star:nth-child(1) {
top: 11px;
left: 39px;
animation-name: dayNightStart;
animation-duration: 3.5s;
}
.day-night-switch-contanier .components .star:nth-child(2) {
top: 39px;
left: 91px;
animation-name: dayNightStart;
animation-duration: 4.1s;
}
.day-night-switch-contanier .components .star:nth-child(3) {
top: 26px;
left: 19px;
animation-name: dayNightStart;
animation-duration: 4.9s;
}
.day-night-switch-contanier .components .star:nth-child(4) {
top: 37px;
left: 66px;
animation-name: dayNightStart;
animation-duration: 5.3s;
}
.day-night-switch-contanier .components .star:nth-child(5) {
top: 21px;
left: 75px;
animation-name: dayNightStart;
animation-duration: 3s;
}
.day-night-switch-contanier .components .star:nth-child(6) {
top: 51px;
left: 38px;
animation-name: dayNightStart;
animation-duration: 2.2s;
}
@keyframes dayNightStart {
0%,
20% {
transform: scale(0);
}
20%,
100% {
transform: scale(1);
}
}
.day-night-switch-contanier .components .star-son {
float: left;
}
.day-night-switch-contanier .components .star-son:nth-child(1) {
--pos: left 0;
}
.day-night-switch-contanier .components .star-son:nth-child(2) {
--pos: right 0;
}
.day-night-switch-contanier .components .star-son:nth-child(3) {
--pos: 0 bottom;
}
.day-night-switch-contanier .components .star-son:nth-child(4) {
--pos: right bottom;
}
.day-night-switch-contanier .components .star-son {
width: var(--size);
height: var(--size);
background-image: radial-gradient(
circle var(--size) at var(--pos),
transparent var(--size),
#fff
);
}
.day-night-switch-contanier .components .star {
transform: scale(1);
transition-timing-function: cubic-bezier(0.56, 1.35, 0.52, 1);
transition: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-timing-function: linear;
}
.day-night-switch-contanier .components .twinkle {
transform: scale(0);
}
← 简单的文本缩略+高亮 简单的防抖输入 →