返回

多页面PDF渲染:优雅提升前端页面体验,告别性能瓶颈

见解分享

在现代网络应用中,高效地渲染和显示PDF文档至关重要。虽然我们已经掌握了在前端页面中优雅地呈现单页PDF的艺术,但我们的追求远不止于此。单页视图只能满足基本需求,却无法提供流畅且全面的用户体验。要实现多页面视图,我们需要突破单一画布的束缚,并解决性能瓶颈。

多页面视图:无缝滚动,逐页探索

多页面视图允许用户无缝滚动浏览PDF文档,逐页探索内容。要实现这一目标,我们需要将PDF按页拆分,并根据用户的滚动行为动态渲染。但是,如果每一页都对应一个<canvas>标签,随着页面数量的增加,浏览器的性能将不堪重负。

虚拟渲染:巧妙之举,释放性能

虚拟渲染是一种巧妙的解决方案,可以避免同时渲染所有页面。它仅在需要时按需加载和渲染可见页面,从而节省内存和计算资源。虚拟渲染技术通过以下步骤实现:

  1. 创建一个<div><ul>容器来容纳PDF页面。
  2. 监听容器的滚动事件,确定当前可见的页面范围。
  3. 仅渲染当前可见范围内的页面,使用<canvas>标签。
  4. 页面滚动超出容器后,将其从DOM中移除,并在需要时重新利用。
// 创建容器
const container = document.createElement('div');
container.classList.add('pdf-container');

// 监听滚动事件
container.addEventListener('scroll', handleScroll);

// 滚动事件处理函数
function handleScroll(e) {
  // 计算当前可见的页面范围
  const visibleRange = getVisibleRange(container);

  // 渲染可见页面
  renderPages(visibleRange);
}

// 获取可见页面范围
function getVisibleRange(container) {
  const scrollTop = container.scrollTop;
  const scrollHeight = container.scrollHeight;
  const clientHeight = container.clientHeight;

  const startIndex = Math.floor(scrollTop / clientHeight);
  const endIndex = startIndex + Math.ceil(scrollHeight / clientHeight);

  return { startIndex, endIndex };
}

// 渲染页面
function renderPages(range) {
  // 获取要渲染的页面
  const pages = getPages(range);

  // 移除超出范围的页面
  removePages(pages);

  // 添加可见页面
  addPages(pages);
}

// 获取要渲染的页面
function getPages(range) {
  const pages = [];
  for (let i = range.startIndex; i <= range.endIndex; i++) {
    // 根据页码获取页面
    const page = getPage(i);
    pages.push(page);
  }

  return pages;
}

// 移除超出范围的页面
function removePages(pages) {
  const existingPages = container.querySelectorAll('.pdf-page');
  existingPages.forEach((page) => {
    if (!pages.includes(page)) {
      page.remove();
    }
  });
}

// 添加可见页面
function addPages(pages) {
  pages.forEach((page) => {
    container.appendChild(page);
  });
}

工具栏增强:便捷导航,提升体验

为了进一步提升用户体验,我们在PDF查看器中添加了一个工具栏。它提供了对页面缩放、旋转和下载等关键功能的便捷访问,使交互更加流畅。工具栏还包含一个页面导航控件,允许用户快速跳转到特定页面。

<div class="pdf-toolbar">
  <button class="btn-zoom-in">+</button>
  <button class="btn-zoom-out">-</button>
  <button class="btn-rotate-left"></button>
  <button class="btn-rotate-right"></button>
  <button class="btn-download"></button>
  <input type="number" class="input-page-number">
  <button class="btn-go-to-page">Go</button>
</div>
// 监听工具栏按钮点击事件
const toolbarButtons = document.querySelectorAll('.pdf-toolbar button');
toolbarButtons.forEach((button) => {
  button.addEventListener('click', handleToolbarClick);
});

// 工具栏按钮点击事件处理函数
function handleToolbarClick(e) {
  const button = e.target;
  switch (button.classList[0]) {
    case 'btn-zoom-in':
      zoomIn();
      break;
    case 'btn-zoom-out':
      zoomOut();
      break;
    case 'btn-rotate-left':
      rotateLeft();
      break;
    case 'btn-rotate-right':
      rotateRight();
      break;
    case 'btn-download':
      download();
      break;
    case 'btn-go-to-page':
      goToPage();
      break;
  }
}

// 缩放页面
function zoomIn() {
  // 获取当前缩放比例
  const scale = parseFloat(container.style.zoom) || 1;

  // 限制最大缩放比例
  if (scale < 3) {
    container.style.zoom = scale + 0.25;
  }
}

// 缩小页面
function zoomOut() {
  // 获取当前缩放比例
  const scale = parseFloat(container.style.zoom) || 1;

  // 限制最小缩放比例
  if (scale > 0.25) {
    container.style.zoom = scale - 0.25;
  }
}

// 左旋页面
function rotateLeft() {
  // 获取当前旋转角度
  const rotation = parseInt(container.style.transform) || 0;

  // 旋转 90 度
  container.style.transform = `rotate(${rotation - 90}deg)`;
}

// 右旋页面
function rotateRight() {
  // 获取当前旋转角度
  const rotation = parseInt(container.style.transform) || 0;

  // 旋转 90 度
  container.style.transform = `rotate(${rotation + 90}deg)`;
}

// 下载页面
function download() {
  // 创建一个新的下载链接
  const link = document.createElement('a');
  link.href = container.toDataURL();
  link.download = 'document.pdf';

  // 触发下载
  link.click();
}

// 跳转到特定页面
function goToPage() {
  // 获取要跳转的页面
  const pageNumber = parseInt(document.querySelector('.input-page-number').value) || 1;

  // 计算页面偏移量
  const pageOffset = (pageNumber - 1) * clientHeight;

  // 滚动到页面
  container.scrollTop = pageOffset;
}

结语:全面提升,打造卓越体验

通过实现多页面视图和添加工具栏,我们极大地增强了前端页面PDF渲染能力。虚拟渲染确保了流畅的滚动体验,而工具栏则提升了交互便利性。这些改进打造了一个全面的PDF查看解决方案,为用户提供卓越的使用体验。