当前位置: 首页 > web >正文

mobile预览

import { useEffect, useRef, useState } from 'react';
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf';
import 'pdfjs-dist/web/pdf_viewer.css';

// 配置 worker
pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js'; // 注意这里路径!

type Props = {
  fileData: ArrayBuffer; // 后端返回的文件流
};

export default function LazyPdfViewer({ fileData }: Props) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [pdf, setPdf] = useState<pdfjsLib.PDFDocumentProxy | null>(null);
  const observerRef = useRef<IntersectionObserver | null>(null);

  useEffect(() => {
    const loadingTask = pdfjsLib.getDocument({ data: fileData });
    loadingTask.promise.then(setPdf);
  }, [fileData]);

  useEffect(() => {
    if (!pdf || !containerRef.current) return;

    const container = containerRef.current;

    const renderPage = async (pageDiv: HTMLDivElement, pageNumber: number) => {
      if (pageDiv.dataset.rendered) return; // 防止重复渲染
      pageDiv.dataset.rendered = 'true';

      const page = await pdf.getPage(pageNumber);

      const scale = window.innerWidth < 768 ? 1.2 : 1.5; // 手机小一点
      const viewport = page.getViewport({ scale });

      // 创建canvas
      const canvas = document.createElement('canvas');
      canvas.width = viewport.width;
      canvas.height = viewport.height;
      canvas.style.width = `${viewport.width}px`;
      canvas.style.height = `${viewport.height}px`;
      const context = canvas.getContext('2d')!;

      // 渲染PDF页到canvas
      await page.render({ canvasContext: context, viewport }).promise;

      // 渲染超链接
      const annotationLayerDiv = document.createElement('div');
      annotationLayerDiv.className = 'annotationLayer';
      const annotations = await page.getAnnotations();
      pdfjsLib.AnnotationLayer.render({
        viewport,
        div: annotationLayerDiv,
        annotations,
        page,
        linkService: new pdfjsLib.SimpleLinkService(),
        renderInteractiveForms: true,
      });

      pageDiv.appendChild(canvas);
      pageDiv.appendChild(annotationLayerDiv);
    };

    const unrenderPage = (pageDiv: HTMLDivElement) => {
      pageDiv.innerHTML = '';
      pageDiv.dataset.rendered = '';
    };

    observerRef.current?.disconnect();

    observerRef.current = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          const pageDiv = entry.target as HTMLDivElement;
          const pageNumber = Number(pageDiv.dataset.pageNumber);
          if (entry.isIntersecting) {
            renderPage(pageDiv, pageNumber);
          } else {
            unrenderPage(pageDiv); // 不可见了卸载
          }
        });
      },
      {
        root: container,
        rootMargin: '200px 0px', // 提前加载
        threshold: 0.1,
      }
    );

    // 创建每页的div
    container.innerHTML = '';
    for (let i = 1; i <= pdf.numPages; i++) {
      const pageDiv = document.createElement('div');
      pageDiv.className = 'pdf-page';
      pageDiv.dataset.pageNumber = String(i);
      pageDiv.style.minHeight = '100vh'; // 初始撑开
      container.appendChild(pageDiv);
      observerRef.current.observe(pageDiv);
    }
  }, [pdf]);

  return (
    <div
      ref={containerRef}
      style={{
        overflowY: 'auto',
        height: '100vh',
        position: 'relative',
      }}
    />
  );
}
 

.pdf-page {
  position: relative;
  margin: 20px 0;
}
.annotationLayer {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: auto;
}
canvas {
  display: block;
  margin: 0 auto;
}
 

import LazyPdfViewer from './LazyPdfViewer';
import { useState, useEffect } from 'react';

export default function App() {
  const [pdfData, setPdfData] = useState<ArrayBuffer | null>(null);

  useEffect(() => {
    // 模拟后端取文件流
    fetch('/your-pdf-url.pdf')
      .then((res) => res.arrayBuffer())
      .then(setPdfData);
  }, []);

  return (
    <div>
      {pdfData ? <LazyPdfViewer fileData={pdfData} /> : <p>Loading...</p>}
    </div>
  );
}
 

http://www.xdnf.cn/news/6758.html

相关文章:

  • 初识仓颉编程语言:高效、简洁与创新的编程选择
  • Unity3D 游戏编程内存优化技巧
  • 在MYSQL中导入cookbook.sql文件
  • Java线程池(Thread Pool)性能优化解析
  • 基于摩尔信使MThings的Modbus协议转换效率优化实践
  • 原生小程序+springboot+vue+协同过滤算法的音乐推荐系统(源码+论文+讲解+安装+部署+调试)
  • 报表控件stimulsoft教程:如何在报表和仪表板中创建热图
  • 兰亭妙微设计:为生命科技赋予人性化的交互语言
  • 相机Camera日志分析之九:高通相机Camx 基于预览1帧的ConfigureStreams二级日志分析详解
  • Python OOP核心技巧:如何正确选择实例方法、类方法和静态方法
  • Redis(三) - 使用Java操作Redis详解
  • 非国产算力DeepSeek 部署中的常见问题及解决方案
  • git 修改一个老commit,再把修改应用到所有后续的 commit
  • Go的单测gomock及覆盖率命令
  • 洛谷 P1375:小猫 ← 预处理模逆元 + 卡特兰数
  • nacos配置文件快速部署另一种方法
  • 第十天——贪心算法——深度总结
  • 提高表达能力
  • FC7300 DMA MCAL 配置引导
  • idea 中引入python
  • 无人设备遥控器的信号传输与抗干扰技术
  • 动态图标切换的艺术
  • 软件架构风格系列(1):分层架构如何让系统“稳如泰山”?
  • AI 笔记 -基于retinaface的FPN上采样替换为CARAFE
  • Android framework 中间件开发(一)
  • 149.WEB渗透测试-MySQL基础(四)
  • 【暗光图像增强】【基于CNN的方法】2020-AAAI-EEMEFN
  • 显性知识的主要特征
  • math.js 加/减/乘/除 使用
  • 第九天——贪心算法——非递减数组