WEBLEB
Home
Editor
Login
Pro
English
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Recording Toggle
966
Andev.web
Open In Editor
Publish Your Code
Recommended
7 November 2024
toggle with light animation
9 September 2024
Emoji Toggle
14 September 2024
Minion Eye Toggle
HTML
Copy
Andev Web
CSS
Copy
* { border: 0; box-sizing: border-box; margin: 0; padding: 0; } :root { --hue: 223; --red: hsl(3,90%,50%); --white: hsl(0,0%,100%); --primary: hsl(var(--hue),90%,50%); --primary-t: hsla(var(--hue),90%,50%,0); --gray1: hsl(var(--hue),10%,90%); --gray2: hsl(var(--hue),10%,80%); --gray3: hsl(var(--hue),10%,70%); --gray4: hsl(var(--hue),10%,60%); --gray5: hsl(var(--hue),10%,50%); --gray6: hsl(var(--hue),10%,40%); --gray7: hsl(var(--hue),10%,30%); --gray8: hsl(var(--hue),10%,20%); --gray9: hsl(var(--hue),10%,10%); --trans-dur: 0.3s; --trans-timing: cubic-bezier(0.65,0,0.35,1); font-size: calc(28px + (60 - 28) * (100vw - 320px) / (3840 - 320)); } body, button { color: var(--gray9); font: 1em/1.5 "DM Sans", sans-serif; transition: background-color var(--trans-dur), color var(--trans-dur); } body { background-color: var(--gray1); } .recorder { background-color: transparent; cursor: pointer; display: flex; align-items: center; margin: auto; outline: transparent; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent; } .recorder__label-start, .recorder__label-end { display: block; position: relative; } .recorder__label-start, .recorder__label-end-text { transition: opacity var(--trans-dur); } .recorder__label-start { margin-inline: 0 0.5em; } .recorder__label-end { margin-inline: 0.5em 0; } .recorder__label-end-text { opacity: 0.4; } .recorder__label-end-text + .recorder__label-end-text { opacity: 0; position: absolute; top: 0; left: 0; } [dir=rtl] .recorder__label-end-text + .recorder__label-end-text { right: 0; left: auto; } .recorder__switch { background-color: var(--white); border-radius: 0.75em; box-shadow: 0 0 0 0.125em var(--primary-t), 0 0.25em 0.25em rgba(0, 0, 0, 0.1); display: flex; padding: 0.25em; width: 2.5em; height: 1.5em; } .recorder__switch, .recorder__switch-handle { transition: background-color var(--trans-dur), box-shadow var(--trans-dur), transform var(--trans-dur) var(--trans-timing), transform-origin var(--trans-dur) var(--trans-timing); } .recorder__switch-handle { background-color: var(--gray3); border-radius: 50%; display: block; transform-origin: 0 0.5em; width: 1em; height: 1em; } [dir=rtl] .recorder__switch-handle { transform-origin: 100% 0.5em; } .recorder__timer { display: block; overflow: visible; width: 100%; height: auto; } .recorder__timer-ring { transition: r var(--trans-dur) var(--trans-timing), stroke-dasharray var(--trans-dur) var(--trans-timing), stroke-dashoffset var(--trans-dur) var(--trans-timing), stroke-width var(--trans-dur) var(--trans-timing); } .recorder:focus-visible .recorder__switch { box-shadow: 0 0 0 0.125em var(--primary), 0 0.25em 0.25em rgba(0, 0, 0, 0.1); } .recorder:active .recorder__switch-handle { transform: scaleX(1.5); } .recorder[aria-pressed=true] .recorder__label-start { opacity: 0.4; } .recorder[aria-pressed=true] .recorder__label-end-text { opacity: 0; } .recorder[aria-pressed=true] .recorder__label-end-text + .recorder__label-end-text { opacity: 1; } .recorder[aria-pressed=true] .recorder__switch-handle { background-color: var(--red); transform: translateX(100%); transform-origin: 100% 0.5em; } [dir=rtl] .recorder[aria-pressed=true] .recorder__switch-handle { transform: translateX(-100%); transform-origin: 0 0.5em; } .recorder[aria-pressed=true] .recorder__timer-ring { r: 6.5px; stroke-width: 3px; } .recorder[aria-pressed=true]:active .recorder__switch-handle { transform: translateX(100%) scaleX(1.5); } [dir=rtl] .recorder[aria-pressed=true]:active .recorder__switch-handle { transform: translateX(-100%) scaleX(1.5); } /* Dark theme */ @media (prefers-color-scheme: dark) { body, button { color: var(--gray1); } body { background-color: var(--gray9); } .recorder__switch { background-color: var(--gray8); box-shadow: 0 0 0 0.125em var(--primary-t), 0 0.25em 0.25em rgba(0, 0, 0, 0.2); } .recorder__switch-handle { background-color: var(--gray6); } .recorder:focus-visible .recorder__switch { box-shadow: 0 0 0 0.125em var(--primary), 0 0.25em 0.25em rgba(0, 0, 0, 0.2); } }
JS
Copy
import React, { StrictMode, useEffect, useState } from "https://esm.sh/react"; import { createRoot } from "https://esm.sh/react-dom/client"; createRoot(document.getElementById("root")).render(React.createElement(StrictMode, null, React.createElement(RecordingToggle, null))); function RecordingToggle() { const [recording, setRecording] = useState(false); const [time, setTime] = useState(0); const timeMax = 60; const [timeStopped, setTimeStopped] = useState(0); const circumference = recording ? 40.84 : 50.27; const circumferencePart = recording ? 1 - (time / timeMax) : 1; const strokeDashArray = `${circumference} ${circumference}`; const strokeDashOffset = +(circumference * circumferencePart).toFixed(2); function timeFormatted() { const timeToDisplay = recording ? time : timeStopped; const minutes = `0${Math.floor(timeToDisplay / 60)}`.slice(-2); const seconds = `0${timeToDisplay % 60}`.slice(-2); return `${minutes}:${seconds}`; } // timer loop useEffect(() => { let frameId = 0; if (recording) { setTimeStopped(0); const render = () => { setTime((time) => time + 1); // allow the time to be shown in the transition when stopping setTimeStopped((time) => time + 1); frameId = setTimeout(render, 1e3); }; frameId = setTimeout(render, 1e3); } else { setTime(0); clearTimeout(frameId); } return () => { clearTimeout(frameId); }; }, [recording]); // stop automatically if time hits limit useEffect(() => { if (time >= timeMax) { setRecording(false); } }, [time]); return (React.createElement("button", { className: "recorder", type: "button", "aria-pressed": recording, onClick: () => setRecording(!recording) }, React.createElement("span", { className: "recorder__label-start", "aria-hidden": recording }, "Stop"), React.createElement("span", { className: "recorder__switch" }, React.createElement("span", { className: "recorder__switch-handle" }, React.createElement("svg", { className: "recorder__timer", viewBox: "0 0 16 16", width: "16px", height: "16px", "aria-hidden": "true" }, React.createElement("g", { fill: "none", strokeLinecap: "round", strokeWidth: "0", transform: "rotate(-90,8,8)" }, React.createElement("circle", { className: "recorder__timer-ring", stroke: "hsla(0,0%,100%,0.3)", cx: "8", cy: "8", r: "8" }), React.createElement("circle", { className: "recorder__timer-ring", stroke: "hsla(0,0%,100%,0.5)", cx: "8", cy: "8", r: "8", strokeDasharray: strokeDashArray, strokeDashoffset: strokeDashOffset }))))), React.createElement("span", { className: "recorder__label-end", "aria-hidden": !recording }, React.createElement("span", { className: "recorder__label-end-text" }, "Record"), React.createElement("span", { className: "recorder__label-end-text" }, timeFormatted())))); }