diff --git a/文本提词器/index.html b/文本提词器/index.html deleted file mode 100644 index dfc9383..0000000 --- a/文本提词器/index.html +++ /dev/null @@ -1,130 +0,0 @@ - - -
- - -
- © 2025-Now SuperJia
All Rights Reserved
版本 v1.2, 发布于2025-09
-
请输入您的提词内容,然后点击"格式化文本"按钮开始使用...
-${sentence}
`; - }); - - if (html === '') { - html = '请输入您的提词内容,然后点击"格式化文本"按钮开始使用...
'; - } - - this.textDisplay.innerHTML = html; - this.totalLinesSpan.textContent = this.totalLines; - - // 应用当前的行间距设置 - const currentLineHeight = this.lineHeight.value; - const paragraphs = this.textDisplay.querySelectorAll('p'); - paragraphs.forEach(p => { - p.style.lineHeight = currentLineHeight; - }); - - // 计算每行的高度并存储 - paragraphs.forEach((p, index) => { - const height = p.offsetHeight + parseFloat(getComputedStyle(p).marginBottom); - this.lineHeights[index] = height; - }); - - // 计算初始位置,将第一行文本对齐到中间位置 - const containerHeight = this.teleprompterContent.clientHeight; - const firstLineHeight = this.lineHeights[0] || this.getCurrentLineHeight(); - const totalContentHeight = this.lineHeights.reduce((sum, height) => sum + height, 0); - - // 如果内容高度小于容器高度,将第一行放在中间位置 - // 如果内容高度大于容器高度,确保第一行不会被遮挡 - let startPosition = (containerHeight / 2) - firstLineHeight; - - // 确保起始位置不会导致文本显示在容器底部之外 - if (startPosition < 0) { - startPosition = Math.min(50, containerHeight / 4); // 给一个合理的顶部边距 - } - - // 重置滚动位置 - this.currentPosition = 0; - const isMirror = this.mirrorText.checked; - const mirrorScale = isMirror ? ' scaleX(-1)' : ' scaleX(1)'; - this.textDisplay.style.transform = `translateY(${startPosition}px)${mirrorScale}`; - } - - // 滚动功能 - startScrolling() { - if (this.formattedText.length === 0) { - alert('请先格式化文本!'); - return; - } - - if (this.isPaused) { - this.resumeScrolling(); - return; - } - - this.isScrolling = true; - this.isPaused = false; - this.startScrollBtn.textContent = '滚动中...'; - this.startScrollBtn.disabled = true; - this.readingGuide.classList.add('visible'); - - const speed = parseFloat(this.scrollSpeed.value); - const interval = Math.max(20, 210 - speed); // 扩大速度转换范围,最低20ms,最高190ms - - this.scrollInterval = setInterval(() => { - this.scrollStep(); - }, interval); - } - - scrollStep() { - if (this.formattedText.length === 0) { - this.stopScrolling(); - alert('请先格式化文本!'); - return; - } - - const lineHeight = this.getCurrentLineHeight(); - this.currentPosition += 1; - - // 更新当前行 - let accumulatedHeight = 0; - let newLine = 0; - for (let i = 0; i < this.lineHeights.length; i++) { - accumulatedHeight += this.lineHeights[i]; - if (accumulatedHeight > this.currentPosition) { - newLine = i; - break; - } - newLine = i; - } - - if (newLine !== this.currentLine && newLine < this.totalLines) { - this.currentLine = newLine; - this.updateProgress(); - } - - // 应用滚动 - const isMirror = this.mirrorText.checked; - const mirrorScale = isMirror ? ' scaleX(-1)' : ' scaleX(1)'; - this.textDisplay.style.transform = `translateY(-${this.currentPosition}px)${mirrorScale}`; - - // 检查是否滚动到底部 - const containerHeight = this.teleprompterContent.clientHeight; - const contentHeight = this.textDisplay.scrollHeight; - - if (this.currentPosition + containerHeight >= contentHeight) { - this.stopScrolling(); - alert('已滚动到文本末尾!'); - } - } - - pauseScrolling() { - if (this.isScrolling && !this.isPaused) { - this.isPaused = true; - clearInterval(this.scrollInterval); - this.pauseScrollBtn.textContent = '继续'; - this.startScrollBtn.textContent = '继续'; - this.startScrollBtn.disabled = false; - } - } - - resumeScrolling() { - if (this.isPaused) { - this.isPaused = false; - this.startScrolling(); - this.pauseScrollBtn.textContent = '暂停'; - } - } - - resetScrolling() { - this.stopScrolling(); - this.currentPosition = 0; - this.currentLine = 0; - - // 重置到初始中间位置 - const containerHeight = this.teleprompterContent.clientHeight; - const firstLineHeight = this.lineHeights[0] || this.getCurrentLineHeight(); - let startPosition = (containerHeight / 2) - firstLineHeight; - - // 确保起始位置不会导致文本显示在容器底部之外 - if (startPosition < 0) { - startPosition = Math.min(50, containerHeight / 4); // 给一个合理的顶部边距 - } - - const isMirror = this.mirrorText.checked; - const mirrorScale = isMirror ? ' scaleX(-1)' : ' scaleX(1)'; - this.textDisplay.style.transform = `translateY(${startPosition}px)${mirrorScale}`; - - this.updateProgress(); - this.readingGuide.classList.remove('visible'); - } - - stopScrolling() { - this.isScrolling = false; - this.isPaused = false; - clearInterval(this.scrollInterval); - this.startScrollBtn.textContent = '开始'; - this.startScrollBtn.disabled = false; - this.pauseScrollBtn.textContent = '暂停'; - } - - // 显示设置 - updateFontSize() { - const size = this.fontSize.value; - this.fontSizeValue.textContent = size + 'px'; - this.textDisplay.style.fontSize = size + 'px'; - - // 重新计算行高 - if (this.formattedText.length > 0) { - setTimeout(() => this.displayFormattedText(), 50); - } - this.saveSettings(); - } - - updateLineHeight() { - const height = this.lineHeight.value; - this.lineHeightValue.textContent = height; - - // 应用到所有段落,让每一行都有这个行间距 - const paragraphs = this.textDisplay.querySelectorAll('p'); - paragraphs.forEach(p => { - p.style.lineHeight = height; - }); - - // 重新计算行高 - if (this.formattedText.length > 0) { - setTimeout(() => this.displayFormattedText(), 50); - } - this.saveSettings(); - } - - updateTextColor() { - const color = this.textColor.value; - this.textDisplay.style.color = color; - this.saveSettings(); - } - - updateBgColor() { - const color = this.bgColor.value; - this.teleprompterContainer.style.backgroundColor = color; - this.saveSettings(); - } - - updateMirrorText() { - const text = this.textInput.value.trim(); - if (text === '') { - this.textDisplay.innerHTML = ''; - this.formattedText = []; - return; - } - - // 简单按换行符分段,保持原始段落结构 - const lines = text.split('\n').map(line => line.trim()).filter(line => line !== ''); - this.formattedText = lines; - this.totalLines = lines.length; - const formattedText = lines.map(line => `${line}
`).join(''); - - this.textDisplay.innerHTML = formattedText; - - // 应用当前设置 - this.updateFontSize(); - this.updateLineHeight(); - this.updateTextColor(); - this.updateBgColor(); - - // 更新镜像状态 - const isMirror = this.mirrorText.checked; - if (isMirror) { - this.textDisplay.style.transform = `translateY(-${this.currentPosition}px) scaleX(-1)`; - } else { - this.textDisplay.style.transform = `translateY(-${this.currentPosition}px) scaleX(1)`; - } - - this.saveSettings(); - } - - // 高级功能 - toggleAdvancedMenu() { - const isVisible = this.advancedContent.style.display === 'block'; - this.advancedContent.style.display = isVisible ? 'none' : 'block'; - this.advancedToggle.style.transform = isVisible ? 'rotate(0deg)' : 'rotate(180deg)'; - } - - updateMaxWordsPerParagraph() { - const value = this.maxWordsPerParagraph.value; - this.maxWordsValue.textContent = value; - this.saveSettings(); - } - - updateForceParagraphBreak() { - this.saveSettings(); - } - - - - // 全屏功能 - toggleFullscreen() { - if (!document.fullscreenElement) { - this.container.requestFullscreen().then(() => { - this.container.classList.add('fullscreen'); - this.toggleFullscreenBtn.textContent = '退出全屏'; - }).catch(err => { - alert(`无法进入全屏模式: ${err.message}`); - }); - } else { - document.exitFullscreen().then(() => { - this.container.classList.remove('fullscreen'); - this.toggleFullscreenBtn.textContent = '全屏显示'; - }); - } - } - - togglePanel() { - this.controlPanel.classList.toggle('hidden'); - const isHidden = this.controlPanel.classList.contains('hidden'); - this.togglePanelBtn.textContent = isHidden ? '显示控制面板' : '隐藏控制面板'; - this.showPanelBtn.style.display = isHidden ? 'block' : 'none'; - - // 同时切换容器的panel-hidden类,用于扩展文本显示区域 - this.container.classList.toggle('panel-hidden', isHidden); - - // 切换body的背景样式,隐藏蓝色渐变背景 - document.body.classList.toggle('panel-hidden-bg', isHidden); - } - - // 键盘快捷键 - handleKeyboard(e) { - if (e.target.tagName === 'TEXTAREA') return; - - switch(e.key) { - case ' ': - e.preventDefault(); - if (this.isScrolling && !this.isPaused) { - this.pauseScrolling(); - } else { - this.startScrolling(); - } - break; - case 'r': - case 'R': - this.resetScrolling(); - break; - case 'f': - case 'F': - this.toggleFullscreen(); - break; - case 'Escape': - if (document.fullscreenElement) { - this.toggleFullscreen(); - } - break; - } - } - - // 快速定位功能 - jumpToLine() { - if (!this.formattedText || this.formattedText.length === 0) { - alert('请先格式化文本'); - return; - } - - const targetLine = parseInt(this.jumpToLineInput.value); - if (isNaN(targetLine) || targetLine < 1 || targetLine > this.formattedText.length) { - alert(`请输入有效的行号 (1-${this.formattedText.length})`); - return; - } - - this.stopScrolling(); - this.currentLine = targetLine - 1; // 转换为0基索引 - this.currentPosition = this.calculatePositionForLine(this.currentLine); - const isMirror = this.mirrorText.checked; - const mirrorScale = isMirror ? ' scaleX(-1)' : ' scaleX(1)'; - this.textDisplay.style.transform = `translateY(-${this.currentPosition}px)${mirrorScale}`; - this.updateProgress(); - } - - calculatePositionForLine(lineIndex) { - let position = 0; - for (let i = 0; i < lineIndex && i < this.lineHeights.length; i++) { - position += this.lineHeights[i] || this.getCurrentLineHeight(); - } - return position; - } - - // 进度更新 - updateProgress() { - const progress = this.totalLines > 0 ? (this.currentLine / this.totalLines) * 100 : 0; - this.progressFill.style.width = progress + '%'; - this.currentLineSpan.textContent = this.currentLine + 1; - } - - // 工具方法 - getCurrentLineHeight() { - const firstParagraph = this.textDisplay.querySelector('p'); - if (firstParagraph) { - return firstParagraph.offsetHeight + - parseFloat(getComputedStyle(firstParagraph).marginBottom); - } - return 50; // 默认值 - } - - handleResize() { - // 重新计算行高和位置 - if (this.formattedText.length > 0) { - this.displayFormattedText(); - } else { - // 如果没有格式化文本,重新初始化位置 - this.initializeTextPosition(); - } - } - - handleFullscreenChange() { - if (!document.fullscreenElement) { - this.container.classList.remove('fullscreen'); - this.toggleFullscreenBtn.textContent = '全屏显示'; - } - } - - // 设置保存和加载 - saveSettings() { - const settings = { - scrollSpeed: this.scrollSpeed.value, - fontSize: this.fontSize.value, - lineHeight: this.lineHeight.value, - textColor: this.textColor.value, - bgColor: this.bgColor.value, - mirrorText: this.mirrorText.checked, - maxWordsPerParagraph: this.maxWordsPerParagraph.value, - forceParagraphBreak: this.forceParagraphBreak.checked - }; - localStorage.setItem('teleprompterSettings', JSON.stringify(settings)); - } - - loadSettings() { - // 加载设置 - const settings = localStorage.getItem('teleprompterSettings'); - if (settings) { - const parsed = JSON.parse(settings); - this.scrollSpeed.value = parsed.scrollSpeed || 50; - this.fontSize.value = parsed.fontSize || 32; - this.lineHeight.value = parsed.lineHeight || 1.8; - this.textColor.value = parsed.textColor || '#ffffff'; - this.bgColor.value = parsed.bgColor || '#000000'; - this.mirrorText.checked = parsed.mirrorText || false; - this.maxWordsPerParagraph.value = parsed.maxWordsPerParagraph || 50; - this.forceParagraphBreak.checked = parsed.forceParagraphBreak !== undefined ? parsed.forceParagraphBreak : true; - - // 应用设置 - this.speedValue.textContent = parseFloat(this.scrollSpeed.value).toFixed(1); - this.updateFontSize(); - this.updateLineHeight(); - this.updateTextColor(); - this.updateBgColor(); - this.updateMirrorText(); - this.updateMaxWordsPerParagraph(); - this.updateForceParagraphBreak(); - } - - // 加载文本 - const savedText = localStorage.getItem('teleprompterText'); - if (savedText) { - this.textInput.value = savedText; - setTimeout(() => this.formatText(), 100); - } - } -} - -// 初始化应用 -document.addEventListener('DOMContentLoaded', () => { - new Teleprompter(); -}); - -// 添加一些提示信息 -window.addEventListener('load', () => { - setTimeout(() => { - console.log('提词器应用已加载完成!'); - console.log('快捷键说明:'); - console.log('- 空格键:开始/暂停滚动'); - console.log('- R键:重置滚动'); - console.log('- F键:切换全屏'); - console.log('- ESC键:退出全屏'); - }, 1000); -}); \ No newline at end of file diff --git a/文本提词器/styles.css b/文本提词器/styles.css deleted file mode 100644 index 39f1da9..0000000 --- a/文本提词器/styles.css +++ /dev/null @@ -1,472 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - height: 100vh; - overflow: hidden; -} - -.container { - display: flex; - height: 100vh; - position: relative; -} - -/* 控制面板样式 */ - .control-panel { - width: 320px; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(10px); - border-right: 1px solid rgba(255, 255, 255, 0.2); - padding: 20px; - overflow-y: auto; - transition: transform 0.3s ease; - box-shadow: 2px 0 20px rgba(0, 0, 0, 0.1); - position: relative; - z-index: 1000; - } - - .control-panel.hidden { - transform: translateX(-100%); - } - - .control-panel.hidden + .teleprompter-container { - margin-left: 0; - } - - .teleprompter-container { - transition: margin-left 0.3s ease; - } - -.panel-section { - margin-bottom: 25px; - padding: 15px; - background: rgba(255, 255, 255, 0.8); - border-radius: 10px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); -} - -/* 版权信息样式 */ -.copyright-section { - margin-top: 20px; - padding: 15px 0 5px 0; - background: rgba(255, 255, 255, 0.6); - border-radius: 8px; - border-top: 2px solid rgba(102, 126, 234, 0.3); -} - -.copyright-section p { - margin: 0; - text-align: center; - font-size: 12px; - color: #666; - font-weight: 500; - letter-spacing: 0.5px; -} - -.panel-section h3 { - color: #333; - margin-bottom: 15px; - font-size: 16px; - font-weight: 600; - border-bottom: 2px solid #667eea; - padding-bottom: 5px; -} - -/* 文本输入区域 */ -#textInput { - width: 100%; - padding: 12px; - border: 2px solid #e1e5e9; - border-radius: 8px; - font-size: 14px; - resize: vertical; - min-height: 120px; - font-family: inherit; - transition: border-color 0.3s ease; -} - -#textInput:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} - -/* 控制组 */ -.control-group { - margin-bottom: 15px; - display: flex; - align-items: center; - gap: 8px; - flex-wrap: nowrap; /* 防止换行 */ -} - -.control-group label { - font-weight: 500; - color: #555; - min-width: 60px; - font-size: 13px; - flex-shrink: 0; /* 防止标签收缩 */ -} - -.control-group input[type="range"] { - flex: 1; - min-width: 0; /* 允许滑块收缩 */ - height: 6px; - border-radius: 3px; - background: #ddd; - outline: none; - -webkit-appearance: none; -} - -.control-group input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 18px; - height: 18px; - border-radius: 50%; - background: #667eea; - cursor: pointer; - transition: background 0.3s ease; -} - -.control-group input[type="range"]::-webkit-slider-thumb:hover { - background: #5a6fd8; -} - -.control-group input[type="color"] { - width: 50px; - height: 30px; - border: none; - border-radius: 5px; - cursor: pointer; -} - -.control-group input[type="checkbox"] { - width: 18px; - height: 18px; - cursor: pointer; - accent-color: #667eea; -} - -.control-group span { - font-weight: 600; - color: #667eea; - min-width: 35px; - max-width: 40px; /* 限制最大宽度 */ - text-align: center; - font-size: 12px; - flex-shrink: 0; /* 防止数字显示收缩 */ -} - -/* 按钮样式 */ -button { - padding: 10px 16px; - border: none; - border-radius: 8px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - margin: 5px; -} - -#formatText { - background: #28a745; - color: white; - width: 100%; - margin-top: 10px; -} - -#formatText:hover { - background: #218838; - transform: translateY(-1px); -} - -.control-buttons { - display: flex; - gap: 10px; - justify-content: center; -} - -#startScroll { - background: #007bff; - color: white; -} - -#startScroll:hover { - background: #0056b3; -} - -#pauseScroll { - background: #ffc107; - color: #212529; -} - -#pauseScroll:hover { - background: #e0a800; -} - -#resetScroll { - background: #dc3545; - color: white; -} - -#resetScroll:hover { - background: #c82333; -} - -#toggleFullscreen { - background: #6f42c1; - color: white; - width: 100%; - margin-bottom: 10px; -} - -#toggleFullscreen:hover { - background: #5a32a3; -} - -#togglePanel { - background: #17a2b8; - color: white; - width: 100%; -} - -#togglePanel:hover { - background: #138496; -} - -/* 进度条 */ -.progress-bar { - width: 100%; - height: 8px; - background: #e9ecef; - border-radius: 4px; - overflow: hidden; - margin-bottom: 10px; -} - -.progress-fill { - height: 100%; - background: linear-gradient(90deg, #667eea, #764ba2); - width: 0%; - transition: width 0.3s ease; -} - -.progress-text { - text-align: center; - font-weight: 600; - color: #667eea; - font-size: 14px; -} - -/* 提词器显示区域 */ -.teleprompter-container { - flex: 1; - background: #000; - position: relative; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - transition: margin-left 0.3s ease; - min-height: 0; /* 修复flex项目的高度问题 */ -} - -/* 悬浮恢复按钮 */ -.show-panel-btn { - position: absolute; - top: 20px; - left: 20px; - background: rgba(23, 162, 184, 0.9); - color: white; - border: none; - padding: 10px 15px; - border-radius: 8px; - cursor: pointer; - font-size: 14px; - z-index: 1001; - transition: all 0.3s ease; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); -} - -.show-panel-btn:hover { - background: rgba(23, 162, 184, 1); - transform: translateY(-2px); - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4); -} - -.teleprompter-content { - width: 98%; - max-width: 1400px; - height: 85vh; - position: relative; - overflow: hidden; - transition: width 0.3s ease, max-width 0.3s ease; /* 添加过渡效果 */ -} - -/* 控制面板隐藏时扩展文本内容区域 */ -.container.panel-hidden .teleprompter-content { - width: 100%; /* 占满整个可用宽度 */ - max-width: none; /* 移除最大宽度限制 */ - height: 100vh; /* 占满整个视口高度 */ - margin: 0; /* 移除边距 */ - padding: 0 40px; /* 添加一些内边距,让文本不会太靠边 */ -} - -.text-display { - padding: 50px 20px; - transition: transform 0.1s linear; - will-change: transform; - color: #ffffff; /* 确保文本颜色为白色 */ - font-size: 32px; /* 默认字体大小 */ - line-height: 1.8; /* 默认行高 */ - font-weight: 500; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); -} - -/* 控制面板隐藏时调整文本显示的内边距 */ -.container.panel-hidden .text-display { - padding: 50px 60px; /* 增加左右内边距,充分利用屏幕宽度 */ -} - -/* 全屏模式下的文本显示内边距 */ -.container.fullscreen .text-display { - padding: 50px 60px; /* 增加左右内边距,充分利用屏幕宽度 */ -} - -.text-display p { - margin-bottom: 0.5em; /* 减少段间距 */ - line-height: 1.8; /* 应用到每一行 */ - text-align: center; - font-weight: 400; - letter-spacing: 0.5px; - transition: all 0.3s ease; -} - -/* 阅读指导线 */ - .reading-guide { - position: absolute; - top: 50%; - left: 0; - right: 0; - height: 3px; - background: rgba(255, 255, 255, 0.8); - transform: translateY(-50%); - pointer-events: none; - opacity: 0; - transition: opacity 0.3s ease; - display: none; /* 隐藏阅读指导线 */ - } - -.reading-guide::before { - content: ''; - position: absolute; - top: -10px; - left: 0; - right: 0; - height: 20px; - background: linear-gradient( - 90deg, - transparent 0%, - rgba(255, 255, 255, 0.3) 20%, - rgba(255, 255, 255, 0.8) 50%, - rgba(255, 255, 255, 0.3) 80%, - transparent 100% - ); -} - -.reading-guide.visible { - opacity: 1; -} - -/* 全屏模式 */ -.container.fullscreen .control-panel { - display: none; -} - -.container.fullscreen .teleprompter-container { - width: 100vw; - height: 100vh; -} - -.container.fullscreen .teleprompter-content { - width: 100%; - max-width: none; - height: 100vh; - padding: 0 40px; /* 添加一些内边距,让文本不会太靠边 */ -} - -/* 控制面板隐藏时的全屏扩展 */ -.container.panel-hidden .teleprompter-container { - margin-left: 0; - width: 100vw; /* 使用视口宽度,占满整个屏幕 */ - border-radius: 0; - position: absolute; /* 使用绝对定位占满空间 */ - left: 0; - top: 0; - height: 100vh; /* 占满整个视口高度 */ -} - -.container.panel-hidden { - padding: 0; - border-radius: 0; - overflow: hidden; /* 防止出现滚动条 */ -} - -/* 当控制面板隐藏时,移除body的渐变背景 */ -body.panel-hidden-bg { - background: #000000; /* 纯黑色背景 */ -} - -/* 响应式设计 */ -@media (max-width: 768px) { - .control-panel { - width: 280px; - padding: 15px; - } - - .teleprompter-content { - width: 98%; - max-width: none; - } - - .text-display { - padding: 30px 15px; - } -} - -@media (min-width: 1200px) { - .teleprompter-content { - width: 95%; - max-width: 1600px; - } -} - -/* 动画效果 */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.panel-section { - animation: fadeIn 0.5s ease forwards; -} - -.panel-section:nth-child(1) { animation-delay: 0.1s; } -.panel-section:nth-child(2) { animation-delay: 0.2s; } -.panel-section:nth-child(3) { animation-delay: 0.3s; } -.panel-section:nth-child(4) { animation-delay: 0.4s; } -.panel-section:nth-child(5) { animation-delay: 0.5s; } \ No newline at end of file