WEBLEB
Início
Editor
Entrar
Pro
Português
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Alternância de gravação
1339
Andev.web
Abrir no Editor
Publique Seu Código
Recomendado
26 July 2025
Exemplo de animação de botão HTML
26 September 2025
Animação de carregamento CSS com imagem de fundo
22 June 2025
Blog Criativo: Design, Podcast e Portfólio HTML
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())))); }