A cool, animated, draggable toggle switch implemented in React.
Playground:
See the Pen
React draggable toggle by Mikael Ainalem (@ainalem)
on CodePen.
How to use it:
1. The main react script.
const { useState } = React; const { useSpring, animated } = ReactSpring; const { useDrag } = ReactUseGesture; const WIDTH = 100; const RANGE = 50; function Toggle() { const [toggled, setToggled] = useState(false); const [{ x }, set] = useSpring(() => ({ x: 0, })); const RB_RANGE = 5; const rubberband = (x) => x > RB_RANGE ? RB_RANGE : RB_RANGE * (x / RB_RANGE) * (x / RB_RANGE); const clamp = (x) => { const dx = toggled ? x * -1 : x; if (dx < 0) { const rb = rubberband(Math.abs(dx)); return x < 0 ? -rb : rb; } if (dx > RANGE) { const diff = dx - RANGE; const rb = rubberband(diff); return x < 0 ? -RANGE - rb : RANGE + rb; } return x; }; // Set the drag hook and define component movement based on gesture data const bind = useDrag(({ down, movement: [mx], tap }) => { if (!down && tap) { set({ x: toggled ? 0 : 50 }); setToggled(!toggled); return; } const left = mx < 0; const mxclamped = clamp(mx); const aboveThreshold = Math.abs(mxclamped) > RANGE / 2; if (!down && aboveThreshold) { setToggled(mx > 0); } const offs = toggled ? RANGE : 0; const newX = offs + (down ? mxclamped : aboveThreshold ? (left ? -RANGE : RANGE) : 0); set({ x: newX }); }); // Bind it to a component return ( <animated.div style={{ backgroundColor: x.interpolate({ range: [0, RANGE], output: ["#FF4650", "#48EA8A"], }), borderRadius: 25, cursor: 'pointer', height: 50, overflow: "hidden", WebkitTapHighlightColor: 'transparent', width: WIDTH, }} > {/* drag area */} <animated.div {...bind()} style={{ height: 50, left: -50, margin: 0, position: "relative", width: 150, x, }} > {/* knob */} <animated.div style={{ backgroundColor: "#fff", clipPath: x.interpolate({ range: [0, RANGE], output: [ "polygon(43.34683% 18.69928%, 49.79018% 17.99435%, 56.05332% 18.56933%, 61.98362% 20.32509%, 67.42845% 23.16254%, 72.2352% 26.98255%, 76.25124% 31.68601%, 79.32396% 37.17381%, 81.30072% 43.34683%, 82.00565% 49.79018%, 81.43067% 56.05332%, 79.67491% 61.98362%, 76.83746% 67.42845%, 73.01745% 72.2352%, 68.31399% 76.25124%, 62.82619% 79.32396%, 56.65317% 81.30072%, 50.20982% 82.00565%, 43.94668% 81.43067%, 38.01638% 79.67491%, 32.57155% 76.83746%, 27.7648% 73.01745%, 23.74876% 68.31399%, 20.67604% 62.82619%, 18.69928% 56.65317%, 17.99435% 50.20982%, 18.56933% 43.94668%, 20.32509% 38.01638%, 23.16254% 32.57155%, 26.98255% 27.7648%, 31.68601% 23.74876%, 37.17381% 20.67604%)", "polygon(48.20592% 18.002%, 50% 18%, 51.79408% 18.002%, 57.60182% 20.39818%, 60% 26.20592%, 60.002% 30.65396%, 60% 35.102%, 60.002% 39.851%, 60% 44.6%, 60.002% 49.612%, 60% 54.624%, 60.002% 59.561%, 60% 64.498%, 60.002% 69.14604%, 60% 73.79408%, 57.60182% 79.60182%, 51.79408% 82.002%, 50% 82%, 48.20592% 82.002%, 42.39818% 79.60182%, 40% 73.79408%, 40.002% 69.14604%, 40% 64.498%, 40.002% 59.561%, 40% 54.624%, 40.002% 49.612%, 40% 44.6%, 40.002% 39.851%, 40% 35.102%, 40.002% 30.65396%, 40% 26.20592%, 42.39818% 20.39818%)", ], }), height: 50, left: 50, position: "absolute", width: 50, }} /> {/* hold */} <animated.div style={{ backgroundColor: x.interpolate({ range: [0, RANGE], output: ["#FF4650", "#48EA8A"], }), borderRadius: 6, height: 12, left: 69, position: "absolute", top: 19, transform: x.interpolate({ range: [0, RANGE], output: ["scaleX(1)", "scaleX(0)"], extrapolateRight: "clamp", }), width: 12, }} /> </animated.div> </animated.div> ); } function App() { return ( <div className="App"> <Toggle /> </div> ); } ReactDOM.render( <App />, document.getElementById("root") );
2. The necessary CSS styles.
.App { align-items: center; display: flex; justify-content: center; height: 100vh; text-align: center; } .App-logo { height: 40vmin; pointer-events: none; } @media (prefers-reduced-motion: no-preference) { .App-logo { animation: App-logo-spin infinite 20s linear; } } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .App-link { color: #61dafb; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
Preview:
Download Details:
Author: Mikael Ainalem
Live Demo: View The Demo
Download Link: Download The Source Code
Official Website: https://codepen.io/ainalem/pen/mdVOmOj
License: MIT