9.3 全屏API

9.3 全屏API

全屏API允许Web开发者以编程方式控制元素的全屏显示,为视频播放、游戏、演示等场景提供沉浸式体验。本节将详细介绍全屏API的使用方法、兼容性处理和最佳实践。

基础API使用

1. 全屏模式切换

// 进入全屏模式
function enterFullscreen(element) {
  if (element.requestFullscreen) {
    return element.requestFullscreen();
  } else if (element.webkitRequestFullscreen) { /* Safari */
    return element.webkitRequestFullscreen();
  } else if (element.msRequestFullscreen) { /* IE11 */
    return element.msRequestFullscreen();
  }
  return Promise.reject('Fullscreen API is not supported');
}

// 退出全屏模式
function exitFullscreen() {
  if (document.exitFullscreen) {
    return document.exitFullscreen();
  } else if (document.webkitExitFullscreen) { /* Safari */
    return document.webkitExitFullscreen();
  } else if (document.msExitFullscreen) { /* IE11 */
    return document.msExitFullscreen();
  }
  return Promise.reject('Fullscreen API is not supported');
}

// 切换全屏状态
function toggleFullscreen(element) {
  if (!document.fullscreenElement) {
    return enterFullscreen(element);
  } else {
    return exitFullscreen();
  }
}

// 使用示例
document.getElementById('fullscreen-btn').addEventListener('click', () => {
  toggleFullscreen(document.documentElement).catch(err => {
    console.error('全屏操作失败:', err);
  });
});

2. 全屏状态检测

// 获取当前全屏状态
function getFullscreenState() {
  return {
    isFullscreen: !!(
      document.fullscreenElement ||
      document.webkitFullscreenElement ||
      document.msFullscreenElement
    ),
    element: (
      document.fullscreenElement ||
      document.webkitFullscreenElement ||
      document.msFullscreenElement
    )
  };
}

// 监听全屏变化事件
function setupFullscreenListeners() {
  const events = [
    'fullscreenchange',
    'webkitfullscreenchange',
    'MSFullscreenChange'
  ];
  
  events.forEach(event => {
    document.addEventListener(event, () => {
      const state = getFullscreenState();
      console.log('全屏状态变化:', state.isFullscreen);
      updateUIForFullscreen(state.isFullscreen);
    });
  });
}

// 初始化
setupFullscreenListeners();

高级功能实现

1. 全屏样式定制

/* 全屏模式专用样式 */
:fullscreen, ::backdrop {
  background-color: #000;
}

/* 浏览器前缀兼容 */
:-webkit-full-screen, :-webkit-full-screen-ancestor {
  background-color: #000;
}

:-moz-full-screen, :-moz-full-screen-ancestor {
  background-color: #000;
}

:-ms-fullscreen, :-ms-fullscreen-ancestor {
  background-color: #000;
}

/* 全屏时的自定义样式 */
#content:fullscreen {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* 背景幕布样式 */
::backdrop {
  background: linear-gradient(135deg, #1e3c72, #2a5298);
}

2. 全屏提示与限制处理

class FullscreenManager {
  constructor() {
    this.supported = this.checkSupport();
    this.setupEventListeners();
  }
  
  checkSupport() {
    return !!(
      document.fullscreenEnabled ||
      document.webkitFullscreenEnabled ||
      document.msFullscreenEnabled
    );
  }
  
  setupEventListeners() {
    document.addEventListener('fullscreenerror', (e) => {
      console.error('全屏错误:', e);
      this.showErrorMessage('无法进入全屏模式,请检查浏览器设置');
    });
    
    document.addEventListener('click', (e) => {
      if (e.target.classList.contains('fullscreen-toggle')) {
        this.handleFullscreenRequest(e.target);
      }
    }, true);
  }
  
  async handleFullscreenRequest(button) {
    if (!this.supported) {
      this.showErrorMessage('您的浏览器不支持全屏功能');
      return;
    }
    
    try {
      const targetElement = button.dataset.target 
        ? document.querySelector(button.dataset.target)
        : document.documentElement;
      
      if (!targetElement) throw new Error('目标元素不存在');
      
      const state = this.getState();
      if (state.isFullscreen) {
        await this.exit();
        button.textContent = '进入全屏';
      } else {
        await this.enter(targetElement);
        button.textContent = '退出全屏';
      }
    } catch (error) {
      console.error('全屏操作失败:', error);
      this.showErrorMessage(error.message);
    }
  }
  
  getState() {
    return {
      isFullscreen: !!(
        document.fullscreenElement ||
        document.webkitFullscreenElement ||
        document.msFullscreenElement
      ),
      element: (
        document.fullscreenElement ||
        document.webkitFullscreenElement ||
        document.msFullscreenElement
      )
    };
  }
  
  enter(element) {
    if (element.requestFullscreen) {
      return element.requestFullscreen();
    } else if (element.webkitRequestFullscreen) {
      return element.webkitRequestFullscreen();
    } else if (element.msRequestFullscreen) {
      return element.msRequestFullscreen();
    }
    throw new Error('全屏API不可用');
  }
  
  exit() {
    if (document.exitFullscreen) {
      return document.exitFullscreen();
    } else if (document.webkitExitFullscreen) {
      return document.webkitExitFullscreen();
    } else if (document.msExitFullscreen) {
      return document.msExitFullscreen();
    }
    throw new Error('退出全屏API不可用');
  }
  
  showErrorMessage(msg) {
    const alert = document.createElement('div');
    alert.className = 'fullscreen-alert';
    alert.textContent = msg;
    document.body.appendChild(alert);
    setTimeout(() => alert.remove(), 3000);
  }
}

// 初始化
const fullscreenManager = new FullscreenManager();

兼容性处理方案

1. 前缀统一封装

// 统一前缀的API访问
const fullscreen = {
  request: function(element) {
    const methods = [
      'requestFullscreen',
      'webkitRequestFullscreen',
      'webkitRequestFullScreen', // 旧版Safari
      'msRequestFullscreen'
    ];
    
    for (let method of methods) {
      if (element[method]) {
        return element[method]();
      }
    }
    return Promise.reject('不支持全屏API');
  },
  
  exit: function() {
    const methods = [
      'exitFullscreen',
      'webkitExitFullscreen',
      'webkitCancelFullScreen', // 旧版Safari
      'msExitFullscreen'
    ];
    
    for (let method of methods) {
      if (document[method]) {
        return document[method]();
      }
    }
    return Promise.reject('不支持退出全屏API');
  },
  
  get enabled() {
    return !!(
      document.fullscreenEnabled ||
      document.webkitFullscreenEnabled ||
      document.msFullscreenEnabled
    );
  },
  
  get element() {
    return (
      document.fullscreenElement ||
      document.webkitFullscreenElement ||
      document.msFullscreenElement
    );
  },
  
  get isActive() {
    return !!this.element;
  },
  
  onChange: function(callback) {
    const events = [
      'fullscreenchange',
      'webkitfullscreenchange',
      'MSFullscreenChange'
    ];
    
    const handler = () => callback(this.isActive);
    
    events.forEach(event => {
      document.addEventListener(event, handler);
    });
    
    // 返回取消监听函数
    return () => {
      events.forEach(event => {
        document.removeEventListener(event, handler);
      });
    };
  }
};

// 使用示例
fullscreen.onChange(isFullscreen => {
  console.log('全屏状态变化:', isFullscreen);
});

document.getElementById('toggle-fs').addEventListener('click', () => {
  if (fullscreen.isActive) {
    fullscreen.exit();
  } else {
    fullscreen.request(document.getElementById('video'));
  }
});

2. 移动端特殊处理

// 检测移动设备
function isMobileDevice() {
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

// 移动端全屏处理
function setupMobileFullscreen() {
  if (!isMobileDevice()) return;
  
  // 禁用iOS默认全屏行为(视频元素)
  const videos = document.querySelectorAll('video');
  videos.forEach(video => {
    video.setAttribute('playsinline', '');
    video.setAttribute('webkit-playsinline', '');
  });
  
  // 添加手势控制
  let fsTimeout;
  document.addEventListener('dblclick', () => {
    clearTimeout(fsTimeout);
    fsTimeout = setTimeout(() => {
      toggleFullscreen(document.documentElement);
    }, 300);
  });
  
  // 处理键盘弹出
  window.addEventListener('resize', () => {
    if (window.innerHeight < document.documentElement.clientHeight * 0.8) {
      fullscreen.exit();
    }
  });
}

实际应用案例

1. 视频播放器全屏控制

class VideoPlayer {
  constructor(containerId) {
    this.container = document.getElementById(containerId);
    this.video = this.container.querySelector('video');
    this.controls = this.container.querySelector('.controls');
    this.setupEvents();
  }
  
  setupEvents() {
    // 全屏按钮
    const fsBtn = this.controls.querySelector('.fullscreen-btn');
    fsBtn.addEventListener('click', () => this.toggleFullscreen());
    
    // 全屏状态变化
    document.addEventListener('fullscreenchange', () => {
      this.updateControls();
    });
    
    // 键盘控制
    document.addEventListener('keydown', (e) => {
      if (!fullscreen.isActive) return;
      
      switch(e.key) {
        case 'Escape':
          this.exitFullscreen();
          break;
        case 'f':
          this.toggleFullscreen();
          break;
      }
    });
  }
  
  async toggleFullscreen() {
    try {
      if (fullscreen.isActive) {
        await fullscreen.exit();
      } else {
        await fullscreen.request(this.container);
        this.video.focus();
      }
    } catch (error) {
      console.error('全屏切换失败:', error);
    }
  }
  
  updateControls() {
    const fsBtn = this.controls.querySelector('.fullscreen-btn');
    if (fullscreen.isActive) {
      fsBtn.innerHTML = '<svg>...</svg> 退出全屏';
      this.container.classList.add('fullscreen-active');
    } else {
      fsBtn.innerHTML = '<svg>...</svg> 全屏';
      this.container.classList.remove('fullscreen-active');
    }
  }
  
  async exitFullscreen() {
    if (fullscreen.isActive) {
      await fullscreen.exit();
    }
  }
}

// 初始化播放器
const player = new VideoPlayer('video-player');

2. 游戏全屏优化

class GameEngine {
  constructor() {
    this.canvas = document.getElementById('game-canvas');
    this.ctx = this.canvas.getContext('2d');
    this.isFullscreen = false;
    this.setupFullscreen();
  }
  
  setupFullscreen() {
    // 自动调整画布尺寸
    const resizeCanvas = () => {
      if (this.isFullscreen) {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
      } else {
        this.canvas.width = 800;
        this.canvas.height = 600;
      }
      this.render();
    };
    
    // 监听全屏变化
    fullscreen.onChange(isFullscreen => {
      this.isFullscreen = isFullscreen;
      resizeCanvas();
    });
    
    // 初始调整
    resizeCanvas();
    
    // 双击切换全屏
    this.canvas.addEventListener('dblclick', () => {
      if (this.isFullscreen) {
        fullscreen.exit();
      } else {
        fullscreen.request(this.canvas);
      }
    });
  }
  
  render() {
    // 渲染逻辑...
    this.ctx.fillStyle = '#333';
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    
    // 显示当前模式
    this.ctx.fillStyle = '#fff';
    this.ctx.font = '24px Arial';
    this.ctx.fillText(
      this.isFullscreen ? '全屏模式(双击退出)' : '窗口模式(双击全屏)',
      20, 40
    );
  }
}

// 启动游戏
const game = new GameEngine();

安全与最佳实践

  1. 用户触发原则

    • 全屏请求必须由用户手势(点击、按键等)直接触发
    • 不能通过setTimeout或异步回调自动触发
  2. 视觉反馈

    • 全屏状态变化时提供清晰的UI反馈
    • 在全屏模式下提供明显的退出选项
  3. 键盘导航

    // 确保ESC可以退出全屏
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape' && fullscreen.isActive) {
        fullscreen.exit();
      }
    });
    
  4. 性能优化

    // 全屏时启动高性能渲染
    fullscreen.onChange(isFullscreen => {
      if (isFullscreen) {
        startHighPerformanceMode();
      } else {
        stopHighPerformanceMode();
      }
    });
    

全屏API为Web应用提供了强大的展示能力,开发时应注意:

  • 始终尊重用户控制权,避免强制全屏
  • 处理不同浏览器的前缀差异
  • 为移动设备提供特殊优化
  • 在全屏模式下保持必要的UI控制元素
  • 考虑无障碍访问需求

通过合理使用全屏API,可以显著提升媒体展示、游戏和交互应用的沉浸感和用户体验。

#前端开发 分享于 2025-05-20

【 内容由 AI 共享,不代表本站观点,请谨慎参考 】