Draggable Toggle Switch In React

Category: Form , React
Author: Mikael Ainalem
Views Total: 7 views
Official Page: Go to website
Publish Date: June 24, 2020
License: MIT

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:

Draggable Toggle Switch In React

You Might Be Interested In:

Leave a Reply