pdfjs4

import React, { useEffect, useState, useRef } from 'react';
import * as pdfjsLib from 'pdfjs-dist';

pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js';

const PDFViewer = ({ url }) => {
  const [pdf, setPdf] = useState(null);
  const [currentPage, setCurrentPage] = useState(1);
  const [numPages, setNumPages] = useState(0);
  const [pageRendering, setPageRendering] = useState(false);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [zoom, setZoom] = useState(1);
  const canvasRef = useRef(null);
  const linkLayerRef = useRef(null);
  const containerRef = useRef(null);
  const touchStartRef = useRef({ x: 0, y: 0, distance: 0 });
  const lastTapRef = useRef(0);

  // ... addWatermark 和 setupLinkLayer 函数保持不变 ...

  // 处理移动端手势
  const handleTouchStart = (e) => {
    if (e.touches.length === 2) {
      // 双指缩放
      const distance = Math.hypot(
        e.touches[0].clientX - e.touches[1].clientX,
        e.touches[0].clientY - e.touches[1].clientY
      );
      touchStartRef.current = {
        x: (e.touches[0].clientX + e.touches[1].clientX) / 2,
        y: (e.touches[0].clientY + e.touches[1].clientY) / 2,
        distance
      };
    } else if (e.touches.length === 1) {
      // 单指点击
      const now = Date.now();
      if (now - lastTapRef.current < 300) {
        // 双击缩放
        e.preventDefault();
        handleDoubleTap(e.touches[0].clientX, e.touches[0].clientY);
      }
      lastTapRef.current = now;
    }
  };

  const handleTouchMove = (e) => {
    if (e.touches.length === 2) {
      e.preventDefault();
      const distance = Math.hypot(
        e.touches[0].clientX - e.touches[1].clientX,
        e.touches[0].clientY - e.touches[1].clientY
      );
      const scale = distance / touchStartRef.current.distance;
      const newZoom = Math.min(Math.max(zoom * scale, 0.5), 3.0);
      setZoom(newZoom);
    }
  };

  const handleDoubleTap = (x, y) => {
    if (zoom > 1) {
      setZoom(1); // 重置缩放
    } else {
      setZoom(2); // 放大到2倍
    }
  };

  // 监听屏幕方向变化
  useEffect(() => {
    const handleOrientationChange = () => {
      // 延迟执行以等待布局完成
      setTimeout(() => {
        if (containerRef.current) {
          renderPage(currentPage);
        }
      }, 100);
    };

    // 监听屏幕方向变化
    window.addEventListener('orientationchange', handleOrientationChange);
    
    // 监听视口大小变化(某些设备需要)
    const mediaQuery = window.matchMedia('(orientation: portrait)');
    mediaQuery.addListener(handleOrientationChange);

    return () => {
      window.removeEventListener('orientationchange', handleOrientationChange);
      mediaQuery.removeListener(handleOrientationChange);
    };
  }, [currentPage]);

  // 修改渲染页面函数
  const renderPage = async (pageNum) => {
    if (pageRendering || !pdf) return;

    setPageRendering(true);
    
    try {
      if (!pageCache.current.has(pageNum)) {
        const page = await pdf.getPage(pageNum);
        pageCache.current.set(pageNum, page);
      }
      
      const page = pageCache.current.get(pageNum);
      const canvas = canvasRef.current;
      const ctx = canvas.getContext('2d');

      // 获取容器宽度(考虑移动端视口)
      const containerWidth = containerRef.current.clientWidth;
      const viewport = page.getViewport({ scale: 1 });
      
      // 计算缩放比例(考虑设备像素比)
      const devicePixelRatio = window.devicePixelRatio || 1;
      const baseScale = (containerWidth / viewport.width);
      const finalScale = baseScale * zoom * devicePixelRatio;

      // 设置canvas尺寸
      const scaledViewport = page.getViewport({ scale: finalScale });
      canvas.width = scaledViewport.width;
      canvas.height = scaledViewport.height;
      
      // 设置显示尺寸
      const displayWidth = containerWidth * zoom;
      const displayHeight = (containerWidth * viewport.height / viewport.width) * zoom;
      canvas.style.width = `${displayWidth}px`;
      canvas.style.height = `${displayHeight}px`;

      // 更新链接层
      if (linkLayerRef.current) {
        linkLayerRef.current.style.width = `${displayWidth}px`;
        linkLayerRef.current.style.height = `${displayHeight}px`;
      }

      // 渲染PDF
      const renderContext = {
        canvasContext: ctx,
        viewport: scaledViewport,
        enableWebGL: true,
      };

      await page.render(renderContext).promise;
      setupLinkLayer(page, scaledViewport);
      addWatermark(canvas, finalScale);
    } catch (error) {
      console.error('Error rendering page:', error);
      setError('页面渲染失败,请刷新重试');
    } finally {
      setPageRendering(false);
    }
  };

  // 返回的 JSX
  return (
    <div className="pdf-viewer">
      <div className="pdf-controls">
        <div className="navigation-controls">
          <button 
            onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
            disabled={currentPage <= 1 || pageRendering}
            className="control-button"
          >
            上一页
          </button>
          <span className="page-info">{`${currentPage} / ${numPages}`}</span>
          <button 
            onClick={() => setCurrentPage(prev => Math.min(prev + 1, numPages))}
            disabled={currentPage >= numPages || pageRendering}
            className="control-button"
          >
            下一页
          </button>
        </div>
      </div>
      <div 
        className="pdf-container" 
        ref={containerRef}
        onTouchStart={handleTouchStart}
        onTouchMove={handleTouchMove}
      >
        <div className="canvas-container">
          <canvas ref={canvasRef} className="pdf-canvas" />
          <div 
            ref={linkLayerRef}
            className="link-layer"
          />
        </div>
      </div>
    </div>
  );
};

export default PDFViewer;

07-28
PDF.js 是一个由 Mozilla 开发并维护的开源库,允许在浏览器中直接渲染和查看 PDF 文件,而无需依赖任何插件。它基于 HTML5 和 JavaScript 构建,适用于各种 Web 应用场景,包括文档预览、嵌入式 PDF 查看器等。以下将从使用指南、集成教程以及常见问题三个维度进行详细说明。 ### 3.1 使用指南 PDF.js 提供了基础的 PDF 渲染功能,同时也支持高级特性,如缩放、搜索、书签导航和注释功能。其核心模块包括 `pdf.js`(解析 PDF 文件)和 `pdf.worker.js`(处理渲染任务)。在实际应用中,可以通过 CDN 引入或本地部署的方式使用 PDF.js。例如,在 HTML 页面中引入 PDF.js 的基本方式如下: ```html <script src="https://unpkg.com/pdfjs-dist@3.4.120/build/pdf.min.js"></script> ``` 然后,通过 JavaScript 代码加载并渲染 PDF 文件: ```javascript const loadingTask = pdfjsLib.getDocument('example.pdf'); loadingTask.promise.then(pdf => { pdf.getPage(1).then(page => { const canvas = document.getElementById('pdfCanvas'); const context = canvas.getContext('2d'); const viewport = page.getViewport({ scale: 1.5 }); canvas.height = viewport.height; canvas.width = viewport.width; page.render({ canvasContext: context, viewport: viewport }); }); }); ``` ### 3.2 集成教程 在现代前端框架中,PDF.js 也可以与 React、Vue 等结合使用。以 React 为例,可以使用 `react-pdf` 这样的封装库来简化集成过程。它基于 PDF.js 提供了组件化的 API,便于在 React 项目中快速实现 PDF 查看功能。例如: ```bash npm install react-pdf ``` ```jsx import { Document, Page } from 'react-pdf'; function PDFViewer() { return ( <Document file="example.pdf"> <Page pageNumber={1} /> </Document> ); } ``` 此外,PDF.js 还支持自定义加载器、错误处理、分页控制等高级功能,开发者可以根据需求进行扩展[^2]。 ### 3.3 常见问题与解决方案 在实际使用 PDF.js 时,常见的问题包括: - **大文件加载缓慢**:可以通过启用 PDF.js 的流式加载(streaming)功能,或在服务端进行 PDF 分块处理来缓解性能问题。 - **跨浏览器兼容性**:尤其是在 Internet Explorer 中,PDF.js 的渲染效果可能不稳定。建议使用 polyfill 或限制在 IE 中仅提供基础 PDF 查看功能[^1]。 - **字体渲染异常**:某些 PDF 文件可能包含嵌入字体,若在浏览器中无法正确解析,可能导致文字显示异常。可以尝试在服务器端将 PDF 转换为图像,再通过 HTML 展示。 - **移动端适配问题**:为提升移动端体验,应启用响应式布局,并针对触摸操作优化缩放与翻页逻辑[^2]。 此外,PDF.js 提供了丰富的配置选项,如设置 worker 路径、启用文本层、控制缩放比例等,开发者应根据具体需求进行合理配置[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值