Components/VoiceClip

VoiceClip

A voice message capsule you can play and scrub through.

VerifiedSince 0.6.3

Installation

pnpm add @talex-touch/tuffex
EXAMPLE.TYPESCRIPT
import { TxVoiceClip } from '@talex-touch/tuffex/voice-clip'
import '@talex-touch/tuffex/voice-clip/style.css'
import '@talex-touch/tuffex/base.css' // design tokens and resets, once per app

Usage

Basic

Pass the recording's URL and its known length. The capsule widens with the length, and one clip plays at a time on a page.

Loading demo...

Waveform sources and states

Pass peaks when the host already has levels, so the clip skips decoding; without src the clip is unavailable.

Loading demo...

Custom colours

Set the --tx-voice-clip-* variables on any ancestor; every clip inside reads them.

Loading demo...

Best Practices

  • Pass durationMs when you know it: the capsule has its final width and time before the element reads the file, so nothing shifts once it loads.
  • Pass peaks when the host already has levels (the level frames of a dictation, say) to save a read and a decode.
  • Pass peaks for remote recordings: the clip only reads local ones a second time (blob:, data:, tfile:, file:).
  • Localise the four label props; screen readers hear the position through the slider's aria-valuetext, and the play key's name follows its state.

API Reference

Props

PropTypeDefaultDescription
srcstring''The recording's URL; empty or failing to load makes the clip unavailable.
durationMsnumber-Known length in ms; sets the width, and the time text until the element reads the file.
peaksreadonly number[]-Amplitudes in 0–1, resampled to the bars that fit; omitted, a local recording is decoded.
disabledbooleanfalseBlocks playback and seeking; disabling a playing clip pauses it.
playLabelstring'Play voice message'Accessible name of the play key.
pauseLabelstring'Pause voice message'Accessible name of the play key while playing.
seekLabelstring'Playback position'Accessible name of the waveform slider.
unavailableLabelstring'Recording unavailable'Shown in place of the waveform while unavailable.

Events

EventPayloadDescription
play-Playback started.
pause-Playback paused, including when another clip takes over and at the end.
ended-Playback reached the end; the clip then returns to the start.
errorerror: unknownThe recording could not load (MediaError) or playback was refused; an interrupted start is not an error.

Exposed Methods

MethodSignatureDescription
play() => Promise<void>Plays from the current position, or from the start once at the end.
pause() => voidPauses.
toggle() => voidPlays or pauses.

CSS Variables

VariableDefaultDescription
--tx-voice-clip-bg--tx-fill-color-lightCapsule fill.
--tx-voice-clip-control-bg--tx-bg-colorPlay key fill.
--tx-voice-clip-control-ink--tx-text-color-primaryPlay key glyph.
--tx-voice-clip-accent--tx-color-primaryThe played part of the waveform.
--tx-voice-clip-track28% of the primary inkThe part not yet played.
--tx-voice-clip-ink--tx-text-color-regularThe time and the unavailable note.
--tx-voice-clip-focus--tx-color-primaryFocus rings.
  • None of these is declared on the root, so setting one on the clip or on any ancestor takes effect.

Overview

  • The play key, the waveform and the time form a 36px capsule; its width grows with the square root of the length, from 140px to 300px at 60 seconds and beyond, and the bar count follows the waveform's real width.
  • At rest the time is the length rounded to whole seconds (at least one); while playing, or paused part-way, it counts down from that number by the whole seconds played, so starting playback never changes it; at the end the clip returns to the start and shows the full length again.
  • One clip plays at a time on a page; starting one pauses whichever was playing.
  • The waveform is a role="slider": click or drag to seek, ←/→ and ↑/↓ step one second, PageUp/PageDown step 10% of the length, Home/End jump to either end; seeking never starts playback.
  • peaks are drawn as given; otherwise a local recording is decoded, with a flat track until it lands or if it fails. An empty or failing src disables the play key and replaces the waveform with unavailableLabel.
  • The press scale and the bar-height transition only run under prefers-reduced-motion: no-preference; progress always updates. The clip draws left to right on every page; right-to-left pages do not mirror it.

Technologies

  • Plays through a hidden <audio preload="metadata">; while playing, a rAF loop (and the element's timeupdate when frames are throttled) reads currentTime into --tx-voice-clip-progress, which clips an accent-coloured copy of the waveform, so nothing re-renders per frame.
  • Decodes with an 8 kHz OfflineAudioContext: the RMS of 96 buckets is stretched from the quietest tenth to the loudest bucket, averaged per bar and scaled so the tallest bar fills the height; the last 24 recordings are cached per URL.
  • Source: packages/tuffex/packages/components/src/voice-clip/.
查看源码
packages/tuffex/packages/components/src/voice-clip/index.ts

Use cases

  • Voice messages in a conversation: the user's recording, playable before and after it is sent.
  • The raw recording beside a dictation result, to recognise it again or send it as is.