使用JS+HTML+CSS编写提词器实例

手搓提词器网页版,有些BUG但是基本功能使用没有问题,有需要的可复制粘贴,BUG自行修复。下面直接进入代码:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>提词器</title>
    <style>
        :root {
            --primary-color: #1e88e5;
            --secondary-color: #26c6da;
            --danger-color: #f44336;
            --success-color: #66bb6a;
            --dark-bg: #121212;
            --darker-bg: #0a0a0a;
            --light-text: #f5f5f5;
            --lighter-text: #ffffff;
            --highlight-color: #ffeb3b;
            --highlight-bg: rgba(30, 136, 229, 0.2);
        }
        
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        body {
            font-family: 'Arial', sans-serif;
            background-color: var(--dark-bg);
            color: var(--light-text);
            line-height: 1.6;
            padding: 20px;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            width: 100%;
            flex: 1;
            display: flex;
            flex-direction: column;
        }
        
        h1 {
            text-align: center;
            margin-bottom: 20px;
            color: var(--lighter-text);
            text-shadow: 0 0 10px rgba(30, 136, 229, 0.5);
        }
        
        .editor-panel {
            background-color: var(--darker-bg);
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
            margin-bottom: 20px;
        }
        
        .control-panel {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-bottom: 15px;
        }
        
        .control-group {
            display: flex;
            flex-direction: column;
        }
        
        label {
            margin-bottom: 8px;
            font-weight: bold;
            color: var(--secondary-color);
        }
        
        textarea {
            width: 100%;
            min-height: 200px;
            padding: 12px;
            background-color: #1e1e1e;
            color: var(--light-text);
            border: 1px solid #333;
            border-radius: 4px;
            resize: vertical;
            font-family: inherit;
            font-size: 16px;
            margin-bottom: 15px;
        }
        
        select, input[type="range"] {
            width: 100%;
            padding: 8px;
            background-color: #1e1e1e;
            color: var(--light-text);
            border: 1px solid #333;
            border-radius: 4px;
        }
        
        .range-container {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .range-value {
            min-width: 30px;
            text-align: center;
        }
        
        .button-group {
            display: flex;
            gap: 10px;
            margin-top: 15px;
            flex-wrap: wrap;
        }
        
        button {
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
            transition: all 0.3s ease;
            flex: 1;
            min-width: 120px;
        }
        
        .btn-play {
            background-color: var(--success-color);
            color: white;
        }
        
        .btn-pause {
            background-color: var(--danger-color);
            color: white;
            display: none;
        }
        
        .btn-fullscreen {
            background-color: var(--primary-color);
            color: white;
        }
        
        .btn-reset {
            background-color: #616161;
            color: white;
        }
        
        button:hover {
            opacity: 0.9;
            transform: translateY(-2px);
        }
        
        button:active {
            transform: translateY(0);
        }
        
        .teleprompter-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            background-color: #000;
            border-radius: 8px;
            overflow: hidden;
            position: relative;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
        }
        
        .teleprompter-display {
            flex: 1;
            overflow: hidden;
            position: relative;
            padding: 20px;
        }
        
        .teleprompter-viewport {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }
        
        .teleprompter-text {
            width: 100%;
            text-align: center;
            transition: transform 0.1s linear;
            color: var(--light-text);
        }
        
        .word {
            transition: all 0.3s ease;
            display: inline-block;
            margin: 0 3px;
            padding: 2px 4px;
            border-radius: 3px;
        }
        
        .highlight {
            color: var(--highlight-color);
            background-color: var(--highlight-bg);
            font-weight: bold;
            transform: scale(1.3);
            text-shadow: 0 0 10px rgba(255, 235, 59, 0.7);
            position: relative;
            z-index: 1;
        }
        
        .highlight::after {
            content: '';
            position: absolute;
            left: 0;
            right: 0;
            bottom: -10px;
            height: 2px;
            background-color: var(--highlight-color);
            opacity: 0.5;
            z-index: -1;
        }
        
        /* 全屏样式 */
        .fullscreen {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            background-color: #000;
            z-index: 1000;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 0;
            margin: 0;
        }
        
        .fullscreen .teleprompter-display {
            width: 100%;
            height: 100%;
        }
        
        .fullscreen .teleprompter-text {
            font-size: 3em !important;
        }
        
        /* 响应式调整 */
        @media (max-width: 768px) {
            .control-panel {
                grid-template-columns: 1fr;
            }
            
            .teleprompter-display {
                padding: 10px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>提词器</h1>
        
        <div class="editor-panel">
            <textarea id="prompterText" placeholder="在这里输入您的提词内容...">欢迎使用提词器</textarea>
            
            <div class="control-panel">
                <div class="control-group">
                    <label for="fontFamily">字体选择</label>
                    <select id="fontFamily">
                        <option value="Arial, sans-serif">Arial</option>
                        <option value="'Microsoft YaHei', sans-serif">微软雅黑</option>
                        <option value="'SimSun', serif">宋体</option>
                        <option value="'Times New Roman', serif">Times New Roman</option>
                        <option value="'Courier New', monospace">Courier New</option>
                    </select>
                </div>
                
                <div class="control-group">
                    <label for="fontSize">字号大小</label>
                    <div class="range-container">
                        <input type="range" id="fontSize" min="12" max="72" value="24">
                        <span class="range-value" id="fontSizeValue">24</span>px
                    </div>
                </div>
                
                <div class="control-group">
                    <label for="speed">滚动速度</label>
                    <div class="range-container">
                        <input type="range" id="speed" min="1" max="100" value="50">
                        <span class="range-value" id="speedValue">50</span>
                    </div>
                </div>
            </div>
            
            <div class="button-group">
                <button id="playBtn" class="btn-play">开始</button>
                <button id="pauseBtn" class="btn-pause">暂停</button>
                <button id="fullscreenBtn" class="btn-fullscreen">全屏</button>
                <button id="resetBtn" class="btn-reset">重置</button>
            </div>
        </div>
        
        <div class="teleprompter-container">
            <div class="teleprompter-display">
                <div class="teleprompter-viewport">
                    <div id="teleprompterText" class="teleprompter-text"></div>
                </div>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 获取DOM元素
            const prompterText = document.getElementById('prompterText');
            const teleprompterText = document.getElementById('teleprompterText');
            const fontFamily = document.getElementById('fontFamily');
            const fontSize = document.getElementById('fontSize');
            const fontSizeValue = document.getElementById('fontSizeValue');
            const speed = document.getElementById('speed');
            const speedValue = document.getElementById('speedValue');
            const playBtn = document.getElementById('playBtn');
            const pauseBtn = document.getElementById('pauseBtn');
            const fullscreenBtn = document.getElementById('fullscreenBtn');
            const resetBtn = document.getElementById('resetBtn');
            const teleprompterContainer = document.querySelector('.teleprompter-container');
            const viewport = document.querySelector('.teleprompter-viewport');
            
            // 状态变量
            let isPlaying = false;
            let scrollInterval;
            let words = [];
            let currentWordIndex = 0;
            let wordElements = [];
            let wordPositions = [];
            let viewportHeight = 0;
            let centerPosition = 0;
            
            // 初始化
            updateTeleprompterText();
            calculatePositions();
            
            // 事件监听
            prompterText.addEventListener('input', updateTeleprompterText);
            fontFamily.addEventListener('change', updateTeleprompterStyle);
            fontSize.addEventListener('input', function() {
                fontSizeValue.textContent = this.value;
                updateTeleprompterStyle();
                setTimeout(calculatePositions, 100); // 等待渲染完成
            });
            speed.addEventListener('input', function() {
                speedValue.textContent = this.value;
                if (isPlaying) {
                    clearInterval(scrollInterval);
                    startScrolling();
                }
            });
            
            playBtn.addEventListener('click', startScrolling);
            pauseBtn.addEventListener('click', pauseScrolling);
            fullscreenBtn.addEventListener('click', toggleFullscreen);
            resetBtn.addEventListener('click', resetTeleprompter);
            
            // 窗口大小变化时重新计算位置
            window.addEventListener('resize', function() {
                calculatePositions();
                updateHighlightPosition();
            });
            
            // 键盘快捷键
            document.addEventListener('keydown', function(e) {
                // 空格键 播放/暂停
                if (e.code === 'Space') {
                    e.preventDefault();
                    if (isPlaying) {
                        pauseScrolling();
                    } else {
                        startScrolling();
                    }
                }
                
                // ESC键 退出全屏
                if (e.code === 'Escape' && teleprompterContainer.classList.contains('fullscreen')) {
                    toggleFullscreen();
                }
                
                // 上箭头 减速
                if (e.code === 'ArrowUp') {
                    e.preventDefault();
                    speed.value = Math.min(100, parseInt(speed.value) + 5);
                    speed.dispatchEvent(new Event('input'));
                }
                
                // 下箭头 加速
                if (e.code === 'ArrowDown') {
                    e.preventDefault();
                    speed.value = Math.max(1, parseInt(speed.value) - 5);
                    speed.dispatchEvent(new Event('input'));
                }
            });
            
            // 更新提词器文本
            function updateTeleprompterText() {
                const text = prompterText.value;
                words = splitTextIntoWords(text);
                renderWords();
                updateTeleprompterStyle();
                calculatePositions();
            }
            
            // 将文本分割为单词数组
            function splitTextIntoWords(text) {
                // 使用正则表达式分割单词和标点符号
                return text.split(/(\s+|\n+|[,.!?;:])/).filter(word => word.trim().length > 0);
            }
            
            // 渲染单词
            function renderWords() {
                teleprompterText.innerHTML = '';
                wordElements = [];
                
                words.forEach((word, index) => {
                    const wordSpan = document.createElement('span');
                    wordSpan.className = 'word';
                    wordSpan.textContent = word;
                    wordSpan.dataset.index = index;
                    
                    // 处理空格和换行
                    if (word.match(/\s+/)) {
                        wordSpan.style.padding = '0 2px';
                        if (word.includes('\n')) {
                            wordSpan.style.display = 'block';
                            wordSpan.style.height = '1em';
                            wordSpan.style.content = ' ';
                        }
                    }
                    
                    teleprompterText.appendChild(wordSpan);
                    wordElements.push(wordSpan);
                });
                
                // 重置高亮
                currentWordIndex = 0;
                updateHighlightPosition();
            }
            
            // 计算所有单词的位置
            function calculatePositions() {
                viewportHeight = viewport.clientHeight;
                centerPosition = viewportHeight / 2;
                wordPositions = [];
                
                // 临时显示所有元素以计算位置
                teleprompterText.style.transform = 'translateY(0)';
                
                // 计算每个单词的绝对位置
                let currentTop = 0;
                wordElements.forEach((wordEl, index) => {
                    const rect = wordEl.getBoundingClientRect();
                    const viewportRect = viewport.getBoundingClientRect();
                    const relativeTop = rect.top - viewportRect.top;
                    
                    wordPositions[index] = relativeTop;
                    
                    // 如果是换行元素,增加额外高度
                    if (wordEl.textContent.includes('\n')) {
                        currentTop += rect.height;
                    }
                });
            }
            
            // 更新高亮单词位置
            function updateHighlightPosition() {
                // 移除所有高亮
                wordElements.forEach(word => {
                    word.classList.remove('highlight');
                });
                
                if (words.length > 0 && currentWordIndex < words.length) {
                    // 计算需要滚动到的位置,使当前词居中
                    const targetPosition = centerPosition - wordPositions[currentWordIndex];
                    
                    // 应用滚动位置
                    teleprompterText.style.transform = `translateY(${targetPosition}px)`;
                    
                    // 高亮当前词
                    wordElements[currentWordIndex].classList.add('highlight');
                }
            }
            
            // 更新提词器样式
            function updateTeleprompterStyle() {
                teleprompterText.style.fontFamily = fontFamily.value;
                teleprompterText.style.fontSize = `${fontSize.value}px`;
            }
            
            // 开始滚动
            function startScrolling() {
                if (isPlaying) return;
                
                isPlaying = true;
                playBtn.style.display = 'none';
                pauseBtn.style.display = 'block';
                
                // 计算速度 (值越大速度越慢)
                const speedFactor = 101 - speed.value; // 反转值,使滑块右侧为快
                const intervalTime = Math.max(50, 2000 / speedFactor);
                
                scrollInterval = setInterval(() => {
                    currentWordIndex++;
                    
                    if (currentWordIndex >= words.length) {
                        // 到达末尾,停止滚动
                        pauseScrolling();
                        return;
                    }
                    
                    updateHighlightPosition();
                }, intervalTime);
            }
            
            // 暂停滚动
            function pauseScrolling() {
                if (!isPlaying) return;
                
                clearInterval(scrollInterval);
                isPlaying = false;
                playBtn.style.display = 'block';
                pauseBtn.style.display = 'none';
            }
            
            // 切换全屏
            function toggleFullscreen() {
                if (teleprompterContainer.classList.contains('fullscreen')) {
                    teleprompterContainer.classList.remove('fullscreen');
                    document.exitFullscreen?.();
                } else {
                    teleprompterContainer.classList.add('fullscreen');
                    teleprompterContainer.requestFullscreen?.();
                }
                
                // 全屏切换后重新计算位置
                setTimeout(() => {
                    calculatePositions();
                    updateHighlightPosition();
                }, 300);
            }
            
            // 重置提词器
            function resetTeleprompter() {
                pauseScrolling();
                currentWordIndex = 0;
                teleprompterText.style.transform = 'translateY(0)';
                updateHighlightPosition();
            }
        });
    </script>
</body>
</html>
相关推荐
Mintopia26 分钟前
Node.js 对前端技术有利的知识点
前端·javascript·node.js
一道雷28 分钟前
🛠️ Unindex:推荐一款自动化索引文件生成工具
前端·javascript·node.js
键指江湖1 小时前
React 更新 state 中的数组
javascript·react.js·ecmascript
Mintopia1 小时前
Three.js 着色器使用教程:进阶指南
前端·javascript·three.js
魔云连洲1 小时前
使用 Sass 打造动态星空背景效果
前端·css·sass
卸任1 小时前
next-auth是如何保持登录状态的?回顾一下Session、Cookie、Token
前端·javascript·next.js
天天扭码1 小时前
前端必看 | 零基础入门 | 你真的懂CSS选择器吗?
前端·css·html
BillKu1 小时前
vue3中,element-plus中el-input的v-model和value的用法示例
javascript·vue.js·elementui
小韩本韩!2 小时前
electron+vue项目 设置全屏
javascript·vue.js·electron
CsharpDev-奶豆哥2 小时前
如何理解前端开发中的“换皮“
前端·css·css3