You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建支持按住持续监听的React语音转文字组件?

解决按住触发持续语音监听、松开停止的问题,避免麦克风启停噪音

问题

当前代码无法实现「按住鼠标/拇指时持续监听、松开才停止」的需求:按住暂停思考时监听会自动停止,重启时移动端Chrome麦克风出现明显启停噪音。

需求

  • 按住鼠标或触摸操作时,持续进行语音识别监听
  • 仅在松开鼠标或触摸操作时,停止语音识别监听

解决方案代码

import React, { useState, useEffect, useRef } from "react";

const ListeningComponent = () => {
  const [listening, setListening] = useState(false);
  const [isHolding, setIsHolding] = useState(false);
  const [transcript, setTranscript] = useState("");
  const [interimTranscript, setInterimTranscript] = useState("");
  const recognitionRef = useRef(null);

  useEffect(() => {
    if (typeof window !== "undefined" && "webkitSpeechRecognition" in window) {
      const recognition = new window.webkitSpeechRecognition();
      recognition.continuous = true;
      recognition.interimResults = true;
      recognition.lang = "en-US";
      recognition.maxAlternatives = 1;

      recognition.onresult = (event) => {
        let finalTranscript = "";
        let interim = "";
        for (let i = event.resultIndex; i < event.results.length; ++i) {
          if (event.results[i].isFinal) {
            finalTranscript += event.results[i][0].transcript;
          } else {
            interim += event.results[i][0].transcript;
          }
        }
        setTranscript(prev => prev + finalTranscript);
        setInterimTranscript(interim);
      };

      recognition.onend = () => {
        // 仅在按住状态下重启识别,避免无意义重启导致麦克风噪音
        if (isHolding) {
          recognition.start();
        } else {
          setListening(false);
        }
      };

      recognitionRef.current = recognition;
    }
  }, []);

  // 开始监听:按住鼠标/触摸时触发
  const handleStart = () => {
    if (!listening && recognitionRef.current) {
      setIsHolding(true);
      setListening(true);
      recognitionRef.current.start();
    }
  };

  // 停止监听:松开鼠标/触摸或离开元素时触发
  const handleStop = () => {
    if (listening && recognitionRef.current) {
      setIsHolding(false);
      recognitionRef.current.stop();
    }
  };

  return (
    <div>
      <div
        style={{
          padding: "1rem 2rem",
          backgroundColor: listening ? "#ff4444" : "#2196F3",
          color: "white",
          cursor: "pointer",
          display: "inline-block",
          userSelect: "none"
        }}
        onMouseDown={handleStart}
        onMouseUp={handleStop}
        onMouseLeave={handleStop}
        onTouchStart={handleStart}
        onTouchEnd={handleStop}
      >
        {listening ? "按住说话..." : "按住开始说话"}
      </div>
      <div style={{ marginTop: "1rem" }}>
        {listening ? <p>正在监听,请说话</p> : <p>未监听</p>}
        <p>最终转录:{transcript}</p>
        {interimTranscript && <p>临时转录:{interimTranscript}</p>}
      </div>
    </div>
  );
};

export default ListeningComponent;

关键改动说明

  • 替换触发逻辑:将原按钮点击事件改为mousedown/touchstart(按住)和mouseup/mouseleave/touchend(松开/离开)事件,精准匹配「按住持续、松开停止」的交互需求
  • 新增状态追踪:添加isHolding状态专门追踪用户是否处于按住状态,区分主动停止和识别服务自动结束的场景
  • 优化onend逻辑:仅在用户按住时重启识别,避免原代码中无意义的循环重启,从根源解决移动端麦克风启停噪音问题
  • 增强兼容性:同时支持鼠标和触摸操作,适配桌面端和移动端场景
  • UI反馈优化:将按钮改为可按住的交互区域,实时反馈监听状态

内容的提问来源于stack exchange,提问作者Luke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 15:43:17