Skip to content

Voice Button

Web Speech API 위에 올린, 텍스트 입력창용 받아쓰기입니다.

이럴 때 쓰세요. 음성을 텍스트 필드를 채우는 또 하나의 방법으로 두고 싶을 때. 대체 수단이 아니라요. 타이핑은 계속 가능해야 합니다. 음성만 있는 길은 발화에 차이가 있는 사람, 시끄러운 곳에 있는 사람, 그리고 인식 기능이 아예 없는 브라우저를 쓰는 사람을 배제합니다.

마이크 버튼, 그뿐입니다. 캡처를 맡아 들린 내용을 알려 줍니다. 그 텍스트가 어디로 갈지는 부르는 쪽의 결정입니다. 입력창까지 써 넣는 컴포넌트라면 어느 입력창인지, 덧붙일지 바꿀지, 캐럿은 어떻게 할지를 알아야 하는데, 그 세 가지 답은 앱마다 다르기 때문입니다.

빠른 시작

html
<r-voice-button label="음성 입력 시작" active-label="음성 입력 중지"></r-voice-button>
ts
const mic = document.createElement('r-voice-button');
mic.label = '음성 입력 시작';
mic.activeLabel = '음성 입력 중지';
const input = document.querySelector('textarea');
let base = '';

mic.addEventListener('voicestart', () => {
  // 입력한 글과 말한 글 사이에 공백 하나. 이미 있으면 넣지 않습니다.
  base = input.value === '' || /\s$/.test(input.value) ? input.value : `${input.value} `;
});

mic.addEventListener('voiceresult', (event) => {
  input.value = base + event.detail.transcript;
});

composer.append(mic);

그 뒤에 있는 판단

최신 조각이 아니라 캡처 전체를 알려 줍니다

중간 결과는 인식이 이어지면서 고쳐집니다. "你好"가 "你好世界"가 되는 것이지, "世界"를 담은 두 번째 이벤트가 따로 오는 게 아닙니다. 이벤트마다 덧붙이는 쪽은 결국 你好你好世界가 됩니다. 필드에 이미 있던 텍스트를 기억해 두고 한 번만 이어 붙이세요.

전송하지 않습니다

인식은 충분히 자주 틀리기 때문에, 말한 사람을 대신해 확정해 버리면 그가 필요로 하는 검토를 빼앗게 됩니다. 이것은 상자를 채우고 거기서 멈춥니다. 전송은 여전히 의도된 행위로 남습니다.

인식이 없는 곳에서는 스스로 숨습니다

Firefox에는 음성 인식이 없고, 그 API가 없는 어떤 브라우저도 마찬가지입니다. 인식이 지원되지 않을 때 이 엘리먼트는 disabled로 자신을 비활성화하는 대신 hidden으로 숨습니다. disabled는 기능이 있지만 잠시 쓸 수 없다는 뜻이고, 이 플랫폼에 기능 자체가 없다면 버튼을 없애는 편이 정확하기 때문입니다. 결코 동작할 수 없는 버튼을 보이면 아무 일도 없는 탭을 부르고, 그다음엔 설명이 필요해집니다.

네 가지 오류 가운데 보여 줄 값어치가 있는 것은 둘뿐

종류무엇인가보여 줄까?
denied마이크가 거부되었다(조치할 수 있다)
failed그 밖의 무언가가 잘못됐다
noSpeech조용한 멈춤아니오
aborted코드로 인한 중지아니오

뒤의 둘은 진짜 실패와 같은 통로로 도착하지만 실패가 아닙니다. 이것들을 드러내면 진짜 실패 때만이 아니라 평범한 캡처마다 오류를 보이게 됩니다.

접근성

아이콘뿐 아니라 접근성 이름이 상태와 함께 바뀌고, aria-pressed가 토글을 실어 나릅니다. 스크린 리더는 아이콘이 아니라 "음성 입력 중지, 눌림"이라고 알려 줍니다. Escape는 캡처를 버립니다. 확정하지 않습니다. 문장 도중에 잘못 말한 걸 깨달은 사람이 바라는 것이 그것입니다.

듣는 상태는 테두리와 채움 그리고 링으로 전해지므로 색에만 기대지 않습니다. 움직이는 것은 링뿐이며 그것은 장식입니다. prefers-reduced-motion에서는 정보를 잃지 않은 채 링만 빠집니다.

언어는 페이지를 따릅니다

lang캡처마다 읽히고 기본값은 문서의 것입니다. 세션 도중 로케일을 바꾸는 앱은 지금 보여 주고 있는 언어로 받아씁니다.

API 레퍼런스

프로퍼티

프로퍼티타입기본값설명
langstring문서의 것말하는 언어의 BCP 47 태그. 캡처마다 읽습니다.
continuousbooleantrue첫 멈춤에서 그치지 않고 멈춤을 건너 계속 듣습니다.
disabledbooleanfalse버튼을 끕니다. start()는 무시되고 안쪽 버튼도 꺼집니다. 이미 진행 중인 캡처는 멈추지 않습니다.
labelstring'Start voice input'대기 중일 때의 접근성 이름.
activeLabelstring'Stop voice input'듣는 중일 때의 접근성 이름.
listeningbooleanfalse읽기 전용이며 반영됩니다. :host([listening])으로 스타일을 주세요.
supportedboolean읽기 전용. 이 플랫폼이 음성을 인식할 수 있는지 여부.
sheetstring''엘리먼트의 섀도 DOM에 주입할 CSS.

메서드

start() · stop()(인식된 것을 남깁니다) · abort()(버립니다) · toggle().

toggle()은 반영된 어트리뷰트가 아니라 인식기 자신의 상태를 읽습니다. 알리지 않은 채 시작된 캡처가 있으면 둘이 어긋난 채로 남고, 다음 활성화는 두 번째 캡처를 열려다 거부되어 아무 일도 하지 않게 됩니다.

이벤트

이벤트Detail발생 시점
voicestart캡처가 시작될 때
voiceresult{ transcript, isFinal }텍스트가 도착하거나 고쳐질 때
voiceerror{ kind, detail }플랫폼이 문제를 알릴 때
voiceend이유가 무엇이든 캡처가 끝날 때

Part

button, icon.

스타일

<r-voice-button>은 자체 CSS 커스텀 프로퍼티 20개와 테마에서 읽어 오는 의미 토큰을 공개합니다. 상속이 닿는 곳이면 어디에나 지정하세요: :root, 바깥 컨테이너, 또는 엘리먼트 자체.

css
r-voice-button {
  --ran-voice-background: var(--ran-color-bg-subtle);
}

Part: button · hint · icon

전체 목록은 스타일 토큰에, 어떤 토큰을 고를지는 디자인 시스템에 있습니다.

함께 보기

MIT 라이선스로 배포됩니다.