WEBLEB
Início
Editor
Entrar
Pro
Português
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Mostradores de volume
266
kevekıbhack
Abrir no Editor
Publique Seu Código
Recomendado
23 November 2024
Página de destino - ImprovalAI
23 August 2025
Portfólio de Designer Criativo - Alex
17 July 2025
Arte de polígonos SVG: código gráfico vetorial
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; };