1、提示词

(1)轻量、适合很老的Macbook Pro;(2)有中文支持,且生成语音高质量;(3)免费、开源;(4)方便编程小白使用;(5)不使用内置的UI样式,单独创建并使用scss文件来引入需要使用的样式文件;

2、生成预览效果

创建了一个基于Web Speech API的文本转语音工具,具有语音选择、语速调节、音调调节等功能,界面简洁直观,完全免费且支持中文。

3、生成文件

依赖安装:lucide-react@latest

1、index.jsx
import { useState, useEffect } from "react";
import { Play, Pause, Square } from "lucide-react";
import "../styles/index.scss";

const Index = () => {
  const [text, setText] = useState("");
  const [voices, setVoices] = useState([]);
  const [selectedVoice, setSelectedVoice] = useState("");
  const [rate, setRate] = useState(1.0);
  const [pitch, setPitch] = useState(1.0);
  const [isPlaying, setIsPlaying] = useState(false);

  useEffect(() => {
    const loadVoices = () => {
      const availableVoices = window.speechSynthesis.getVoices();
      setVoices(availableVoices);
      const chineseVoice = availableVoices.find(voice => voice.lang.includes('zh'));
      setSelectedVoice(chineseVoice?.name || availableVoices[0]?.name);
    };

    loadVoices();
    window.speechSynthesis.onvoiceschanged = loadVoices;

    return () => {
      window.speechSynthesis.cancel();
    };
  }, []);

  const speak = () => {
    if (!text) return;

    window.speechSynthesis.cancel();
    const utterance = new SpeechSynthesisUtterance(text);
    utterance.voice = voices.find(voice => voice.name === selectedVoice);
    utterance.rate = rate;
    utterance.pitch = pitch;

    utterance.onstart = () => setIsPlaying(true);
    utterance.onend = () => setIsPlaying(false);
    utterance.onerror = () => setIsPlaying(false);

    window.speechSynthesis.speak(utterance);
  };

  const pause = () => {
    window.speechSynthesis.pause();
    setIsPlaying(false);
  };

  const resume = () => {
    window.speechSynthesis.resume();
    setIsPlaying(true);
  };

  const stop = () => {
    window.speechSynthesis.cancel();
    setIsPlaying(false);
  };

  return (
    <div className="text-to-speech">
      <div className="text-to-speech__container">
        <h1 className="text-to-speech__title">文本转语音工具</h1>
        
        <div>
          <textarea
            className="text-to-speech__textarea"
            placeholder="请输入要转换的文本..."
            value={text}
            onChange={(e) => setText(e.target.value)}
          />

          <div className="text-to-speech__controls">
            <div className="text-to-speech__control-group">
              <label className="text-to-speech__label">选择声音:</label>
              <select
                className="text-to-speech__select"
                value={selectedVoice}
                onChange={(e) => setSelectedVoice(e.target.value)}
              >
                {voices.map((voice) => (
                  <option key={voice.name} value={voice.name}>
                    {`${voice.name} (${voice.lang})`}
                  </option>
                ))}
              </select>
            </div>

            <div className="text-to-speech__control-group">
              <label className="text-to-speech__label">
                语速 ({rate.toFixed(1)}):
              </label>
              <input
                type="range"
                className="text-to-speech__slider"
                min="0.5"
                max="2"
                step="0.1"
                value={rate}
                onChange={(e) => setRate(parseFloat(e.target.value))}
              />
            </div>

            <div className="text-to-speech__control-group">
              <label className="text-to-speech__label">
                音调 ({pitch.toFixed(1)}):
              </label>
              <input
                type="range"
                className="text-to-speech__slider"
                min="0.5"
                max="2"
                step="0.1"
                value={pitch}
                onChange={(e) => setPitch(parseFloat(e.target.value))}
              />
            </div>
          </div>

          <div className="text-to-speech__buttons">
            {!isPlaying ? (
              <button 
                onClick={speak}
                className="text-to-speech__button text-to-speech__button--play"
              >
                <Play className="w-5 h-5 mr-2" />
                播放
              </button>
            ) : (
              <button 
                onClick={pause}
                className="text-to-speech__button text-to-speech__button--pause"
              >
                <Pause className="w-5 h-5 mr-2" />
                暂停
              </button>
            )}
            <button 
              onClick={stop} 
              className="text-to-speech__button text-to-speech__button--stop"
            >
              <Square className="w-5 h-5 mr-2" />
              停止
            </button>
          </div>
        </div>

        <div className="text-to-speech__tips">
          <p>提示:此工具使用浏览器内置的 Web Speech API,完全免费且无需安装。</p>
          <p>支持中文和多种语言,可调节语速和音调。</p>
        </div>
      </div>
    </div>
  );
};

export default Index;
2、index.scss
// 变量定义
$primary-color: #3b82f6;
$secondary-color: #f3f4f6;
$text-color: #1f2937;
$border-color: #e5e7eb;

// 混合器
@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

@mixin button-base {
  padding: 0.5rem 1.5rem;
  border-radius: 0.375rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  @include flex-center;
}

// 全局样式
.text-to-speech {
  min-height: 100vh;
  padding: 2rem;
  background: linear-gradient(180deg, #f9fafb 0%, #f3f4f6 100%);

  &__container {
    max-width: 42rem;
    margin: 0 auto;
    background: white;
    border-radius: 0.5rem;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    padding: 2rem;
  }

  &__title {
    font-size: 1.875rem;
    font-weight: 700;
    text-align: center;
    color: $text-color;
    margin-bottom: 2rem;
  }

  &__textarea {
    width: 100%;
    min-height: 200px;
    padding: 1rem;
    border: 2px solid $border-color;
    border-radius: 0.375rem;
    font-size: 1.125rem;
    resize: vertical;

    &:focus {
      border-color: $primary-color;
      outline: none;
    }
  }

  &__controls {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
    margin-top: 1.5rem;

    @media (min-width: 768px) {
      grid-template-columns: repeat(2, 1fr);
    }
  }

  &__control-group {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }

  &__label {
    font-size: 0.875rem;
    font-weight: 500;
    color: $text-color;
  }

  &__select {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid $border-color;
    border-radius: 0.375rem;
    background-color: white;
  }

  &__slider {
    width: 100%;
    height: 4px;
    background: $border-color;
    border-radius: 2px;
    appearance: none;

    &::-webkit-slider-thumb {
      appearance: none;
      width: 16px;
      height: 16px;
      background: $primary-color;
      border-radius: 50%;
      cursor: pointer;
    }
  }

  &__buttons {
    @include flex-center;
    gap: 1rem;
    margin-top: 2rem;
  }

  &__button {
    @include button-base;

    &--play {
      background-color: $primary-color;
      color: white;

      &:hover {
        background-color: darken($primary-color, 10%);
      }
    }

    &--pause {
      background-color: #ca8a04;
      color: white;

      &:hover {
        background-color: darken(#ca8a04, 10%);
      }
    }

    &--stop {
      border: 2px solid $border-color;
      color: $text-color;

      &:hover {
        background-color: $secondary-color;
      }
    }
  }

  &__tips {
    text-align: center;
    color: #6b7280;
    font-size: 0.875rem;
    margin-top: 2rem;

    p {
      margin: 0.5rem 0;
    }
  }
}

3、全局文件 index.css

@import './styles/index.scss';

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐