Vue实现图片预览,侧边栏懒加载,不用任何插件,简单好用
实现样式

需求
实现PDF上传预览,并且不能下载
第一次实现:用vue-pdf,将上传的文件用base64传给前端展示
问题:
- 水印第一次加载有后面又没有了。
- 当上传大的pdf文件后,前端获取和渲染又长又慢,甚至不能用
修改实现模式
- 前端上传PDF,后端将PDF转化成一页一页的图片
- 前端根据page去获取一页一页的PDF图片,类似于百度文库
实现思路
配合后端实现思路
- 获取全部页数,先把侧边栏的元素画出来占个位置
- 获取已经看到的页数,没有默认1
- 渲染上次看到的页数,同时侧边栏滚动到相同的index位置,通过监听元素是否进入视口去获取base64图片
- 已经获取回来的图片不再去请求
主要重点难点是侧边栏懒加载、定位、等比例展示图片
<div class="pdf-viewer"><div class="pdf-main"><canvas id="pdf-view"></canvas></div><div class="pdf-list" :class="{ collapse: collapse }"><divclass="pdf-item":class="{ active: currentPage === index }"v-for="index in pageTotalNum":key="index"@click="changePage(index)":data-index="index"><img :src="imgList[index - 1]" alt="" /></div></div></div><script>
let observer = null;
export default {name: "PDFView",data() {return {currentPage: 1, //当前页数pageTotalNum: 1, //总页数imgList: [], //base64图片列表updateTimer: null};},watch: {/*** @description 监听当前页变化 滚动列表到顶部*/currentPage() {this.$nextTick(() => {const activeEl = document.querySelector(".pdf-list .active");if (activeEl) {document.querySelector(".pdf-list").scrollTo({top: activeEl.offsetTop - 20,behavior: "smooth",});// 解决进来会请求当前页数 前面所有图片setTimeout(() => {if (observer) {observer.disconnect();}this.isEnter();}, 500);}// 切换页面 将查看区域滚动到最上面const mainEl = document.querySelector(".pdf-main");mainEl.scrollTo({top: 0,});});},},mounted() {this.getPageTotal();},beforeDestroy() {if (observer) {observer.disconnect();}},methods: {/*** @description 获取pdf总页数*/getPageTotal() {const params = {id: this.$route.query.id,};apiGetViewPdfPageTotal(params).then((response) => {this.pageTotalNum = response.data;this.updateStudy(true);});},/*** @description 切换当前页*/changePage(index) {this.currentPage = index;this.updateStudy();if (this.imgList[index - 1]) {this.drawImage(this.imgList[index - 1]);} else {this.getPdf();}},/*** @description 上一页*/prePage() {let page = this.currentPage;if (page !== 1) {page = page > 1 ? page - 1 : this.pageTotalNum;this.currentPage = page;this.updateStudy();if (this.imgList[page - 1]) {this.drawImage(this.imgList[page - 1]);} else {this.getPdf();}}},/*** @description 下一页*/nextPage() {let page = this.currentPage;if (page !== this.pageTotalNum) {page = page < this.pageTotalNum ? page + 1 : 1;this.currentPage = page;this.updateStudy();if (this.imgList[page - 1]) {this.drawImage(this.imgList[page - 1]);} else {this.getPdf();}}},/*** @description 更新学习 flag=true第一次进入*/updateStudy(flag = false) {const params = {courseId: this.$route.query.id,pageRate: this.currentPage,flag,totalPageRate: this.pageTotalNum,};apiUpdateStudy(params).then((response) => {this.currentPage = response.data.pageRate;if (flag) {this.updateTimer = setInterval(() => {this.updateStudy();}, 1000 * 10);}if (flag) {this.getPdf();// 解决第一页进来不请求的问题,一页大概能展示4-5张if (this.currentPage < 5) {this.isEnter();}}})},/*** @description 查看资料*/getPdf() {const params = {id: this.$route.query.id,page: this.currentPage,};apiGetPdf(params).then((response) => {let base64 = "data:image/png;base64," + response.data;this.drawImage(base64);});},/*** @description 将base64图片 画到canvas上*/drawImage(base64) {const canvas = document.getElementById("pdf-view");const context = canvas.getContext("2d");const image = new Image();image.src = base64;image.onload = () => {const proportion = image.width / image.height;// 获取style设置width:100% 的canvas宽度const canvasWidth = canvas.offsetWidth;// 图片宽度与canvas宽度比例const canvasWidthProportion = image.width / canvasWidth;// canvas宽度设置为宽度canvas.width = image.width;// 根据图片比例和宽度比例计算出canvas高度canvas.height = (canvasWidth / proportion) * canvasWidthProportion;context.drawImage(image, 0, 0);};},/*** @description 监听元素进入视口*/isEnter() {observer = new IntersectionObserver((entries) => {entries.forEach((entry) => {const target = entry.target;const index = target.dataset.index;if (entry.isIntersecting) {if (!this.imgList[index - 1]) {this.getImgList(index);}} else {// console.log("元素离开视口", index);}});});this.$nextTick(() => {//将所有侧边栏的元素进行监听const els = document.querySelectorAll(".pdf-item");Array.from(els).forEach((el) => {observer.observe(el);});});},/*** @description 滚动获取图片*/getImgList(index) {const params = {id: this.$route.query.id,page: index,};apiGetPdf(params).then((response) => {let base64 = "data:image/png;base64," + response.data;this.imgList[index - 1] = base64;// 解决请求回来页面没更新的问题this.$forceUpdate();});},},
};
</script><style lang="scss" scoped>
.pdf-container {width: 100%;height: 100%;color: #999;
}
.pdf-viewer {width: 100%;height: calc(100vh - 50px - 30px - 60px - 6px);position: relative;display: flex;
}
.pdf-list {width: 240px;overflow-y: auto;display: flex;flex-direction: column;padding: 20px;background: #000;box-sizing: border-box;// transition: all 0.3s ease-in-out;border-left: 1px solid #999;&::-webkit-scrollbar {width: 0px;}.pdf-item {height: 183px;min-height: 183px;display: inline-flex;justify-content: center;align-items: center;cursor: pointer;overflow: hidden;&:hover {::v-deep img {transition: all 0.5s ease-in-out;transform: scale(1.1);}}&.active {box-shadow: 0px 0px 0px 4px #e6a23c;}&:not(:last-child) {margin-bottom: 10px;}img {pointer-events: none;width: 100%;// height: 100%;}}&.collapse {width: 0;padding: 0;}
}
.pdf-main {flex: 1;// width: 100%;// height: 100%;overflow-y: auto;background: #000;position: relative;padding: 10px 0;&::-webkit-scrollbar {width: 0px;}
}
.handle-btn {background: #000;display: flex;font-size: 12px;position: relative;height: 60px;padding: 0 6px;border-bottom: 1px solid #999;.right {width: 240px;display: flex;align-items: center;justify-content: flex-end;font-size: 32px;}.main {flex: 1;display: flex;align-items: center;justify-content: center;font-size: 32px;margin-left: 250px;.pagination {display: flex;align-items: center;margin: 0 10px;.pagination-info {font-size: 14px;margin: 0 8px;}}.zoom {display: flex;align-items: center;margin: 0 10px;.scale {font-size: 14px;margin: 0 8px;}}}.tips {color: #e6a23c;font-size: 12px;}.start-test {display: flex;align-items: center;}.time {position: absolute;left: 6px;top: 50%;transform: translateY(-50%);> span {display: inline-block;margin-left: 10px;}}
}
i {cursor: pointer;&:hover {color: #fff;}
}
#pdf-view {width: 100%;// height: 100%;padding: 10px;
}
</style>相关文章:
Vue实现图片预览,侧边栏懒加载,不用任何插件,简单好用
实现样式 需求 实现PDF上传预览,并且不能下载 第一次实现:用vue-pdf,将上传的文件用base64传给前端展示 问题: 水印第一次加载有后面又没有了。当上传大的pdf文件后,前端获取和渲染又长又慢,甚至不能用 修…...
Spring依赖注入之setter注入与构造器注入以及applicationContext.xml配置文件特殊值处理
依赖注入之setter注入 在管理bean对象的组件的时候同时给他赋值,就是setter注入,通过setter注入,可以将某些依赖项标记为可选的,因为它们不是在构造对象时立即需要的。这种方式可以减少构造函数的参数数量,使得类的构…...
碳排放预测 | Matlab实现LSTM多输入单输出未来碳排放预测,预测新数据
碳排放预测 | Matlab实现LSTM多输入单输出未来碳排放预测,预测新数据 目录 碳排放预测 | Matlab实现LSTM多输入单输出未来碳排放预测,预测新数据预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现LSTM长短期记忆神经网络多输入单输出未来…...
手拉手JavaFX UI控件与springboot3+FX桌面开发
目录 javaFx文本 javaFX颜色 字体 Label标签 Button按钮 //按钮单击事件 鼠标、键盘事件 //(鼠标)双击事件 //键盘事件 单选按钮RadioButton 快捷键、键盘事件 CheckBox复选框 ChoiceBox选择框 Text文本 TextField(输入框)、TextArea文本域 //过滤 (传入一个参数&a…...
02 分解质因子
一、数n的质因子分解 题目描述: 输入一个数n(n<10^6),将数n分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。 输入 5 输出 5 1 输入 10 输出 2 1 5 1 朴素解法: 首先求出1~n的所有质数…...
科技赋能智慧水利——山海鲸软件水利方案解析
作为山海鲸可视化软件的开发者,我们深感荣幸能为我国智慧水利建设提供强大助力。作为钻研数字孪生领域的开创者,我们希望不仅能为大家带来免费好用,人人都能用起来的数字孪生产品,还希望以其独特的技术优势和创新设计理念…...
C4.5决策树的基本建模流程
C4.5决策树的基本建模流程 作为ID3算法的升级版,C4.5在三个方面对ID3进行了优化: (1)它引入了信息值(information value)的概念来修正信息熵的计算结果,以抑制ID3更偏向于选择具有更多分类水平…...
本科毕业设计过程中应该锻炼的能力 (深度学习方向)
摘要: 本文以本科毕业设计做深度学习方向, 特别是全波形反演为例, 描述学生应在此过程中锻炼的能力. 搭建环境的能力. 包括 Python, PyTorch 等环境的安装.采集数据的能力. 包括 OpenFWI 等数据集.查阅资料的能力. 包括自己主要参考的文献, 以及其它相关文献 (不少于 20 篇). …...
深度学习——pycharm远程连接
目录 远程环境配置本地环境配置(注意看假设!!!这是很多博客里没写的)步骤1步骤2步骤2.1 配置Connection步骤2.2 配置Mappings 步骤3 配置本地项目的远程解释器技巧1 pycharm中远程终端连接技巧2 远程目录技巧3 上传代码文件技巧4 …...
信号量机制解决经典同步互斥问题
生产者 / 消费者问题、读者 / 写者问题和哲学家问题是操作系统的三大经典同步互斥问题。本文将介绍这三个问题的基本特点以及如何用信号量机制进行解决。 在分析这三个问题之前,我们首先需要了解用信号量机制解决同步互斥问题的一般规律: 实现同步与互斥…...
java基础09-==和equals()的区别,附代码举例
和equals()的区别 在Java中,和equals()是两个不同的运算符,它们在比较对象时有着本质的区别。 运算符: 用于比较两个基本数据类型(如int、char等)或两个对象的引用。 当用于比较基本数据类型时,它会比较它们的值。 当…...
qml与C++的交互
qml端使用C对象类型、qml端调用C函数/c端调用qml端函数、qml端发信号-连接C端槽函数、C端发信号-连接qml端函数等。 代码资源下载: https://download.csdn.net/download/TianYanRen111/88779433 若无法下载,直接拷贝以下代码测试即可。 main.cpp #incl…...
LabVIEW电路板插件焊点自动检测系统
LabVIEW电路板插件焊点自动检测系统 介绍了电路板插件焊点的自动检测装置设计。项目的核心是使用LabVIEW软件,开发出一个能够自动检测电路板上桥接、虚焊、漏焊和多锡等焊点缺陷的系统。 系统包括成像单元、机械传动单元和软件处理单元。首先,利用工业相…...
第十一站:多态练习ODU
实现动态切换 ODU.h #pragma once #include <iostream> using namespace std; #define ODU_TYPE_311_FLAG "311" #define ODU_TYPE_335_FLAG "335" enum class ODU_TYPE {ODU_TYPE_311,ODU_TYPE_335,ODU_TYPE_UNKNOW };class ODU{ public:ODU();//发…...
【深度学习】详解利用Matlab和Python中 LSTM 网络实现序列分类
🔗 运行环境:Matlab、Python 🚩 撰写作者:左手の明天 🥇 精选专栏:《python》 🔥 推荐专栏:《算法研究》 🔐#### 防伪水印——左手の明天 ####🔐 💗 大家好🤗🤗🤗,我是左手の明天!好久不见💗 💗今天分享Matlab深度学习—— LSTM 网络实现序列分...
Unity 工厂方法模式(实例详解)
文章目录 在Unity中,工厂方法模式是一种创建对象的常用设计模式,它提供了一个接口用于创建对象,而具体的产品类是由子类决定的。这样可以将对象的创建过程与使用过程解耦,使得代码更加灵活和可扩展。 工厂模式的主要优点如下&…...
2024年美赛数学建模思路 - 案例:异常检测
文章目录 赛题思路一、简介 -- 关于异常检测异常检测监督学习 二、异常检测算法2. 箱线图分析3. 基于距离/密度4. 基于划分思想 建模资料 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 一、简介 – 关于异常…...
一键完成,批量转换HTML为PDF格式的方法,提升办公效率
在当今数字化的时代,HTML和PDF已经成为两种最常用的文件格式。HTML用于网页内容的展示,而PDF则以其高度的可读性和不依赖于平台的特性,成为文档分享和传播的首选格式。然而,在办公环境中,我们经常需要在这两种格式之间…...
【重点问题】攻击面发现及管理
Q1:在使用长亭云图极速版时,是否需要增设白名单扫描节点? 长亭云图极速版高级网络安全产品基于一种理念,即攻击面发现是一个不断变换且需要持续对抗的过程。在理想的情况下,用户应当在所有预置防护设施发挥作用的环境…...
UE4外包团队:国外使用UE4虚幻引擎制作的十个知名游戏
1.俄罗斯方块效果(任天堂 Switch、PlayStation 4、PC、Xbox) 2.耀西的手工世界(任天堂 Switch) 3. Final Fantasy 7 Remake Intergrade (PlayStation, PC) 4.《堡垒之夜》(PC、Nintendo Switch、PlayStation、Xb…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
