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

实现样式

在这里插入图片描述

需求

实现PDF上传预览,并且不能下载

第一次实现:用vue-pdf,将上传的文件用base64传给前端展示
问题:

  1. 水印第一次加载有后面又没有了。
  2. 当上传大的pdf文件后,前端获取和渲染又长又慢,甚至不能用

修改实现模式

  1. 前端上传PDF,后端将PDF转化成一页一页的图片
  2. 前端根据page去获取一页一页的PDF图片,类似于百度文库

实现思路

配合后端实现思路

  1. 获取全部页数,先把侧边栏的元素画出来占个位置
  2. 获取已经看到的页数,没有默认1
  3. 渲染上次看到的页数,同时侧边栏滚动到相同的index位置,通过监听元素是否进入视口去获取base64图片
  4. 已经获取回来的图片不再去请求

主要重点难点是侧边栏懒加载定位等比例展示图片

 <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>

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

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

相关文章

Docker K8s-存储相关概念

Docker中的存储有两个概念&#xff1a;存储驱动程序Storage Driver和卷驱动程序Volumes Drivers。 存储驱动 Storage Driver 首先我们来看一下安装docker以后&#xff0c;docker的文件夹下面有哪些内容&#xff1a; cd /var/lib/docker && ll这里存储了所有的数据&a…

力扣hot100 轮转数组 一题多解 翻转数组

Problem: 189. 轮转数组 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public void rotate(int[] nums, int k){int n nums.length;k k % n;reverse(…

Git搭建

文件格式 <VirtuaHost * 80> nginx </virtualHost> pache xml server {} nginx conf格式 [xx] 配置内容 代码开发中版本控制,项目代码编译构建,项目版本迭代全流程 命令300条 Hospital Information System 开发语言分类: 编译型: C nginx ma…

ArcEngine添加点要素、线要素、面要素及学习总结

基于C#的ArcEngine二次开发教程&#xff08;13&#xff09;&#xff1a;点、线、面要素的绘制_arcengine onmousedown-CSDN博客 https://www.cnblogs.com/cannel/p/11074343.html ArcEngine绘制点、线、多边形、矩形、圆形、椭圆的代码_arcengine 开发 生成矩形-CSDN博客 https…

DocsOpenApi自动化校验

一、背景 生产环境的文档中心的OpenApi和Kong服务配置的OpenApi可能存在不一致的情况&#xff0c;比如生产环境的文档中心有某个OpenApi&#xff0c;但是Kong服务没有配置到&#xff0c;那客户使用就会有问题。所以&#xff0c;前段时间&#xff08;M09版本&#xff09;花了4个…

MS7256C:L1 频段卫星导航射频前端低噪声放大器芯片

1、描述 MS7256C 是一款具有高增益、低噪声系数的低噪声放 大器&#xff08;LNA&#xff09;芯片&#xff0c;支持 L1 频段多模式全球卫星定位&#xff0c;可 以应用于 GPS、北斗二代、伽利略、Glonass 等 GNSS 导航 接收机中。芯片采用先进工艺制造&#xff0c;封装采用 1…

HDD的烦恼:HAMR会让SMR黯然失色吗?

HDD相关阅读参考&#xff1a; HDD回暖于2024&#xff0c;与SSD决战于2028 HDD最后的冲刺&#xff1a;大容量硬盘的奋力一搏 叠瓦式磁记录技术&#xff08;SMR&#xff09;自20世纪90年代起开始研究&#xff0c;于2010年后逐渐商业化应用于高密度硬盘。该技术的核心理念在于通…

从一个简单的Ping案例来分析二层,三层的数据包封装过程

1. 应用程序生成数据DATA&#xff0c;加上传输层报文头(TCP/UDP Head),调用网络层服务&#xff08;IP包头中的源地址由主机网卡直接得到&#xff0c;目的IP则由我们在使用应用程序时输入得到&#xff0c;如果是基于域名&#xff0c;调用一个通信过程DNS来获得目的IP&#xff0…

便捷接口调测:API 开发工具大比拼 | 开源专题 No.62

hoppscotch/hoppscotch Stars: 56.1k License: MIT Hoppscotch 是一个开源的 API 开发生态系统&#xff0c;主要功能包括发送请求和获取实时响应。该项目具有以下核心优势&#xff1a; 轻量级&#xff1a;采用简约的 UI 设计。快速&#xff1a;实时发送请求并获得响应。支持多…

独立站怎么建设对seo好?

现如今市面上就有不少开源的建站程序可供挑选&#xff0c;哪怕你不懂技术&#xff0c;不懂代码&#xff0c;也能建自己的独立站&#xff0c;效果比不少所谓的用自己技术开发的站都要好&#xff0c;本身做一个网站不难&#xff0c;但你做网站的目的是什么&#xff1f;是为了在搜…

【开源】基于JAVA语言的人事管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员功能模块2.2 普通员工功能模块2.3 答辩文案 三、系统展示四、核心代码4.1 查询职称4.2 新增留言回复4.3 工资申请4.4 工资审核4.5 员工请假 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的人…

Spring 声明式事务 @Transactional(详解)【面试重点,小林出品】

关于 Transactional 注解的基本使用&#xff0c;推荐看Spring 声明式事务 Transactional&#xff08;基本使用&#xff09; 概述 本篇博客主要学习 Transactional 注解当中的三个常⻅属性: 1. rollbackFor:异常回滚属性.指定能够触发事务回滚的异常类型.可以指定多个异常类型 …

AIGC是什么?GPT-4.0、DALL·E以及Midjourney等多种智能服务

AIGC&#xff08;人工智能生成内容&#xff0c;Artificial Intelligence Generated Content&#xff09;是指利用人工智能技术自动生成的文本、图像、音频和视频等内容。随着技术的进步&#xff0c;AIGC已经成为创意产业和内容创作领域的一股新兴力量。MidTool作为一款集成了多…

qml与C++的交互

qml端使用C对象类型、qml端调用C函数/c端调用qml端函数、qml端发信号-连接C端槽函数、C端发信号-连接qml端函数等。 代码资源下载&#xff1a; https://download.csdn.net/download/TianYanRen111/88779433 若无法下载&#xff0c;直接拷贝以下代码测试即可。 main.cpp #incl…

mmpose 2d姿态预测值转json文件

目录 效果图: 参考 模板文件下载地址: python预测代码: 效果图: <

51单片机ESP8266

一、MQTT透传AT固件 安信可提供的烧录WiFi固件工具&#xff1a; 链接: https://docs.ai-thinker.com/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B72 安信可提供的固件库链接: https://docs.ai-thinker.com/%E5%9B%BA%E4%BB%B6%E6%B1%87%E6%80%BB 经过测试&#xff0c;选择这个不可以…

Zephyr 源码调试

背景 调试环境对于学习源码非常重要&#xff0c;但嵌入式系统的调试环境搭建稍微有点复杂&#xff0c;需要的条件略多。本文章介绍如何在 Zephyr 提供的 qemu 上调试 Zephyr 源码&#xff0c;为后续分析 Zephyr OS 相关原理做铺垫。 环境 我的开发环境为 wsl ubuntu&#xf…

Redis 击穿、穿透、雪崩产生原因解决思路

大家都知道&#xff0c;计算机的瓶颈之一就是IO&#xff0c;为了解决内存与磁盘速度不匹配的问题&#xff0c;产生了缓存&#xff0c;将一些热点数据放在内存中&#xff0c;随用随取&#xff0c;降低连接到数据库的请求链接,避免数据库挂掉。需要注意的是&#xff0c;无论是击穿…

逻辑回归中的损失函数梯度下降

一、引言 逻辑回归中的损失函数通常采用的是交叉熵损失函数&#xff08;cross-entropy loss function&#xff09;。在逻辑回归中&#xff0c;我们通常使用sigmoid函数将线性模型的输出转换为概率值&#xff0c;然后将这些概率值与实际标签进行比较&#xff0c;从而计算损失。 …

模型选择实战

我们现在可以通过多项式拟合来探索这些概念。 import math import numpy as np import torch from torch import nn from d2l import torch as d2l生成数据集 给定x&#xff0c;我们将使用以下三阶多项式来生成训练和测试数据的标签&#xff1a; max_degree 20 # 多项式的最…