diff --git a/pdf提词器/index.html b/pdf提词器/index.html deleted file mode 100644 index ebbcc69..0000000 --- a/pdf提词器/index.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - PDF提词器 - - - -
-
-

PDF提词器

-

上传PDF文件,自动生成镜像提词效果

-
- -
-
- - - - - -

点击或拖拽PDF文件到此处

- -
-
- - - - - - - - -
- - - - - \ No newline at end of file diff --git a/pdf提词器/script.js b/pdf提词器/script.js deleted file mode 100644 index c81e4c9..0000000 --- a/pdf提词器/script.js +++ /dev/null @@ -1,408 +0,0 @@ -class PDFTeleprompter { - constructor() { - this.pdfDoc = null; - this.pages = []; - this.isPlaying = false; - this.animationId = null; - this.scrollPosition = 0; - this.scrollSpeed = 2; - - this.initializeElements(); - this.bindEvents(); - } - - initializeElements() { - this.uploadArea = document.getElementById('uploadArea'); - this.fileInput = document.getElementById('fileInput'); - this.controls = document.getElementById('controls'); - this.displayArea = document.getElementById('displayArea'); - this.teleprompterContent = document.getElementById('teleprompterContent'); - this.loading = document.getElementById('loading'); - this.speedSlider = document.getElementById('speedSlider'); - this.speedValue = document.getElementById('speedValue'); - this.playBtn = document.getElementById('playBtn'); - this.pauseBtn = document.getElementById('pauseBtn'); - this.resetBtn = document.getElementById('resetBtn'); - this.fullscreenBtn = document.getElementById('fullscreenBtn'); - - // 悬浮控制面板元素 - this.floatingControls = document.getElementById('floatingControls'); - this.floatPlayBtn = document.getElementById('floatPlayBtn'); - this.floatResetBtn = document.getElementById('floatResetBtn'); - this.floatExitBtn = document.getElementById('floatExitBtn'); - this.floatSpeedSlider = document.getElementById('floatSpeedSlider'); - this.floatSpeedValue = document.getElementById('floatSpeedValue'); - - // 滚动容器 - this.teleprompterContainer = document.querySelector('.teleprompter-container'); - } - - bindEvents() { - this.uploadArea.addEventListener('click', () => this.fileInput.click()); - this.fileInput.addEventListener('change', (e) => this.handleFileUpload(e)); - this.uploadArea.addEventListener('dragover', (e) => this.handleDragOver(e)); - this.uploadArea.addEventListener('drop', (e) => this.handleDrop(e)); - - this.speedSlider.addEventListener('input', (e) => { - this.scrollSpeed = parseFloat(e.target.value); - this.speedValue.textContent = this.scrollSpeed; - if (this.floatSpeedSlider) { - this.floatSpeedSlider.value = this.scrollSpeed; - this.floatSpeedValue.textContent = this.scrollSpeed; - } - }); - - this.playBtn.addEventListener('click', () => this.play()); - this.pauseBtn.addEventListener('click', () => this.pause()); - this.resetBtn.addEventListener('click', () => this.reset()); - this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen()); - - // 悬浮控制面板事件 - if (this.floatPlayBtn) { - this.floatPlayBtn.addEventListener('click', () => this.togglePlayPause()); - this.floatResetBtn.addEventListener('click', () => this.reset()); - this.floatExitBtn.addEventListener('click', () => this.exitFullscreen()); - - this.floatSpeedSlider.addEventListener('input', (e) => { - this.scrollSpeed = parseFloat(e.target.value); - this.floatSpeedValue.textContent = this.scrollSpeed; - this.speedSlider.value = this.scrollSpeed; - this.speedValue.textContent = this.scrollSpeed; - }); - } - - // 全屏状态变化监听 - ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(event => { - document.addEventListener(event, () => this.handleFullscreenChange()); - }); - - // 手动滚动监听 - this.teleprompterContainer.addEventListener('scroll', () => { - if (!this.isPlaying) { - this.scrollPosition = this.teleprompterContainer.scrollTop; - } - }); - - // 防止滚动时自动播放 - this.teleprompterContainer.addEventListener('mousedown', () => { - if (this.isPlaying) { - this.pause(); - } - }); - - // 键盘快捷键 - document.addEventListener('keydown', (e) => { - if (e.key === ' ' || e.key === 'Spacebar') { - e.preventDefault(); - this.togglePlayPause(); - } else if (e.key === 'f' || e.key === 'F') { - e.preventDefault(); - this.toggleFullscreen(); - } else if (e.key === 'Escape') { - if (document.fullscreenElement) { - this.exitFullscreen(); - } - } - }); - - // 触摸手势支持 - let touchStartY = 0; - this.teleprompterContainer.addEventListener('touchstart', (e) => { - touchStartY = e.touches[0].clientY; - if (this.isPlaying) { - this.pause(); - } - }); - - this.teleprompterContainer.addEventListener('touchmove', (e) => { - e.preventDefault(); - const touchY = e.touches[0].clientY; - const deltaY = touchStartY - touchY; - this.teleprompterContainer.scrollTop += deltaY; - touchStartY = touchY; - this.scrollPosition = this.teleprompterContainer.scrollTop; - }); - } - - async handleFileUpload(event) { - const file = event.target.files[0]; - if (file && file.type === 'application/pdf') { - await this.processPDF(file); - } - } - - async processPDF(file) { - this.showLoading(true); - - try { - const arrayBuffer = await file.arrayBuffer(); - this.pdfDoc = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; - - await this.renderAllPages(); - this.showControls(); - - } catch (error) { - console.error('Error processing PDF:', error); - alert('处理PDF文件时出错,请重试'); - } finally { - this.showLoading(false); - } - } - - async renderAllPages() { - this.pages = []; - this.teleprompterContent.innerHTML = ''; - - const totalPages = this.pdfDoc.numPages; - - for (let pageNum = 1; pageNum <= totalPages; pageNum++) { - const page = await this.pdfDoc.getPage(pageNum); - const canvas = await this.renderPageToCanvas(page); - - const img = document.createElement('img'); - img.src = canvas.toDataURL('image/png'); - img.style.width = '100%'; - img.style.height = 'auto'; - img.style.marginBottom = '20px'; - - this.teleprompterContent.appendChild(img); - this.pages.push(img); - } - } - - async renderPageToCanvas(page) { - const viewport = page.getViewport({ scale: 1.5 }); - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - - canvas.width = viewport.width; - canvas.height = viewport.height; - - const renderContext = { - canvasContext: context, - viewport: viewport - }; - - await page.render(renderContext).promise; - return canvas; - } - - showLoading(show) { - this.loading.style.display = show ? 'block' : 'none'; - } - - showControls() { - this.uploadArea.parentElement.style.display = 'none'; - this.controls.style.display = 'flex'; - this.displayArea.style.display = 'block'; - } - - play() { - if (!this.isPlaying) { - this.isPlaying = true; - this.playBtn.disabled = true; - this.pauseBtn.disabled = false; - this.animateScroll(); - } - } - - pause() { - this.isPlaying = false; - this.playBtn.disabled = false; - this.pauseBtn.disabled = true; - if (this.animationId) { - cancelAnimationFrame(this.animationId); - } - } - - reset() { - this.pause(); - this.scrollPosition = 0; - this.teleprompterContainer.scrollTop = 0; - } - - animateScroll() { - if (!this.isPlaying) return; - - const contentHeight = this.teleprompterContent.scrollHeight; - const containerHeight = this.teleprompterContainer.offsetHeight; - - this.scrollPosition += this.scrollSpeed; - - if (this.scrollPosition > contentHeight) { - this.scrollPosition = 0; - } - - this.teleprompterContainer.scrollTop = this.scrollPosition; - - this.animationId = requestAnimationFrame(() => this.animateScroll()); - } - - toggleFullscreen() { - if (!document.fullscreenElement) { - this.enterFullscreen(); - } else { - this.exitFullscreen(); - } - } - - enterFullscreen() { - const element = this.displayArea; - - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(); - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(); - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(); - } - } - - exitFullscreen() { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } - } - - handleFullscreenChange() { - const isFullscreen = !!( - document.fullscreenElement || - document.webkitFullscreenElement || - document.mozFullScreenElement || - document.msFullscreenElement - ); - - if (isFullscreen) { - this.displayArea.classList.add('fullscreen'); - document.body.classList.add('fullscreen'); - this.floatingControls.style.display = 'flex'; - this.fullscreenBtn.innerHTML = ` - - - - 退出全屏 - `; - } else { - this.displayArea.classList.remove('fullscreen'); - document.body.classList.remove('fullscreen'); - this.floatingControls.style.display = 'none'; - this.fullscreenBtn.innerHTML = ` - - - - 全屏 - `; - } - } - - togglePlayPause() { - if (this.isPlaying) { - this.pause(); - this.updatePlayButtons(false); - } else { - this.play(); - this.updatePlayButtons(true); - } - } - - updatePlayButtons(isPlaying) { - const playIcon = ` - - - - `; - const pauseIcon = ` - - - - - `; - - if (isPlaying) { - this.floatPlayBtn.innerHTML = pauseIcon; - } else { - this.floatPlayBtn.innerHTML = playIcon; - } - } - - play() { - if (!this.isPlaying) { - this.isPlaying = true; - this.playBtn.disabled = true; - this.pauseBtn.disabled = false; - this.updatePlayButtons(true); - this.animateScroll(); - } - } - - pause() { - this.isPlaying = false; - this.playBtn.disabled = false; - this.pauseBtn.disabled = true; - this.updatePlayButtons(false); - if (this.animationId) { - cancelAnimationFrame(this.animationId); - } - } - - // 键盘快捷键 - setupKeyboardShortcuts() { - document.addEventListener('keydown', (e) => { - switch(e.code) { - case 'Space': - e.preventDefault(); - if (this.isPlaying) { - this.pause(); - } else { - this.play(); - } - break; - case 'KeyR': - if (e.ctrlKey || e.metaKey) { - e.preventDefault(); - this.reset(); - } - break; - } - }); - } -} - -// 初始化应用 -document.addEventListener('DOMContentLoaded', () => { - const teleprompter = new PDFTeleprompter(); - teleprompter.setupKeyboardShortcuts(); -}); - -// 添加触摸手势支持 -let touchStartY = 0; -document.addEventListener('touchstart', (e) => { - touchStartY = e.touches[0].clientY; -}); - -document.addEventListener('touchend', (e) => { - const touchEndY = e.changedTouches[0].clientY; - const diff = touchStartY - touchEndY; - - if (Math.abs(diff) > 50) { - // 滑动控制滚动速度 - const speedSlider = document.getElementById('speedSlider'); - const currentSpeed = parseInt(speedSlider.value); - - if (diff > 0 && currentSpeed < 10) { - speedSlider.value = currentSpeed + 1; - } else if (diff < 0 && currentSpeed > 1) { - speedSlider.value = currentSpeed - 1; - } - - speedSlider.dispatchEvent(new Event('input')); - } -}); \ No newline at end of file diff --git a/pdf提词器/styles.css b/pdf提词器/styles.css deleted file mode 100644 index 7a3fb7e..0000000 --- a/pdf提词器/styles.css +++ /dev/null @@ -1,407 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - min-height: 100vh; - color: #333; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; -} - -header { - text-align: center; - margin-bottom: 40px; - color: white; -} - -h1 { - font-size: 2.5rem; - margin-bottom: 10px; - font-weight: 300; -} - -.subtitle { - font-size: 1.1rem; - opacity: 0.9; -} - -.upload-section { - margin-bottom: 30px; -} - -.upload-area { - border: 2px dashed rgba(255, 255, 255, 0.5); - border-radius: 15px; - padding: 60px 20px; - text-align: center; - cursor: pointer; - transition: all 0.3s ease; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); -} - -.upload-area:hover { - border-color: rgba(255, 255, 255, 0.8); - background: rgba(255, 255, 255, 0.2); - transform: translateY(-2px); -} - -.upload-area.dragover { - border-color: white; - background: rgba(255, 255, 255, 0.3); - transform: scale(1.02); -} - -.upload-icon { - width: 60px; - height: 60px; - color: white; - margin-bottom: 15px; -} - -.upload-text { - color: white; - font-size: 1.2rem; - font-weight: 300; -} - -.controls { - background: white; - border-radius: 15px; - padding: 30px; - margin-bottom: 30px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: 20px; -} - -.control-group { - display: flex; - align-items: center; - gap: 15px; -} - -.control-group label { - font-weight: 500; - color: #555; -} - -input[type="range"] { - width: 150px; - height: 6px; - border-radius: 3px; - background: #ddd; - outline: none; - -webkit-appearance: none; -} - -input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - width: 18px; - height: 18px; - border-radius: 50%; - background: #667eea; - cursor: pointer; -} - -#speedValue { - font-weight: bold; - color: #667eea; - min-width: 20px; -} - -.btn { - padding: 12px 24px; - border: none; - border-radius: 8px; - font-size: 1rem; - cursor: pointer; - transition: all 0.3s ease; - font-weight: 500; -} - -.btn.primary { - background: #667eea; - color: white; -} - -.btn.primary:hover { - background: #5a6fd8; - transform: translateY(-1px); -} - -.btn.secondary { - background: #f1f3f4; - color: #333; -} - -.btn.secondary:hover:not(:disabled) { - background: #e8eaed; - transform: translateY(-1px); -} - -.btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.display-area { - background: white; - border-radius: 15px; - overflow: hidden; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); - margin: 20px; - max-height: 70vh; -} - -.teleprompter-container { - height: 500px; - overflow-y: auto; - overflow-x: hidden; - position: relative; - background: #000; - cursor: grab; - scrollbar-width: thin; - scrollbar-color: #666 #222; -} - -.teleprompter-container::-webkit-scrollbar { - width: 8px; -} - -.teleprompter-container::-webkit-scrollbar-track { - background: #222; -} - -.teleprompter-container::-webkit-scrollbar-thumb { - background: #666; - border-radius: 4px; -} - -.teleprompter-container::-webkit-scrollbar-thumb:hover { - background: #888; -} - -.teleprompter-container:active { - cursor: grabbing; -} - -.teleprompter-content { - padding: 20px; - transform: scaleX(-1); - transition: transform 0.3s ease; - min-height: 100%; -} - -.teleprompter-content img { - width: 100%; - height: auto; - display: block; - margin-bottom: 20px; - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); -} - -/* 全屏模式无边框 */ -.display-area.fullscreen { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: 999; - border-radius: 0; - margin: 0; - max-height: none; -} - -.display-area.fullscreen .teleprompter-container { - height: 100vh; - border-radius: 0; - margin: 0; - padding: 0; -} - -.display-area.fullscreen .teleprompter-content { - padding: 0; -} - -.display-area.fullscreen .teleprompter-content img { - border-radius: 0; - box-shadow: none; - margin-bottom: 0; -} - -/* 滚动提示 */ -.scroll-indicator { - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - background: rgba(0, 0, 0, 0.5); - color: white; - padding: 8px 12px; - border-radius: 20px; - font-size: 12px; - opacity: 0; - transition: opacity 0.3s ease; - pointer-events: none; - z-index: 10; -} - -.teleprompter-container:hover .scroll-indicator { - opacity: 1; -} - -.loading { - text-align: center; - color: white; - padding: 40px; -} - -.spinner { - width: 40px; - height: 40px; - border: 4px solid rgba(255, 255, 255, 0.3); - border-top: 4px solid white; - border-radius: 50%; - animation: spin 1s linear infinite; - margin: 0 auto 20px; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.btn-icon { - width: 16px; - height: 16px; - margin-right: 8px; - vertical-align: middle; -} - -.floating-controls { - position: fixed; - top: 20px; - right: 20px; - background: rgba(0, 0, 0, 0.8); - backdrop-filter: blur(10px); - border-radius: 15px; - padding: 15px; - display: flex; - align-items: center; - gap: 10px; - z-index: 1000; - transition: all 0.3s ease; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); -} - -.floating-controls:hover { - background: rgba(0, 0, 0, 0.9); - transform: translateY(-2px); -} - -.float-btn { - width: 45px; - height: 45px; - border: none; - border-radius: 50%; - background: rgba(255, 255, 255, 0.2); - color: white; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; - backdrop-filter: blur(5px); -} - -.float-btn:hover { - background: rgba(255, 255, 255, 0.3); - transform: scale(1.1); -} - -.float-btn svg { - width: 20px; - height: 20px; -} - -.float-speed-control { - display: flex; - align-items: center; - gap: 8px; - padding: 0 10px; -} - -.float-speed-control input[type="range"] { - width: 100px; - height: 4px; - background: rgba(255, 255, 255, 0.3); -} - -.float-speed-control input[type="range"]::-webkit-slider-thumb { - width: 14px; - height: 14px; - background: white; -} - -#floatSpeedValue { - color: white; - font-size: 0.9rem; - font-weight: bold; - min-width: 25px; -} - -/* 全屏模式样式 */ -.display-area.fullscreen { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: 999; - border-radius: 0; - margin: 0; -} - -.display-area.fullscreen .teleprompter-container { - height: 100vh; - border-radius: 0; -} - -/* 全屏时隐藏原控制面板 */ -body.fullscreen .controls { - display: none !important; -} - -@media (max-width: 768px) { - .controls { - flex-direction: column; - align-items: stretch; - } - - .control-group { - justify-content: center; - } - - h1 { - font-size: 2rem; - } - - .teleprompter-container { - height: 400px; - } -} \ No newline at end of file