WEBLEB
Início
Editor
Entrar
Pro
Português
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Mostradores de volume
18
kevekıbhack
Abrir no Editor
Publique Seu Código
Recomendado
26 July 2025
Exemplo de animação de botão HTML
17 July 2025
Página de login do CoffeeShop com efeito de vidro e Tailwind
28 December 2024
Uma página inicial de matriz criada com índices
HTML
Copy
CSS
Copy
/* :root kısmına yeni renkler ekleyin */ :root { /* ...diğer değişkenler... */ --purple: hsl(270, 90%, 60%); --pink: hsl(330, 90%, 60%); --yellow: hsl(60, 90%, 60%); --cyan: hsl(180, 90%, 60%); } /* Yeni renk sınıfları ekleyin */ .volume--rainbow .volume__dot--filled { --dot-color: var(--red); animation: rainbow 5s linear infinite; } .volume--gradient .volume__dot--filled { --dot-color: var(--blue); animation: gradientGlow 3s ease-in-out infinite alternate; } .volume--pastel .volume__dot--filled { --dot-color: var(--green); filter: brightness(1.2); } /* Animasyonlar */ @keyframes rainbow { 0% { background-color: var(--red); } 14% { background-color: var(--orange); } 28% { background-color: var(--yellow); } 42% { background-color: var(--green); } 57% { background-color: var(--blue); } 71% { background-color: var(--indigo); } 85% { background-color: var(--purple); } 100% { background-color: var(--red); } } @keyframes gradientGlow { 0% { box-shadow: 0 0 5px var(--blue); } 100% { box-shadow: 0 0 15px var(--cyan); } }
JS
Copy
import React, { StrictMode, useEffect, useRef, useState } from "https://esm.sh/react"; import { createRoot } from "https://esm.sh/react-dom/client"; import gsap from "https://esm.sh/gsap"; import { useGSAP } from "https://esm.sh/@gsap/react"; import { Draggable } from "https://esm.sh/gsap/Draggable"; gsap.registerPlugin(useGSAP, Draggable); createRoot(document.getElementById("root")!).render( <StrictMode> <main> <VolumeDial ticks={24} color="rainbow" volume={0.7} label="Party Mode" /> <VolumeDial ticks={16} color="gradient" volume={0.4} label="Neon Glow" /> <VolumeDial ticks={12} color="pastel" volume={0.2} label="Soft Tones" /> </main> </StrictMode> ); function VolumeDial({ color, ticks, volume = 0, label = "Volume", children }: VolumeDialProps) { const [isKeyDown, setIsKeydown] = useState(false); const dialRef = useRef<HTMLButtonElement>(null); const rootClass = `volume${color ? ` volume--${color}` : ''}`; // volume state const [vol, setVol] = useState(volume); const maxTick = Math.max(1, ticks) || 1; const tick = Math.max(0, vol * maxTick); const percent = `${Math.round(vol * 100)}%`; // dial angle const minAngle = -135; const maxAngle = 135; const angleSpan = maxAngle - minAngle; const startAngle = minAngle + vol * angleSpan; const [angle, setAngle] = useState(startAngle); // dots const dotSectorAngle = angleSpan / maxTick; const dotSectorAngleOffset = -angleSpan / 2 + dotSectorAngle / 2; const dotTickOffset = 0.5; const dotFillArray: boolean[] = []; for (let t = 0; t < ticks; ++t) { dotFillArray.push(t < tick - dotTickOffset); } // Color schemes const colorSchemes = { rainbow: [ '#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#4B0082', '#9400D3' ], gradient: [ '#FF00FF', '#FF007F', '#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF' ], pastel: [ '#FFD1DC', '#FFECB8', '#B5EAD7', '#C7CEEA', '#E2F0CB', '#FFDAC1', '#B5EAD7', '#F8B195' ], default: ['var(--dot-color)'] }; const getDotColor = (index: number) => { const scheme = colorSchemes[color as keyof typeof colorSchemes] || colorSchemes.default; return scheme[index % scheme.length]; }; // set the volume useEffect(() => { setVol((angle - minAngle) / angleSpan); }, [angle]); // initiate the draggable useEffect(() => { Draggable.create(dialRef.current, { type: "rotation", bounds: { minRotation: minAngle, maxRotation: maxAngle }, onDrag: function() { setAngle(this.rotation); } }); }, []); // run a transition when adjusting using arrow keys useGSAP(() => { if (isKeyDown) { gsap.to(dialRef.current, { rotation: angle, duration: 0.15 }); } }, [angle]); // starting angle useGSAP(() => { gsap.to(dialRef.current, { rotation: startAngle, duration: 0 }); }, []); function keyboardAction(e: React.KeyboardEvent<HTMLButtonElement>) { const up = e.code === "ArrowUp" || e.code === "ArrowRight"; const down = e.code === "ArrowDown" || e.code === "ArrowLeft"; if (up || down) { e.preventDefault(); setIsKeydown(true); } if (up) { setAngle(minAngle + Math.min(tick + 1, maxTick) / maxTick * angleSpan); } else if (down) { setAngle(minAngle + Math.max(0, tick - 1) / maxTick * angleSpan); } } return ( <div className={rootClass}> <div className="volume__control"> {dotFillArray.map((filled, i) => { const angle = dotSectorAngle * i + dotSectorAngleOffset; return ( <VolumeDialDot key={i} angle={angle} filled={filled} color={getDotColor(i)} /> ) })} <div className="volume__dial-wrap"> <button className="volume__dial" type="button" ref={dialRef} onKeyDown={keyboardAction} onKeyUp={() => setIsKeydown(false)} aria-description={percent} > <span className="volume__dial-label">{label}</span> </button> </div> </div> <div className="volume__content"> {children || <div className="volume__display">{percent}</div>} </div> </div> ); } function VolumeDialDot({ angle = 0, filled = false, color = '' }: VolumeDialDotProps) { const filledClass = filled ? " volume__dot--filled" : ""; const dotClass = `volume__dot${filledClass}`; const dotStyle = { transform: `rotate(${angle}deg)`, ...(color && filled ? { '--dot-color': color } : {}) }; return ( <div className={dotClass} style={dotStyle}></div> ); } interface VolumeDialProps { ticks: number; volume?: number; color?: string; label?: string; children?: React.ReactNode; }; interface VolumeDialDotProps { angle?: number; filled?: boolean; color?: string; };