优化ios端全屏模式

This commit is contained in:
mtvpls
2025-12-16 20:19:26 +08:00
parent e88a572db0
commit e9a7f43c6f

View File

@@ -3244,38 +3244,325 @@ function PlayPageClient() {
handleNextEpisode();
},
},
// iOS 设备上添加自定义网页全屏按钮(竖屏显示,横屏时隐藏
// iOS 设备上添加自定义全屏按钮(横屏和竖屏显示)
...(isIOS ? [{
position: 'right',
index: 100, // 大数字确保在设置按钮右边
html: '<i class="art-icon ios-portrait-fullscreen"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" fill="currentColor"/></svg></i>',
tooltip: '网页全屏',
tooltip: '全屏',
style: {
color: '#fff',
},
mounted: function($el: HTMLElement) {
// 添加 CSS 样式:横屏时隐藏,竖屏显示
// 添加 CSS 样式:横屏竖屏显示
const style = document.createElement('style');
style.textContent = `
/* 横屏时隐藏 iOS 自定义全屏按钮 */
@media (orientation: landscape) {
.ios-portrait-fullscreen {
display: none !important;
}
/* iOS 自定义全屏按钮在所有方向都显示 */
.ios-portrait-fullscreen {
display: inline-flex !important;
}
/* 竖屏时显示 iOS 自定义全屏按钮 */
@media (orientation: portrait) {
.ios-portrait-fullscreen {
display: inline-flex !important;
}
/* iOS 全屏选择对话框样式(遵循项目统一风格) */
.ios-fullscreen-dialog {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
}
.ios-fullscreen-dialog-content {
background: white;
border-radius: 16px;
max-width: 480px;
width: 100%;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.dark .ios-fullscreen-dialog-content {
background: rgb(31, 41, 55);
}
/* 标题栏 */
.ios-fullscreen-dialog-header {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
padding: 20px 24px;
}
.ios-fullscreen-dialog-title {
font-size: 20px;
font-weight: 700;
color: white;
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 6px;
}
.ios-fullscreen-dialog-title svg {
stroke: white;
}
.ios-fullscreen-dialog-subtitle {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
margin: 0;
}
/* 选项列表 */
.ios-fullscreen-dialog-options {
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.ios-fullscreen-option {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
background: rgb(249, 250, 251);
border: 2px solid transparent;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
.dark .ios-fullscreen-option {
background: rgba(55, 65, 81, 0.5);
}
.ios-fullscreen-option:hover {
background: rgb(243, 244, 246);
border-color: #22c55e;
box-shadow: 0 4px 12px rgba(34, 197, 94, 0.15);
}
.dark .ios-fullscreen-option:hover {
background: rgb(55, 65, 81);
}
.ios-fullscreen-option:active {
transform: scale(0.98);
}
/* 推荐选项 */
.ios-fullscreen-option-recommended {
border-color: #22c55e;
}
/* 选项图标 */
.ios-fullscreen-option-icon {
flex-shrink: 0;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: white;
border-radius: 10px;
color: #22c55e;
}
.dark .ios-fullscreen-option-icon {
background: rgb(31, 41, 55);
}
.ios-fullscreen-option-recommended .ios-fullscreen-option-icon {
background: #22c55e;
color: white;
}
/* 选项内容 */
.ios-fullscreen-option-content {
flex: 1;
}
.ios-fullscreen-option-title {
font-size: 16px;
font-weight: 600;
color: rgb(17, 24, 39);
margin-bottom: 4px;
display: flex;
align-items: center;
gap: 8px;
}
.dark .ios-fullscreen-option-title {
color: white;
}
.ios-fullscreen-option-badge {
display: inline-block;
padding: 2px 8px;
background: #22c55e;
color: white;
font-size: 12px;
font-weight: 500;
border-radius: 4px;
}
.ios-fullscreen-option-desc {
font-size: 13px;
color: rgb(107, 114, 128);
line-height: 1.4;
}
.dark .ios-fullscreen-option-desc {
color: rgb(156, 163, 175);
}
/* 箭头图标 */
.ios-fullscreen-option-arrow {
flex-shrink: 0;
color: rgb(209, 213, 219);
transition: transform 0.2s;
}
.dark .ios-fullscreen-option-arrow {
color: rgb(75, 85, 99);
}
.ios-fullscreen-option:hover .ios-fullscreen-option-arrow {
transform: translateX(4px);
color: #22c55e;
}
/* 底部提示 */
.ios-fullscreen-dialog-footer {
padding: 16px 24px;
background: rgb(249, 250, 251);
border-top: 1px solid rgb(229, 231, 235);
display: flex;
align-items: flex-start;
gap: 10px;
font-size: 12px;
color: rgb(107, 114, 128);
line-height: 1.5;
}
.dark .ios-fullscreen-dialog-footer {
background: rgba(17, 24, 39, 0.5);
border-top-color: rgb(55, 65, 81);
color: rgb(156, 163, 175);
}
.ios-fullscreen-dialog-footer svg {
flex-shrink: 0;
margin-top: 2px;
stroke: currentColor;
}
`;
document.head.appendChild(style);
},
click: function () {
if (artPlayerRef.current) {
artPlayerRef.current.fullscreenWeb = !artPlayerRef.current.fullscreenWeb;
if (!artPlayerRef.current) return;
// 如果已经在全屏状态,直接退出
if (artPlayerRef.current.fullscreenWeb) {
artPlayerRef.current.fullscreenWeb = false;
return;
}
// 创建对话框(使用项目统一风格)
const dialog = document.createElement('div');
dialog.className = 'ios-fullscreen-dialog';
dialog.innerHTML = `
<div class="ios-fullscreen-dialog-content">
<!-- 标题栏 -->
<div class="ios-fullscreen-dialog-header">
<h3 class="ios-fullscreen-dialog-title">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" stroke="currentColor" stroke-width="2" fill="none"/>
</svg>
选择全屏模式
</h3>
<p class="ios-fullscreen-dialog-subtitle">
由于 iOS 系统限制,原生全屏会使用系统播放器,将无法显示弹幕及使用部分播放器功能。网页全屏可能无法完全占满屏幕,但可保留所有功能。
</p>
</div>
<!-- 选项列表 -->
<div class="ios-fullscreen-dialog-options">
<!-- 网页全屏选项 -->
<button class="ios-fullscreen-option ios-fullscreen-option-recommended" data-action="web">
<div class="ios-fullscreen-option-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2"/>
<path d="M7 10h2v7H7zm4-3h2v10h-2zm4 6h2v4h-2z" fill="currentColor"/>
</svg>
</div>
<div class="ios-fullscreen-option-content">
<div class="ios-fullscreen-option-title">
网页全屏
<span class="ios-fullscreen-option-badge">推荐</span>
</div>
<div class="ios-fullscreen-option-desc">
保留弹幕、控制栏等所有功能
</div>
</div>
<svg class="ios-fullscreen-option-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M9 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
<!-- 原生全屏选项 -->
<button class="ios-fullscreen-option" data-action="native">
<div class="ios-fullscreen-option-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" stroke="currentColor" stroke-width="2"/>
</svg>
</div>
<div class="ios-fullscreen-option-content">
<div class="ios-fullscreen-option-title">
原生全屏
</div>
<div class="ios-fullscreen-option-desc">
使用系统播放器,部分功能不可用
</div>
</div>
<svg class="ios-fullscreen-option-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M9 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
</div>
<!-- 底部提示 -->
<div class="ios-fullscreen-dialog-footer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
<path d="M12 16v-4m0-4h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
<span>将网站添加到主屏幕PWA网页全屏可以完全全屏</span>
</div>
</div>
`;
// 添加到页面
document.body.appendChild(dialog);
// 点击背景关闭
dialog.addEventListener('click', (e) => {
if (e.target === dialog) {
document.body.removeChild(dialog);
}
});
// 按钮点击事件
const buttons = dialog.querySelectorAll('.ios-fullscreen-option');
buttons.forEach(button => {
button.addEventListener('click', () => {
const action = button.getAttribute('data-action');
if (action === 'web') {
// 网页全屏
if (artPlayerRef.current) {
artPlayerRef.current.fullscreenWeb = true;
}
} else if (action === 'native') {
// 原生全屏(尝试使用浏览器的全屏 API
if (artPlayerRef.current && artPlayerRef.current.template.$video) {
const videoElement = artPlayerRef.current.template.$video;
if (videoElement.requestFullscreen) {
videoElement.requestFullscreen();
} else if ((videoElement as any).webkitEnterFullscreen) {
(videoElement as any).webkitEnterFullscreen();
}
}
}
// 关闭对话框
document.body.removeChild(dialog);
});
});
},
}] : []),
],
@@ -3289,31 +3576,45 @@ function PlayPageClient() {
setPlayerReady(true);
console.log('[PlayPage] Player ready, triggering sync setup');
// iOS 全屏拦截:强制使用 Web 全屏而不是原生全屏
// iOS 设备:监听屏幕方向变化,自动调整全屏状态
if (isIOS && artPlayerRef.current) {
console.log('[iOS] 设置全屏拦截,强制使用 Web 全屏');
const handleOrientationChange = () => {
if (!artPlayerRef.current) return;
// 保存原始的 fullscreen 属性描述符
const player = artPlayerRef.current;
const originalFullscreenDescriptor = Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(player),
'fullscreen'
);
// 获取当前屏幕方向
const isLandscape = window.matchMedia('(orientation: landscape)').matches;
const isPortrait = window.matchMedia('(orientation: portrait)').matches;
// 覆盖 fullscreen 属性,将其重定向到 fullscreenWeb
Object.defineProperty(player, 'fullscreen', {
get() {
return player.fullscreenWeb;
},
set(value: boolean) {
console.log('[iOS] 拦截 fullscreen 设置,重定向到 fullscreenWeb:', value);
player.fullscreenWeb = value;
},
configurable: true,
enumerable: true,
console.log('[iOS] 屏幕方向变化:', {
isLandscape,
isPortrait,
fullscreenWeb: artPlayerRef.current.fullscreenWeb
});
// 如果在网页全屏状态下旋转到横屏,切换到正常全屏
if (artPlayerRef.current.fullscreenWeb && isLandscape) {
console.log('[iOS] 横屏模式:从网页全屏切换到正常全屏');
// 先退出网页全屏
artPlayerRef.current.fullscreenWeb = false;
// 延迟一下再进入正常全屏,确保布局已更新
setTimeout(() => {
if (artPlayerRef.current) {
artPlayerRef.current.fullscreenWeb = true;
}
}, 100);
}
};
// 监听屏幕方向变化
window.addEventListener('orientationchange', handleOrientationChange);
// 也监听 resize 事件(某些设备上更可靠)
window.addEventListener('resize', handleOrientationChange);
// 清理函数
artPlayerRef.current.on('destroy', () => {
window.removeEventListener('orientationchange', handleOrientationChange);
window.removeEventListener('resize', handleOrientationChange);
});
console.log('[iOS] 已覆盖 fullscreen 属性,所有全屏操作将使用 Web 全屏');
}
// 从 art.storage 读取弹幕设置并应用