Electron 视频播放器开发
作成日: 2025年1月9日
作成日: 2025年1月9日
使用electron写一个本地视频播放器,要求:
1,多种视频格式支持:如 MP4、MKV、AVI、MOV、WMV 等。
2,点击播放进度条可以快速跳到对应的某个位置
3,按下左右键分别后退、前进5秒
4,按下上下键分别增加、减少5%音量
5,按下f/F键(不区分大小写)实现全屏播放
6,鼠标单击播放/暂停,双击视频区域进入/退出全屏
7,按下j/k键(不区分大小写)分别前进、后退10%
8,播放本地视频
9,视频界面有两种语言,英语和汉语,默认使用英语
10,第一个标签页为“本地视频”,第二个标签页为“远程视频”,可以在用户输入iframe标签加载对应的视频
下面给出一个示例 Electron 项目的结构示例及关键代码,演示如何实现一个具有以下功能的视频播放器:
下面代码仅供参考,建议在此基础上进行扩展和调整,以满足更多需求。
textelectron-video-player/ ├── package.json ├── main.js # Electron 主进程入口文件 ├── index.html # 渲染进程主页面 ├── renderer.js # 页面逻辑脚本 ├── style.css # 样式文件 └── assets/ └── sample.mp4 # 测试用的视频(可自行替换)
json{ "name": "electron-video-player", "version": "1.0.0", "main": "main.js", "scripts": { "start": "electron ." }, "dependencies": { "electron": "^22.0.0" } }
注意:如果你想使用更高版本的 Electron,请在此处进行相应修改。
jsconst { app, BrowserWindow } = require('electron'); const path = require('path'); let mainWindow; function createWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: false, // 建议关闭以提高安全性 contextIsolation: true, // 启用 contextIsolation 后,需使用 preload 或 IPC 通信 preload: path.join(__dirname, 'preload.js') // 如果有预加载脚本,可在此处配置 } }); mainWindow.loadFile('index.html'); // 打开开发者工具,方便调试 // mainWindow.webContents.openDevTools(); mainWindow.on('closed', () => { mainWindow = null; }); } app.whenReady().then(createWindow); app.on('window-all-closed', () => { // 在 macOS 上,除非用户用 Cmd + Q 退出,否则绝大部分应用会保持激活 if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { // 在 macOS 上,当单击 dock 图标并且没有其他窗口打开时 if (mainWindow === null) { createWindow(); } });
如果你想在渲染进程中直接使用
require,可将nodeIntegration设置为true,但这会带来安全隐患,需自行评估。
html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Electron Video Player</title> <link rel="stylesheet" href="style.css"> </head> <body> <!-- 语言切换按钮 --> <div class="language-switch"> <button id="toggle-lang">切换到中文</button> </div> <!-- 标签页导航 --> <div class="tab-nav"> <button class="tab-button" id="local-video-tab">Local Video</button> <button class="tab-button" id="remote-video-tab">Remote Video</button> </div> <!-- 内容区域 --> <div class="tab-content" id="local-video-content"> <div id="video-container"> <video id="video-player" src="assets/sample.mp4" controls="false"></video> <!-- 自定义控件示例,这里主要演示进度条 --> <div id="controls-container"> <input type="range" id="progress-bar" value="0" step="0.1" min="0" max="100"> </div> </div> </div> <div class="tab-content" id="remote-video-content" style="display: none;"> <label for="iframe-url" class="lang lang-en">Please input iframe URL: </label> <label for="iframe-url" class="lang lang-zh" style="display: none;">请输入iframe地址: </label> <input type="text" id="iframe-url" placeholder="https://example.com/video-iframe"> <button id="load-iframe" class="lang lang-en">Load iFrame</button> <button id="load-iframe" class="lang lang-zh" style="display: none;">加载视频</button> <div id="iframe-container"></div> </div> <script src="renderer.js"></script> </body> </html>
这里为了演示,使用了一个自定义进度条
<input type="range">。如果想更精细控制,可以使用<progress>或者完全自定义一个元素并手动绘制。
cssbody { margin: 0; padding: 0; font-family: sans-serif; } .language-switch { margin: 10px; } .tab-nav { margin: 10px; } .tab-button { padding: 6px 12px; margin-right: 5px; cursor: pointer; } .tab-content { margin: 10px; } #video-container { position: relative; width: 800px; max-width: 100%; } #video-player { width: 100%; outline: none; background: black; cursor: pointer; /* 让视频区域表现为可点击 */ } #controls-container { display: flex; align-items: center; width: 100%; margin-top: 5px; } #progress-bar { width: 100%; } #iframe-container { width: 800px; height: 450px; margin-top: 10px; background: #ccc; /* 仅做示意,可自行替换或去掉 */ }
主要实现以下功能:
js// 缓存一些 DOM 元素 const video = document.getElementById('video-player'); const progressBar = document.getElementById('progress-bar'); const localVideoContent = document.getElementById('local-video-content'); const remoteVideoContent = document.getElementById('remote-video-content'); const localVideoTab = document.getElementById('local-video-tab'); const remoteVideoTab = document.getElementById('remote-video-tab'); const iframeUrlInput = document.getElementById('iframe-url'); const loadIframeButton = document.getElementById('load-iframe'); const iframeContainer = document.getElementById('iframe-container'); const toggleLangBtn = document.getElementById('toggle-lang'); // 语言相关 // 用于切换隐藏/显示类名为 .lang-en / .lang-zh 的元素 let currentLang = 'en'; // 默认英文 // 切换语言函数 function toggleLanguage() { const enElements = document.querySelectorAll('.lang-en'); const zhElements = document.querySelectorAll('.lang-zh'); if (currentLang === 'en') { // 切换到中文 enElements.forEach(el => el.style.display = 'none'); zhElements.forEach(el => el.style.display = 'inline-block'); toggleLangBtn.innerText = 'Switch to English'; currentLang = 'zh'; } else { // 切换到英文 enElements.forEach(el => el.style.display = 'inline-block'); zhElements.forEach(el => el.style.display = 'none'); toggleLangBtn.innerText = '切换到中文'; currentLang = 'en'; } } // 初始化语言 toggleLangBtn.addEventListener('click', toggleLanguage); // 标签页切换 localVideoTab.addEventListener('click', () => { localVideoContent.style.display = 'block'; remoteVideoContent.style.display = 'none'; }); remoteVideoTab.addEventListener('click', () => { localVideoContent.style.display = 'none'; remoteVideoContent.style.display = 'block'; }); // 远程视频 iframe 加载 loadIframeButton.addEventListener('click', () => { const url = iframeUrlInput.value.trim(); if (url) { // 这里简单演示直接生成 iframe // 注意:实际使用中要确保 iframe URL 安全 iframeContainer.innerHTML = `<iframe src="${url}" width="100%" height="100%" frameborder="0"></iframe>`; } }); // 播放器事件 // 1. 更新进度条 video.addEventListener('timeupdate', () => { if (!video.duration) return; const percent = (video.currentTime / video.duration) * 100; progressBar.value = percent; }); // 2. 拖动进度条跳转 progressBar.addEventListener('input', () => { if (!video.duration) return; const newTime = (progressBar.value / 100) * video.duration; video.currentTime = newTime; }); // 3. 单击播放/暂停,双击全屏 let clickTimer = null; video.addEventListener('click', (event) => { if (clickTimer) { // 如果双击 clearTimeout(clickTimer); clickTimer = null; toggleFullScreen(); } else { // 单击 clickTimer = setTimeout(() => { clickTimer = null; togglePlayPause(); }, 300); // 300ms 内第二次点击,则视为双击 } }); // 播放/暂停 function togglePlayPause() { if (video.paused) { video.play(); } else { video.pause(); } } // 4. 进入/退出全屏 function toggleFullScreen() { if (!document.fullscreenElement) { video.requestFullscreen(); } else { document.exitFullscreen(); } } // 键盘事件监听 document.addEventListener('keydown', (event) => { const key = event.key.toLowerCase(); switch (key) { case 'arrowleft': // 左箭头:后退 5 秒 video.currentTime = Math.max(0, video.currentTime - 5); break; case 'arrowright': // 右箭头:前进 5 秒 video.currentTime = Math.min(video.duration, video.currentTime + 5); break; case 'arrowup': // 上箭头:音量 +5% video.volume = Math.min(1, video.volume + 0.05); break; case 'arrowdown': // 下箭头:音量 -5% video.volume = Math.max(0, video.volume - 0.05); break; case 'f': // f/F:全屏切换 toggleFullScreen(); break; case 'j': // j/J:前进 10% if (video.duration) { video.currentTime = Math.min( video.duration, video.currentTime + video.duration * 0.1 ); } break; case 'k': // k/K:后退 10% if (video.duration) { video.currentTime = Math.max( 0, video.currentTime - video.duration * 0.1 ); } break; default: break; } });
在项目根目录下(即 electron-video-player/)执行:
bashnpm install
安装依赖后,执行:
bashnpm start
Electron 窗口启动后,即可看到本示例项目提供的界面:
assets/sample.mp4)多格式支持
只要 Electron 运行的环境中安装了对应解码器,或者使用的是系统原生支持的格式,<video> 标签基本都能播放。也可以考虑使用第三方库(如 mpv.js、video.js 等)来扩展更多格式/功能支持。
单击/双击区分
目前采用在第一次点击时启动一个 300ms 的定时器,若 300ms 内检测到第二次点击,则视为双击,可以根据项目需求调整此阈值。
自定义进度条
可以改用 <progress> 或其他更华丽的自定义控件来显示进度条,也可以配合 canvas 绘制更酷炫的样式。
语言切换
这里的示例是通过在 HTML 中给元素加 .lang-en 和 .lang-zh 类名,并控制 display 来切换语言。如果需要更多语言,或者更丰富的文本内容,可以使用 i18n(如 i18next)等专业国际化库来做。
远程视频
本示例仅仅是通过 <iframe> 加载远程内容;实际应用中,可能需要鉴权、跨域等处理,甚至还需要自己搭建视频服务器,也可直接使用 <video> 加载远程视频流。
安全性
nodeIntegration 设置为 true,但要注意这样做会带来安全风险。preload.js + contextIsolation + IPC 通信在主进程和渲染进程之间传递数据。上述示例仅供参考,如果你需要更完整或更丰富的样式及功能,请根据自身需求自行扩展。祝你开发顺利!