vite:npm 安装 pdfjs-dist , PDF.js View 预览功能示例

news2025/5/11 2:46:29

pdfjs-dist 是 Mozilla 的 PDF.js 库的预构建版本,能让你在项目里展示 PDF 文件。下面为你介绍如何用 npm 安装 pdfjs-dist 并应用 pdf.js 和 pdf.worker.js

为了方便,我将使用 vite 搭建一个原生 js 项目。

1.创建项目
npm create vite@latest pdf-view
选:Vanilla
选:JavaScript

2.初始化项目
 cd pdf-view
 cnpm install

3.安装 pdfjs-dist
 cnpm install pdfjs-dist@3.11.174 --save

编写 index1.html  如下

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>PDF.js View 示例</title>
  <link rel="stylesheet" href="/pdf.css" />
</head>
<body>
  <div class="container">
    <input type="file" id="fileInput" accept=".pdf">
    
    <div class="controls">
      <button id="prevPage">← 上一页</button>
      
      <div class="page-control">
        <input type="number" id="pageInput" min="1" step="1" disabled>
        <button id="goButton" disabled>跳转</button>
        <span>/ <span id="totalPages">-</span></span>
      </div>
      
      <button id="nextPage">下一页 →</button>
      
      <div class="scale-control">
        <button id="zoomOut">-</button>
        <span id="scaleValue">100%</span>
        <button id="zoomIn">+</button>
      </div>
    </div>

    <div id="canvasContainer">
      <canvas id="pdfCanvas"></canvas>
    </div>
  </div>

  <script type="module" src="/pdf-view.js"></script>
</body>
</html>

 编写 pdf.css  如下

    .container {
      max-width: 1000px;
      margin: 10px auto;
      padding: 15px;
    }

    .controls {
      display: flex;
      gap: 10px;
      align-items: center;
      margin-bottom: 15px;
    }

    #canvasContainer {
      border: 1px solid #ddd;
      overflow: auto;
      max-height: 80vh;
      position: relative;
    }

    canvas {
      transition: transform 0.2s ease;
    }

    input[type="number"] {
      width: 60px;
      padding: 4px;
      text-align: center;
    }

    button {
      padding: 6px 12px;
      background: #007bff;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }

    button:disabled {
      background: #6c757d;
      cursor: not-allowed;
    }

    .scale-control {
      margin-left: auto;
      display: flex;
      align-items: center;
      gap: 5px;
    }

编写 pdf-view.js  如下

import * as pdfjsLib from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.js';

// 初始化配置
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;

// 状态管理对象
const state = {
  currentPage: 1,
  totalPages: 0,
  scale: 1.0,
  pdfDoc: null,
  isRendering: false,
  MIN_SCALE: 0.5,
  MAX_SCALE: 3.0,
  ZOOM_STEP: 0.1
};

// DOM 元素引用
const dom = {
  canvas: document.getElementById('pdfCanvas'),
  container: document.getElementById('canvasContainer'),
  prevBtn: document.getElementById('prevPage'),
  nextBtn: document.getElementById('nextPage'),
  pageInput: document.getElementById('pageInput'),
  goBtn: document.getElementById('goButton'),
  totalPages: document.getElementById('totalPages'),
  zoomIn: document.getElementById('zoomIn'),
  zoomOut: document.getElementById('zoomOut'),
  scaleValue: document.getElementById('scaleValue'),
  fileInput: document.getElementById('fileInput')
};

// 初始化事件监听
function initEventListeners() {
  // 文件选择
  dom.fileInput.addEventListener('change', handleFileSelect);

  // 翻页控制
  dom.prevBtn.addEventListener('click', () => changePage(-1));
  dom.nextBtn.addEventListener('click', () => changePage(1));

  // 页面跳转
  dom.goBtn.addEventListener('click', handlePageJump);
  dom.pageInput.addEventListener('keypress', e => {
    if (e.key === 'Enter') handlePageJump();
  });

  // 缩放控制
  dom.zoomIn.addEventListener('click', () => changeScale(state.ZOOM_STEP));
  dom.zoomOut.addEventListener('click', () => changeScale(-state.ZOOM_STEP));

  // 鼠标滚轮缩放
  dom.container.addEventListener('wheel', handleWheelZoom, { passive: false });
}

// 处理文件选择
async function handleFileSelect(e) {
  const file = e.target.files[0];
  if (!file) return;

  try {
    const arrayBuffer = await file.arrayBuffer();
    state.pdfDoc = await pdfjsLib.getDocument(arrayBuffer).promise;
    state.totalPages = state.pdfDoc.numPages;
    
    initUI();
    await renderPage();
  } catch (err) {
    console.error('文件加载失败:', err);
  }
}

// 初始化界面状态
function initUI() {
  dom.totalPages.textContent = state.totalPages;
  dom.pageInput.max = state.totalPages;
  dom.pageInput.value = 1;
  dom.pageInput.disabled = false;
  dom.goBtn.disabled = false;
  dom.zoomIn.disabled = false;
  dom.zoomOut.disabled = false;
}

// 核心渲染函数
async function renderPage() {
  if (!state.pdfDoc || state.isRendering) return;
  state.isRendering = true;

  try {
    const page = await state.pdfDoc.getPage(state.currentPage);
    const viewport = page.getViewport({ scale: state.scale });

    // 调整画布尺寸
    dom.canvas.width = viewport.width;
    dom.canvas.height = viewport.height;

    // 渲染页面
    await page.render({
      canvasContext: dom.canvas.getContext('2d'),
      viewport: viewport
    }).promise;

    updateUIState();
  } catch (err) {
    console.error('页面渲染失败:', err);
  } finally {
    state.isRendering = false;
  }
}

// 更新界面状态
function updateUIState() {
  dom.pageInput.value = state.currentPage;
  dom.totalPages.textContent = state.totalPages;
  dom.scaleValue.textContent = `${Math.round(state.scale * 100)}%`;
  
  // 按钮状态
  dom.prevBtn.disabled = state.currentPage <= 1;
  dom.nextBtn.disabled = state.currentPage >= state.totalPages;
  dom.zoomIn.disabled = state.scale >= state.MAX_SCALE;
  dom.zoomOut.disabled = state.scale <= state.MIN_SCALE;
}

// 翻页处理
function changePage(offset) {
  const newPage = state.currentPage + offset;
  if (newPage < 1 || newPage > state.totalPages) return;
  
  state.currentPage = newPage;
  renderPage();
}

// 页面跳转处理
function handlePageJump() {
  const target = parseInt(dom.pageInput.value) || 1;
  const validPage = Math.max(1, Math.min(target, state.totalPages));
  
  if (validPage !== state.currentPage) {
    state.currentPage = validPage;
    renderPage();
  }
}

// 缩放处理
function changeScale(delta) {
  state.scale = parseFloat(
    Math.max(state.MIN_SCALE, 
           Math.min(state.scale + delta, state.MAX_SCALE))
           .toFixed(1)
  );
  renderPage();
}

// 鼠标滚轮缩放处理
function handleWheelZoom(e) {
  if (!e.ctrlKey) return;
  e.preventDefault();
  
  const delta = e.deltaY > 0 ? -state.ZOOM_STEP : state.ZOOM_STEP;
  changeScale(delta);
}

// 启动应用
initEventListeners();

代码解释

  1. 导入 pdfjsLib 和 pdfjsWorker:借助 ES6 模块语法导入 pdfjs-dist 库和 pdf.worker.js
  2. 设置 worker 源:把 pdfjsLib.GlobalWorkerOptions.workerSrc 设为 pdfjsWorker,从而让 PDF.js 能够正确使用 Web Worker。
  3. 加载 PDF 文件:利用 pdfjsLib.getDocument 方法加载指定的 PDF 文件。
  4. 渲染第一页:获取 PDF 的第一页,创建一个 canvas 元素,然后把页面渲染到 canvas 上。
  5. 错误处理:使用 .catch 方法捕获并处理加载 PDF 文件时可能出现的错误。

运行 npm run dev
  VITE v6.3.5  ready in 1066 ms

访问  http://localhost:5173/index1.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2372799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【开源版】likeshop上门家政系统PHP版全开源+uniapp前端

一.系统介绍 likeshop_上门家政系统&#xff0c;PHP版本更新至2.1.1最新版&#xff0c;全开源&#xff0c;适用于上门家政场景&#xff0c;系统拥有用户端、师傅端、无论运营还是二开都是性价比极高的100%开源家政系统。 二.搭建环境-教程 系统环境&#xff1a;CentOS、 运行…

MySQL 8.0 OCP 英文题库解析(一)

Oracle 为庆祝 MySQL 30 周年&#xff0c;从 2025.04.20 ~ 2025.07.31 之间&#xff0c;所有人均可以免费考取 MySQL OCP 认证。从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证&#xff0c;省1700多RMB&…

路由器断流排查终极指南:从Ping测试到Wireshark抓包5步定位法

测试路由器是否出现“断流”&#xff08;网络连接间歇性中断&#xff09;&#xff0c;需通过多维度排查硬件、软件及外部干扰因素。以下是详细步骤指南&#xff1a; 一、基础环境准备 设备连接 有线测试&#xff1a;用网线将电脑直接连接路由器LAN口&#xff0c;排除WiFi干扰。…

04 基于 STM32 的时钟展示程序

前言 我们经常会看到 各个场合下面有 基于数码管 的时钟程序 比如 在车站, 教室, 办公室 等等 各个场合都有 然后 这里就是做一个 简单的 时钟程序 展示程序 测试用例 每一秒钟更新时间, 然后 迭代更新 天, 时, 分 等等 然后 主流程 基于 天, 时分秒 渲染数码管 #incl…

n8n工作流自动化平台:生成图文并茂的分析报告之Merge节点详细说明

1.成果展示 1.1工作流示意图 1.2成果 数据都是造得 2Merge节点 2.1Mode 通过选择模式指定合并节点应如何组合来自不同数据流的数据 2.1.1Append 保存所有输入的数据。选择一个输入数量,逐一输出每个输入的项目。节点等待所有连接的输入的执行。 2.1.2Combine 2.1.2.1Co…

华为设备MSTP

一、MSTP核心理论 1. 基本概念 MSTP定义&#xff1a;MSTP&#xff08;Multiple Spanning Tree Protocol&#xff09;是一种基于实例的生成树协议&#xff0c;支持多个生成树实例&#xff08;MSTI&#xff09;&#xff0c;每个实例对应一组VLAN&#xff0c;实现不同VLAN流量的负…

Loly: 1靶场渗透

Loly: 1 来自 <Loly: 1 ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.23.241 3&#xff0c;对靶机进行端口服务探测 n…

Linux系统入门第十一章 --Shell编程之函数与数组

一、Shell函数 1、函数的用法 Shell函数可用于存放一系列的指令。在Shell脚本执行的过程中&#xff0c;函数被置于内存中&#xff0c;每次调用函数时不需要从硬盘读取&#xff0c;因此运行的速度比较快。在Shell编程中函数并非是必须的元素&#xff0c;但使用函数可以对程序进…

聊聊自动化办公未来趋势

1. 自动化办公未来趋势 1.1 智能化与AI融合加深 随着人工智能技术的不断成熟&#xff0c;其在自动化办公中的应用将更加广泛和深入。未来&#xff0c;办公软件将具备更强的智能交互能力&#xff0c;能够理解自然语言指令&#xff0c;自动完成复杂的任务&#xff0c;如文档编辑…

HarmonyOS学习——ArkTS语法介绍之基本知识

ArkTS是一种为构建高性能应用而设计的编程语言。ArkTS在继承TypeScript语法的基础上进行了优化&#xff0c;以提供更高的性能和开发效率。 目前流行的编程语言TypeScript是在JavaScript基础上通过添加类型定义扩展而来的&#xff0c;而ArkTS则是TypeScript的进一步扩展。TypeS…

电子电器架构 --- 网关转发时延解析

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

特殊配合力(SCA)作为全基因组关联分析(GWAS)的表型,其生物学意义和应用价值

生物学意义 解析非加性遗传效应 特殊配合力(SCA)主要反映特定亲本组合的杂交优势,由非加性遗传效应(如显性、超显性、上位性)驱动。显性效应涉及等位基因间的显性互作,上位性效应则涉及不同位点间的基因互作。通过SCA-GWAS,可以定位调控这些非加性效应的关键基因组区域…

2025年 全新 AI 编程工具 Cursor 安装使用教程

一、Cursor 软件下载 首选&#xff0c;登录Cursor官网&#xff0c;进行软件下载&#xff0c;官网下载地址如下&#xff1a; Cursor AI IDE 下载 二、Cursor软件安装配置 此处以Windows10系统安装为例&#xff0c;下载完成之后&#xff0c;右键安装包&#xff0c;以管理员身份…

HarmonyOS 鸿蒙操作物联网设备蓝牙模块、扫描蓝牙、连接蓝牙和蓝牙通信

01【HarmonyOS 蓝牙】 物联网无线传输方案、HarmonyOS蓝牙数据通信之前的准备工作 02【HarmonyOS 蓝牙】配置蓝牙权限 检测 打开 关闭蓝牙 扫描蓝牙 显示蓝牙设备 03【HarmonyOS 蓝牙】连接蓝牙 发现服务 获取特征值 读取信息 写入信息 和蓝牙模块交互 04【物联网 Wifi模块…

【质量管理】TRIZ因果链分析:解码质量问题的“多米诺效应“

为什么要使用因果链分析 没有发现问题并不等于没有问题。爱因斯坦曾说&#xff0c;如果我只有一个小时的时间来拯救世界&#xff0c;我将花45分钟时间分析问题&#xff0c;10分钟的时间来检查问题&#xff0c;最后5分钟的时间来解决问题。可见问题分析的重要性。 在质量管理实践…

解决librechat 前端界面没有google gemini 2.5模型的选项

问题概述 根据librechat 的更新清单&#xff0c;是支持了google gemini的模型&#xff0c;但是却找不到界面上较新的 2.5模型的配置选项。 通过查阅项目的文档&#xff08;GitHub&#xff09;&#xff0c; 看到&#xff1a; 由于目前还不支持获取模型列表&#xff0c;因此您应…

项目实战-基于信号处理与SVM机器学习的声音情感识别系统

目录 一.背景描述 二.理论部分 三.程序设计 编程思路 流程图 1.信号部分 创建数据 generate_samples.py 头文件 生成函数 generate_emotion_sample 传入参数 存储路径 生成参数 创建基础正弦波信号 调制基础正弦波 对于愤怒可以增加噪声 归一化信号 存储 主函…

【论文笔记】SOTR: Segmenting Objects with Transformers

【题目】&#xff1a;SOTR: Segmenting Objects with Transformers 【引用格式】&#xff1a;Guo R, Niu D, Qu L, et al. Sotr: Segmenting objects with transformers[C]//Proceedings of the IEEE/CVF international conference on computer vision. 2021: 7157-7166. 【网…

23盘古石决赛

一&#xff0c;流量分析 1. 计算流量包文件的SHA256值是&#xff1f;[答案&#xff1a;字母小写][★☆☆☆☆] 答案&#xff1a;2d689add281b477c82b18af8ab857ef5be6badf253db1c1923528dd73b3d61a9 解压出来流量包计算 2. 流量包长度在“640 - 1279”之间的的数据包总共有多少…

LLM量化方法:ZeroQuant、LLM.int8()、SmoothQuant、GPTQ、AWQ

文章目录 TLDR;量化分类量化时机量化粒度ZeroQuant: Efficient and Affordable Post-Training Quantization for Large-Scale Transformers细粒度硬件感知量化低成本逐层知识蒸馏&#xff08;Layer-by-layer Knowledge Distillation, LKD&#xff09; LLM.int8(): 8-bit Matrix…