* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: rgba(255, 255, 255, 0.95);
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
header {
background: linear-gradient(to right, #1a2a6c, #b21f1f);
color: white;
padding: 25px 40px;
text-align: center;
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
max-width: 700px;
margin: 0 auto;
}
.content {
display: flex;
padding: 30px;
gap: 30px;
}
.video-section {
flex: 3;
background: #f8f9fa;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.video-container {
position: relative;
padding-top: 56.25%;
background: #000;
}
video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
}
.video-controls {
display: flex;
padding: 15px;
gap: 10px;
background: #e9ecef;
}
button {
background: #1a2a6c;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
button:hover {
background: #0d1a4d;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
button:disabled {
background: #6c757d;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.info-section {
flex: 2;
background: white;
padding: 25px;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}
h2 {
color: #1a2a6c;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #e9ecef;
}
.feature-list {
margin: 20px 0;
}
.feature {
display: flex;
align-items: flex-start;
margin-bottom: 15px;
}
.feature-icon {
background: #1a2a6c;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
flex-shrink: 0;
}
.pip-window {
position: fixed;
bottom: 20px;
right: 20px;
width: 300px;
height: 200px;
background: black;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
z-index: 1000;
display: none;
}
.pip-window video {
width: 100%;
height: 100%;
object-fit: cover;
}
.pip-controls {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
display: flex;
justify-content: center;
gap: 10px;
opacity: 0;
transition: opacity 0.3s;
}
.pip-window:hover .pip-controls {
opacity: 1;
}
.status {
padding: 15px;
background: #e9ecef;
border-radius: 8px;
margin-top: 20px;
font-family: monospace;
}
.browser-support {
margin-top: 30px;
padding: 20px;
background: #fff8e1;
border-radius: 8px;
border-left: 4px solid #ffc107;
}
.support-list {
display: flex;
gap: 15px;
margin-top: 15px;
flex-wrap: wrap;
}
.browser {
display: flex;
align-items: center;
gap: 8px;
}
.supported {
color: #28a745;
}
.unsupported {
color: #dc3545;
}
@media (max-width: 900px) {
.content {
flex-direction: column;
}
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>视频小窗模式演示</title>
<link href="css/pip.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="container">
<header>
<h1>视频小窗模式演示</h1>
<p class="subtitle">使用 Document Picture-in-Picture API 实现在其他内容上浮动播放视频</p>
</header>
<div class="content">
<div class="video-section">
<div class="video-container">
<video id="mainVideo" src="1.mp4" controls playsinline></video>
</div>
<div class="video-controls">
<button id="pipButton" title="开启小窗模式">
<svg width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M0 3.5A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5v-9zM1.5 3a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-13z"/>
<path d="M8 8.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-3z"/>
</svg> 开启小窗模式
</button>
<button id="fullscreenButton">
<svg width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M1.5 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4A1.5 1.5 0 0 1 1.5 0h4a.5.5 0 0 1 0 1h-4zM10 .5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 16 1.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5zM.5 10a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 0 14.5v-4a.5.5 0 0 1 .5-.5zm15 0a.5.5 0 0 1 .5.5v4a1.5 1.5 0 0 1-1.5 1.5h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5z"/>
</svg> 全屏
</button>
</div>
</div>
<div class="info-section">
<h2>Document Picture-in-Picture API</h2>
<div class="feature-list">
<div class="feature">
<div class="feature-icon">1</div>
<div>
<h3>任意 HTML 内容</h3>
<p>可以在小窗中显示视频控件、字幕等任意 HTML 元素</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">2</div>
<div>
<h3>保持播放状态</h3>
<p>进入小窗模式时视频持续播放,不中断观看体验</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">3</div>
<div>
<h3>双向同步</h3>
<p>主页面和小窗中的视频状态实时同步</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">4</div>
<div>
<h3>自由调整</h3>
<p>用户可以调整小窗位置和大小,适应不同需求</p>
</div>
</div>
</div>
<div class="status">
<p>当前状态:<span id="statusText">等待操作</span></p>
<p>小窗状态:<span id="pipStatus">未激活</span></p>
</div>
<div class="browser-support">
<h3>浏览器支持情况</h3>
<div class="support-list">
<div class="browser">
<svg width="24" height="24" fill="#4285F4" viewBox="0 0 24 24"><path d="M12 15.6l-3.9 2.3 1-4.3-3.2-2.9 4.3-.4L12 6.5l1.8 4.1 4.3.4-3.2 2.9 1 4.3z"/></svg>
<span class="supported">Chrome 108+</span>
</div>
<div class="browser">
<svg width="24" height="24" fill="#FF9500" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17.2 3H6.8l-5.2 9 5.2 9h10.4l5.2-9-5.2-9zm-1.15 16h-8.1l-4.04-7 4.04-7h8.09l4.04 7-4.03 7z"/></svg>
<span class="unsupported">Firefox</span>
</div>
<div class="browser">
<svg width="24" height="24" fill="#0078D7" viewBox="0 0 24 24"><path d="M0 0v24h24V0H0zm22 22H2V2h20v20z"/><path d="M12 12l-4 4 1.4 1.4 2.6-2.6 2.6 2.6 1.4-1.4z"/></svg>
<span class="unsupported">Edge</span>
</div>
<div class="browser">
<svg width="24" height="24" fill="#000000" viewBox="0 0 24 24"><path d="M18.7 4.3c-1.2-1.2-2.9-1.9-4.7-1.9H5C3.3 2.4 2 3.7 2 5.4v13.1c0 1.8 1.5 3.2 3.3 3.2H19c1.8 0 3.2-1.4 3.2-3.2V9c0-1.8-.7-3.5-1.9-4.7h-.6zM19 20.5H5.3c-.9 0-1.7-.7-1.7-1.7V5.4c0-.9.8-1.7 1.7-1.7h9c.9 0 1.7.7 1.7 1.7v3.9h3.9c.9 0 1.7.8 1.7 1.7v7.9c0 .9-.7 1.7-1.6 1.7z"/></svg>
<span class="unsupported">Safari</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="pipContainer" class="pip-window">
<video id="pipVideo" src="1.mp4" controls></video>
<div class="pip-controls">
<button id="closePipButton" title="关闭小窗">关闭</button>
</div>
</div>
<script>
const mainVideo = document.getElementById('mainVideo');
const pipVideo = document.getElementById('pipVideo');
const pipButton = document.getElementById('pipButton');
const fullscreenButton = document.getElementById('fullscreenButton');
const closePipButton = document.getElementById('closePipButton');
const pipContainer = document.getElementById('pipContainer');
const statusText = document.getElementById('statusText');
const pipStatus = document.getElementById('pipStatus');
const isPipSupported = 'documentPictureInPicture' in window;
function init() {
updateStatus(isPipSupported ? "Document Picture-in-Picture API 可用" : "您的浏览器不支持 Document Picture-in-Picture API");
pipButton.disabled = !isPipSupported;
pipButton.addEventListener('click', togglePictureInPicture);
fullscreenButton.(, toggleFullscreen);
closePipButton.(, closePictureInPicture);
pipVideo. = mainVideo.;
mainVideo. = ;
pipVideo. = ;
}
() {
statusText. = message;
}
() {
pipStatus. = message;
}
() {
(!isPipSupported) ;
(..) {
();
;
}
{
pipWindow = ..({
: ,
: ,
});
pipWindow.. = ;
style = .();
style. = ;
pipWindow...(style);
pipWindow...(pipVideo);
pipVideo. = mainVideo.;
(!mainVideo.) {
pipVideo.();
} {
pipVideo.();
}
pipWindow.(, {
pipContainer.(pipVideo);
pipContainer.. = ;
();
});
pipContainer.. = ;
();
();
mainVideo.(, syncVideoTime);
pipVideo.(, syncVideoTime);
mainVideo.(, pipVideo.());
mainVideo.(, pipVideo.());
pipVideo.(, mainVideo.());
pipVideo.(, mainVideo.());
mainVideo.(, syncVolume);
pipVideo.(, syncVolume);
} (error) {
();
.(error);
}
}
() {
(.(mainVideo. - pipVideo.) > ) {
( === mainVideo) {
pipVideo. = mainVideo.;
} {
mainVideo. = pipVideo.;
}
}
}
() {
( === mainVideo) {
pipVideo. = mainVideo.;
pipVideo. = mainVideo.;
} {
mainVideo. = pipVideo.;
mainVideo. = pipVideo.;
}
}
() {
(..) {
...();
}
mainVideo.(, syncVideoTime);
pipVideo.(, syncVideoTime);
mainVideo.(, syncVolume);
pipVideo.(, syncVolume);
pipContainer.(pipVideo);
pipContainer.. = ;
();
();
}
() {
(!.) {
(mainVideo.) {
mainVideo.();
} (mainVideo.) {
mainVideo.();
} (mainVideo.) {
mainVideo.();
}
} {
(.) {
.();
} (.) {
.();
} (.) {
.();
}
}
}
.(, init);
</script>
</body>
</html>