Sesler ve haptik

Tarayıcıda çaldığı anda sentezlenen 24 arayüz sesi: ses dosyası yok, barındıracak bir şey yok, 7 KB'lık tek modül. Her ses, do majör pentatonik üzerinde iki üç sinüs bileşeni, yumuşak bir atak ve filtrelenmiş bir tıktan oluşan küçük bir tariftir, −14 dBFS'de miksajlanır. Sesler ziyaretçi açana kadar kapalıdır, tarayıcı başına hatırlanır ve bir kullanıcı hareketinden önce asla çalmaz. Hareket ve ses ayrı tercihlerdir: prefers-reduced-motion onları değiştirmez.

Bu tarayıcıda sesler kapalı. Aşağıdaki çalma düğmeleri her durumda çalışır; bileşenler ve data-sound yalnızca bu açıkken ses verir.

Kurulum

npx shadcn@latest add https://goodui.dev/r/sounds.json

lib/goodui-sounds.ts içine iner. sound prop'u olan bileşenler onu bağımlılık olarak listeler, yani onlarla birlikte gelir. Modül tembel yüklenir ve yalnızca bir bileşende sound açıksa; yani hiç kullanmayan bir uygulama bundan hiçbir şey taşımaz.

Tüm sesler

Dinlemek için bir karta tıkla. Dalga biçimi sesin çevrimdışı render'ıdır, sessiz olanlar okunabilsin diye normalize edilmiştir.

Bileşenler

Bileşenin varsayılanını çalmak için sound geç ya da başka birini seçmek için bir ses adı ver. Geçmediğin sürece her şey kapalıdır.

  • button: tıklamada tap
  • stateful-button: tap, sonra sonuca göre success ya da error
  • switch: toggle-on / toggle-off
  • checkbox: işaretlenince pop, kaldırılınca tick
  • tabs: seçimde tick
  • rating: imlecin altındaki her yıldızda tick, sıra boyunca yükselen perde, seçimde pop
  • number-input: her adımda tick, + için daha tiz, − için daha pes
  • dialog: open / close
  • toast-stack (Toaster): her yeni toast için success, error, warning ya da notify; yükleme toast'u sonuçlanınca tekrar
  • confetti: patlamayla celebrate (fireConfetti({ sound: true }) ya da <Confetti sound />)
  • hold-to-confirm: basılı tutma başlayınca tick, tamamlanınca confirm
  • copy-button: kopyalamadan sonra pop, başarısız olursa error
<Button sound onClick={save}>Save</Button>
<Switch sound label="SMS reminders" />
<Rating sound defaultValue={4} />
<Dialog sound title="Cancel this booking?" onConfirm={cancel} trigger={<Button variant="outline">Cancel</Button>} />
<Toaster sound />                   {/* success, error, warning or notify per toast */}
<HoldToConfirm sound="unlock" onConfirm={deleteAccount}>Delete account</HoldToConfirm>

Anahtarı kullanıcıya ver

SoundProvider bileşenini bir kez bağla ve ayarlarına ya da üst çubuğuna bir anahtar koy. Ziyaretçinin seçimi localStorage içine kaydedilir ve uygulama varsayılanının önüne geçer.

app/layout.tsx
import { SoundProvider } from "@/lib/goodui-sounds"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <SoundProvider>{children}</SoundProvider>   {/* enabled: app default, persist, volume, attributes */}
      </body>
    </html>
  )
}
components/sound-toggle.tsx
"use client"
import { useSound } from "@/lib/goodui-sounds"

export function SoundToggle() {
  const { enabled, toggle, play } = useSound()
  return (
    <button type="button" role="switch" aria-checked={enabled} onClick={() => { toggle(); play(enabled ? "toggle-off" : "toggle-on", { force: true }) }}>
      Sounds {enabled ? "on" : "off"}
    </button>
  )
}

Her öğe

Provider bağlıyken her öğe kod yazmadan ses çalabilir. data-sound tıklamada çalar (toggle tıklamadan sonra işaretli durumu okur), data-sound-hover imleç üzerine gelince.

<button data-sound="tap">Book</button>
<input type="checkbox" data-sound="toggle" />
<a href="/pricing" data-sound-hover>Pricing</a>   {/* "hover", very subtle */}

API

import { play, useSound, setSoundEnabled, isSoundEnabled, setSoundVolume, SOUND_NAMES, type SoundName } from "@/lib/goodui-sounds"

play("success")                          // false when sounds are off, before the first gesture, or without Web Audio
play("tick", { rate: 1.1, volume: 0.6 }) // rate: pitch and speed, volume: 0..1 on top of the -14 dBFS bus
play("notify", { force: true })          // previews and settings: ignores the toggle
setSoundEnabled(true)                    // saved in localStorage (unless <SoundProvider persist={false} />)
setSoundVolume(0.5)

const { enabled, toggle, setEnabled, play } = useSound()   // reads "off" during SSR

Kendininkini tasarla

Bir tarif, seslerden oluşan bir listedir: osilatör bileşenleri ya da filtrelenmiş gürültü, her birinin başlangıcı, süresi, seviyesi ve isteğe bağlı bir kayması var. tone() oktav bileşeniyle bir nota verir, noise() bir tık ya da bir vınlama. Kısa (350 ms altı), sessiz (0,5 altı seviyeler) ve dizide tut, yerleşik seslerin yanına otursun.

import { defineSound, tone, noise, play } from "@/lib/goodui-sounds"

// A cash-register "cha-ching": a click, then two notes a fifth apart with a bright octave.
defineSound("cha-ching", [
  noise(0.02, { gain: 0.2, filter: 3000 }),
  ...tone(1046.5, 0.12, { at: 0.03, gain: 0.4, shine: 0.3 }),   // C6
  ...tone(1568, 0.26, { at: 0.11, gain: 0.42, shine: 0.3 }),    // G6
])

play("cha-ching")                 // or <button data-sound="cha-ching">
defineSound("tap", [...])         // built-ins can be replaced the same way

Ses alanları: wave (sine, triangle, square, sawtooth ya da noise), freq ve to Hz cinsinden, glide, at, dur ve attack saniye cinsinden, gain 0..1, alçak geçiren (osilatörler) ya da bant geçiren (gürültü) filtre için filter / filterTo / q, detune cent cinsinden.

Test

renderSound(name) bir tarifi OfflineAudioContext üzerinden render eder ve buffer'ı döndürür, yani bir test hiçbir şey çalmadan sesin ne sessiz ne de kırpılmış olduğunu doğrulayabilir.

const buffer = await renderSound("success")
const data = buffer.getChannelData(0)
const peak = Math.max(...data.map(Math.abs))   // the built-ins stay under 0.3 at volume 1

goodui Native: haptik

Aynı adlar goodui Native'de haptik desenlerdir (foundation/haptics.ts); expo-haptics impact, notification ve selection dokunuşlarından zamanlamayla kurulmuştur. foundation/sounds içindeki cue(name) deseni ve sesi tek çağrıda çalar, yani bir kez yazılan ekran her platformda doğru hissettirir. Sesler uygulama setSoundsEnabled(true) çağırana kadar kapalıdır; iOS ve Android'de uygulamanın setSoundPlayer ile kaydettiği bir oynatıcıdan çalarlar (kendi kısa dosyalarıyla expo-audio), Expo web'de ise buradaki gibi sentezlenirler.

  • tap: impact light
  • select, tick, count-tick: selection
  • toggle-on / toggle-off: impact light → medium / medium → light, 60 ms arayla
  • success, warning, error: aynı türde notification
  • notify: iki light impact, 90 ms arayla
  • send / receive: light → rigid / soft → light
  • delete: impact heavy
  • undo: light → medium
  • pop: impact rigid
  • whoosh / slide: üç / iki selection
  • confirm: impact medium, ardından notification success
  • open / close: soft → light / light → soft
  • drag-start / drop: impact medium / heavy → light
  • celebrate: light, light, medium, ardından notification success
  • unlock: rigid, rigid, ardından notification success
  • hover: hiçbir şey (dokunmatikte hover yok)
import { cue, setSoundsEnabled } from "@/components/goodui-native/foundation"

setSoundsEnabled(settings.sounds)        // from your settings screen
cue("confirm")                           // haptic pattern + sound, one vocabulary