WEBLEB
Accueil
Éditeur
Connexion
Pro
Français
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Basculement d'enregistrement
1100
Andev.web
Ouvrir dans l'éditeur
Publiez votre code
Recommandé
15 July 2025
Animation de basculement de triangle CSS
14 September 2024
Basculement des yeux de Minion
2 December 2023
Basculement roulant
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())))); }