<label class="switch donut">
    <input type="checkbox">
    <div>
        <canvas></canvas>
    </div>
    <span>Switch</span>
</label>

<label class="switch">
    <input type="checkbox" checked>
    <div>
        <canvas></canvas>
    </div>
    <span>Switch</span>
</label>

<!-- dribbble -->
<a class="dribbble" href="https://dribbble.com/shots/7077918-3D-Switch-animation-2" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
.switch {
    --background: #171C28;
    --background-active: #275EFE;
    --shadow: rgba(18, 22, 33, .2);
    --shadow-dark: rgba(18, 22, 33, .6);
    cursor: pointer;
    display: flex;
    -webkit-tap-highlight-color: transparent;
    //zoom for demo
    zoom: 1.4;
    input[type="checkbox"] {
        display: none;
        & + div {
            width: 40px;
            height: 24px;
            border-radius: 12px;
            overflow: hidden;
            position: relative;
            transition: transform 300ms ease;
            background: var(--background);
            -webkit-mask-image: -webkit-radial-gradient(white, black);
            &:before,
            canvas {
                left: 0;
                top: 0;
                display: block;
                position: absolute;
                transition: transform .45s ease, filter .45s ease;
            }
            &:before {
                --x: -100%;
                --s: 1;
                content: '';
                width: 24px;
                height: 24px;
                border-radius: 50%;
                background: var(--background-active);
                transform: translateX(var(--x)) scale(var(--s));
            }
            canvas {
                --x: -8px;
                display: block;
                filter: drop-shadow(0 1px 2px var(--shadow));
                transform: translate(var(--x), -8px);
            }
        }
        &:checked {
            & + div {
                &:before {
                    --s: 3;
                    --x: 0;
                }
                canvas {
                    --x: 8px;
                    filter: drop-shadow(0 1px 2px var(--shadow-dark));
                }
            }
        }
    }
    &:active {
        input[type="checkbox"] {
            & + div {
                transform: scale(.92);
            }
        }
    }
    span {
        line-height: 24px;
        font-size: 14px;
        font-weight: 500;
        display: block;
        margin: 0 0 0 8px;
    }
}

html {
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
}

* {
    box-sizing: inherit;
    &:before,
    &:after {
        box-sizing: inherit;
    }
}

// Center & dribbble
body {
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    background: #2F3545;
    font-family: 'Roboto', Arial;
    color: #99A3BA;
    .switch {
        &:not(:last-child) {
            margin-bottom: 16px;
        }
    }
    .dribbble {
        position: fixed;
        display: block;
        right: 20px;
        bottom: 20px;
        img {
            display: block;
            height: 28px;
        }
    }
}
View Compiled
let Ring3D = (innerRadius, outerRadius, heigth, Segments) => {

    let extrudeSettings = {
            depth: heigth,
            bevelEnabled: false,
            curveSegments: Segments
        },
        arcShape = new THREE.Shape();

    arcShape.moveTo(outerRadius, 0);
    arcShape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);

    let holePath = new THREE.Path();
    holePath.moveTo(innerRadius, 0);
    holePath.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
    arcShape.holes.push(holePath);

    return new THREE.ExtrudeGeometry(arcShape, extrudeSettings);

}

$('.switch').each(function() {

    let toggle = $(this),
        donut = toggle.hasClass('donut'),
        input = toggle.children('input'),
        $canvas = toggle.find('canvas'),
        canvas = $canvas[0],
        renderer = new THREE.WebGLRenderer({
            canvas: canvas,
            context: canvas.getContext('webgl2'),
            antialias: true,
            alpha: true
        });

    renderer.setSize(40, 40);
    renderer.setPixelRatio(8);

    renderer.shadowMap.enabled = true;

    let scene = new THREE.Scene();
        camera = new THREE.PerspectiveCamera(45, 40 / 40, 0.1, 1000);

    camera.position.z = 92;

    let shape = donut ? Ring3D(7, 15, 8, 60) : new THREE.CylinderGeometry(15, 15, 8, 60);
    let material = new THREE.MeshPhongMaterial({
        color: 0xE4ECFA,
        shininess: 20,
        opacity: .96,
        transparent: true
    });
    let object = new THREE.Mesh(shape, material);

    if(!donut) {
        object.rotation.x = THREE.Math.degToRad(90);
    } else {
        shape.translate(0, 0, -4);
    }

    scene.add(object);

    let lightTop = new THREE.DirectionalLight(0xFFFFFF, .6);
    lightTop.position.set(0, 60, 60);
    lightTop.castShadow = true;
    scene.add(lightTop);

    let right = new THREE.DirectionalLight(0xFFFFFF, .5);
    right.position.set(20, 20, 40);
    right.castShadow = true;
    scene.add(right);

    let left = new THREE.DirectionalLight(0xFFFFFF, .5);
    left.position.set(-20, 20, 40);
    left.castShadow = true;
    scene.add(left);

    let active = new THREE.DirectionalLight(0x275EFE, .8);
    active.position.set(0, -80, 20);
    active.castShadow = true;
    scene.add(active);

    scene.add(new THREE.AmbientLight(0x6C7486));

    var render = function() {

        requestAnimationFrame(render);

        TweenMax.render();

        renderer.render(scene, camera);

    };

    render();

    input.on('change', e => {
        let checked = input.is(':checked'),
            easing = Elastic.easeOut.config(checked ? .8 : 1.6, checked ? .28 : .8);
        if(donut) {
            TweenMax.to(object.rotation, 1, {
                ease: easing,
                y: !checked ? THREE.Math.degToRad(0) : THREE.Math.degToRad(90)
            }, 0);
        } else {
            TweenMax.to(object.rotation, 1, {
                ease: easing,
                z: !checked ? THREE.Math.degToRad(0) : THREE.Math.degToRad(90)
            }, 0);
        }
    }).trigger('change');

});

External CSS

  1. https://fonts.googleapis.com/css?family=Roboto:400,500,700&amp;display=swap

External JavaScript

  1. https://code.jquery.com/jquery-3.4.1.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/three.js/107/three.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js