location_on 首页 keyboard_arrow_right 观影清单 keyboard_arrow_right 正文

这一步让我瞬间清醒,蘑菇视频的小窗播放我试了三种方案,最后选了这一种

观影清单 access_alarms2026-02-02 visibility18 text_decrease title text_increase

这一步让我瞬间清醒,蘑菇视频的小窗播放我试了三种方案,最后选了这一种

这一步让我瞬间清醒,蘑菇视频的小窗播放我试了三种方案,最后选了这一种

前言 作为长期打磨内容、不断折腾展示方式的人,我一直想把蘑菇视频做成“随看随走”的体验——观众在浏览页面、滚动阅读或切换标签时,视频能在一个小窗里继续播放,不丢失观看进度,也不破坏页面布局。为此我先后试了三种实现方案:浏览器原生画中画(Picture-in-Picture)、用新窗口/弹窗承载视频、以及在页面内做一个可拖拽的小窗播放器。试验过程让我对兼容性、用户体验和可落地性有了非常清晰的判断,最终把第三种方案投入生产。下面把过程、优劣以及最终实现的代码和部署建议都写清楚,方便你直接拿去用或改造。

目标与约束

  • 用户在浏览或阅读时,视频能以“小窗”形式持续播放(悬浮在页面角落或可拖动)。
  • 控制要简单:播放/暂停、继续回到原位置、关闭小窗。
  • 尽量兼容主流桌面/移动浏览器(考虑限制与不同浏览器策略)。
  • 能方便地在 Google 网站(或通过外部嵌入)投放。

我试过的三种方案(含优缺点)

1) 浏览器原生 Picture-in-Picture(PiP)API

  • 思路:调用 video.requestPictureInPicture(),让浏览器把视频弹出到系统级小窗。
  • 优点:原生行为、占用少、系统级小窗随其他标签/窗口都在上层。
  • 缺点:不同浏览器/平台支持不一致(尤其移动端差异大);样式和位置无法自定义;有些视频来源/跨域会被阻止;部分浏览器需要用户交互才允许调用。
  • 适合场景:桌面浏览器做极简小窗,不需要自定义样式时。

2) 新窗口(window.open)或弹窗承载视频

  • 思路:用 window.open 打开一个小尺寸窗口,里面加载视频页面。
  • 优点:位置与大小可控(通过打开参数),与主页面隔离不会影响布局。
  • 缺点:受浏览器弹窗拦截、用户体验差(新窗口切换、任务栏占用);移动端基本不可行。
  • 适合场景:内部工具或受控环境下临时调试,不推荐做为面向用户的常规方案。

3) 页面内悬浮小窗(我最终选择的方案)

  • 思路:在当前页面内用一个固定定位或可拖动的浮层承载 video 元素,支持最小化、拖拽、回到原位等交互。
  • 优点:兼容性好、样式可以完全自定义、交互体验可控、移动端更容易实现响应式方案;更容易与网站其他功能(如播放列表、弹幕、评论)联动。
  • 缺点:如果页面被刷新或切换,需考虑播放状态保存或使用单页应用路由;需要自己处理拖拽/遮挡/样式等细节。
  • 适合场景:面向普通访客的媒体站点、需要自定义样式和交互的场景、在 Google 网站通过外部嵌入时也很方便。

为什么最后选第三种 实测后,第三种方案在用户体验与落地可行性上取得了最佳平衡。PiP 虽然方便,但在移动端和某些设备上不可用,且无法风格化;新窗口方案的问题太明显。页面内小窗既能保证一致的样式,又能实现更多交互(比如拖拽位置记忆、最小化成图标、与播放列表联动),对内容创作者更友好,便于在各种页面布局下整合。

最终实现(可直接拿去部署的示例) 下面是一个简洁且实用的实现,包含:嵌入 video、悬浮小窗样式、最小化/还原/关闭、拖拽支持。将视频地址替换为蘑菇视频的播放源(注意跨域与授权),或将这个页面托管后通过 iframe 嵌入 Google 网站。

HTML(示例结构)

开启小窗播放

CSS(关键样式)

mini-player {

position: fixed; right: 16px; bottom: 16px; width: 320px; height: 180px; background: #000; box-shadow: 0 8px 24px rgba(0,0,0,0.3); border-radius: 8px; overflow: hidden; z-index: 9999; display: flex; align-items: center; justify-content: center; touch-action: none; /* 允许拖拽手势 */ }

mini-player.hidden { display: none; }

mini-player video {

width: 100%; height: 100%; object-fit: cover; } .mini-controls { position: absolute; top: 6px; right: 6px; display: flex; gap: 6px; } .mini-controls button { background: rgba(255,255,255,0.85); border: none; padding: 4px 6px; border-radius: 4px; cursor: pointer; }

JavaScript(交互与拖拽逻辑) (function(){ const mini = document.getElementById('mini-player'); const video = document.getElementById('mini-video'); const openBtn = document.getElementById('open-mini'); const closeBtn = document.getElementById('btn-close'); const toggleBtn = document.getElementById('btn-toggle');

let isMinimized = false; let offset = {x:0, y:0}; let dragging = false; let start = {x:0, y:0}; // 打开小窗(可在这里把主页面的视频进度同步到小窗) openBtn.addEventListener('click', () => { mini.classList.remove('hidden'); video.play().catch(()=>{ /* 如果自动播放被阻止,用户点击后再播放 */ }); });

// 关闭 closeBtn.addEventListener('click', () => { video.pause(); mini.classList.add('hidden'); });

// 切换最小化(示例:把小窗缩成更小) toggleBtn.addEventListener('click', () => { isMinimized = !isMinimized; if (isMinimized) { mini.style.width = '160px'; mini.style.height = '90px'; } else { mini.style.width = '320px'; mini.style.height = '180px'; } });

// 简单拖拽(兼容鼠标和触摸) function pointerDown(e){ dragging = true; start.x = (e.touches ? e.touches[0].clientX : e.clientX); start.y = (e.touches ? e.touches[0].clientY : e.clientY); const rect = mini.getBoundingClientRect(); offset.x = start.x - rect.left; offset.y = start.y - rect.top; document.addEventListener('mousemove', pointerMove); document.addEventListener('mouseup', pointerUp); document.addEventListener('touchmove', pointerMove, {passive:false}); document.addEventListener('touchend', pointerUp); } function pointerMove(e){ if (!dragging) return; e.preventDefault(); const clientX = (e.touches ? e.touches[0].clientX : e.clientX); const clientY = (e.touches ? e.touches[0].clientY : e.clientY); let left = clientX - offset.x; let top = clientY - offset.y; // 限制范围,避免拖出视口 left = Math.max(8, Math.min(window.innerWidth - mini.offsetWidth - 8, left)); top = Math.max(8, Math.min(window.innerHeight - mini.offsetHeight - 8, top)); mini.style.left = left + 'px'; mini.style.top = top + 'px'; mini.style.right = 'auto'; mini.style.bottom = 'auto'; } function pointerUp(){ dragging = false; document.removeEventListener('mousemove', pointerMove); document.removeEventListener('mouseup', pointerUp); document.removeEventListener('touchmove', pointerMove); document.removeEventListener('touchend', pointerUp); // 这里可以把位置保存到 localStorage,以便刷新后恢复 localStorage.setItem('miniPos', JSON.stringify({left: mini.style.left, top: mini.style.top})); }

// 把拖拽绑定到容器(也可以绑定到标题栏) mini.addEventListener('mousedown', pointerDown); mini.addEventListener('touchstart', pointerDown);

// 页面加载时恢复位置 const saved = localStorage.getItem('miniPos'); if (saved) { try { const pos = JSON.parse(saved); if (pos.left && pos.top) { mini.style.left = pos.left; mini.style.top = pos.top; mini.style.right = 'auto'; mini.style.bottom = 'auto'; } } catch(e){} }

// 如果你希望在小窗与主页面视频间同步进度、播放状态, // 可以在这里增加事件监听,通过 postMessage 或直接操作 DOM 实现。 })();

部署与在 Google 网站上的实用方法

  • 直接嵌入 HTML+JS 到 Google 新版 Sites 的原生区域一般受限,无法执行自定义脚本。常见可行方案: 1) 将上述页面托管在你自己的服务器、GitHub Pages 或任何静态托管(Netlify、Vercel 等),然后在 Google 网站中用“嵌入”功能插入该页面的 iframe 链接。这样页面内的脚本可以正常运行,外观也能完全自定义。 2) 使用 CodePen / JSFiddle 等在线示例页,把可运行的示例生成分享链接,再以 iframe 方式嵌入到 Google 网站中(注意宽高与响应式)。
  • 视频源问题:若蘑菇视频播放源受限或有防盗链,需要确保你有权嵌入或使用官方提供的播放链接/播放器 SDK。若是外部视频平台,优先使用平台提供的嵌入播放器并在 iframe 内做小窗逻辑。

兼容性小贴士

  • 自动播放策略:多数浏览器对自动播放有策略,通常静音的视频可以自动播放,声音需用户交互。要在打开小窗时自动播放,最好先通过用户点击启动播放。
  • 移动端适配:移动端的可用空间有限,建议在宽度小于某阈值时把小窗改为底部悬浮条或隐藏拖拽功能,提供明显的关闭和回到原页面按钮。
  • 无痕/隐私模式:部分浏览器在私密模式会禁用某些存储(如 localStorage),获取/保存位置时需兼容 try/catch。

结语(附建议) 小窗播放并不是单纯的技术特性,而是提升用户留存与内容消费连贯性的体验细节。选择页面内可控的小窗后,你能自由定义缩放、样式、交互逻辑,还能把它和播放列表、相关推荐、计时器等功能结合,进一步延长观看时长和提高转化。把上面的代码拿去做一个最小可用版本,先在自己的域名或 GitHub Pages 上试验,再嵌入 Google 网站,这样部署风险最低且调试最方便。

report_problem 举报
镜头语言的小机关,解释了人物动机:说的就是91大事件(新91视频的暗示别错过)
« 上一篇 2026-02-02
蘑菇视频电脑版深夜刷到的投屏体验翻车?多半是这个原因
下一篇 » 2026-02-03