Vue3使用EasyOFD.js实现ofd文件自定义展示

news/2025/1/10 12:27:32/文章来源:https://www.cnblogs.com/hyt09/p/18640976

EasyOFD.js——一个在web端展示ofd文件的控件,该控件基于CANVAS绘制。官网提供的事例,不适合用于多页ofd文件的展示,本文基于EAYSOFD实现放大、缩小、页面跳转以及多页滚动等功能

1、安装EAYSOFD依赖和EASYOFD组件

//依赖
npm i jszip x2js jb2 opentype.js
//本程序
npm i easyofd

2、vue3中的使用方法

2.1 增加使用EASYOFD的VUE组件

<template><div id="1111111"> </div>
</template>

2.2 在控件加载时初始化组件

onMounted(() => {let yourElement=document.getElementById("1111111");let ofd=new EasyOFD('myofdID', yourElement);
})

2.3 完整代码

<script setup>
import EasyOFD from "easyofd";
import { onMounted } from 'vue'onMounted(() => {let yourElement=document.getElementById("1111111");let ofd=new EasyOFD('myofdID', yourElement);
})</script><template><div id="1111111"> </div></template><style >.OfdButton{padding: 10px 20px;background-color: #007bff;color: #fff;border: none;border-radius: 5px;cursor: pointer;margin-right: 10px;}
</style>
View Code

 

4、按以上步骤可以轻松实现ofd为文件展示、效果如下:

 5、官网给的使用步骤没有给出API,打印ofd.js对象可以看到部分按钮对应的操作方法

 6、根据对API可以自定义放大、缩小、以及页面跳转,完整代码如下:(easyofd.js是利用canvas绘制,每次只显示一页,无法实现多页滚动)

<template><div class="ofd-preview" v-loading="isShow && loading"><div class="ofd-tool"><div class="item"><el-icon class="zoom-btn" @click="zoomIn" title="缩小"><Minus /></el-icon>|<el-icon class="zoom-btn" @click="zoomOut" title="放大"><Plus /></el-icon></div><div class="item"><el-inputv-model="nowPage"style="width: 55px; height: 28px"placeholder="Please input"class="now-page"@keydown.enter="getPage" /><span class="line">/ {{ totalPage }}</span></div></div><div id="ofdBox"></div></div>
</template><script setup lang="ts">
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue';
import { docFilePreview, downloadPdf } from '@/api/docLib';
import { Minus, Plus } from '@element-plus/icons-vue';
import EasyOFD from 'easyofd';
const props = withDefaults(defineProps<{docId: string;isShow: boolean;}>(),{docId: '',isShow: true}
);
const loading = ref(true);
let ofd: any = null;
const nowPage = ref(1); // 当前页;
const totalPage = ref(1); // 总页;

onMounted(() => {let ofdBox = document.getElementById('ofdBox');ofd = new EasyOFD('ofdContainer', ofdBox);
});watch(() => props.docId,async () => {loading.value = true;if (props.docId) {let res = await downloadPdf(props.docId);if (ofd) {ofd.loadFromBlob(res, () => {console.log('加载完成');});console.log('ofd1', ofd);const timer = setInterval(() => {if (ofd.view.AllPageNo) { // 获取总页码loading.value = false;nowPage.value = ofd.view.pageNow;totalPage.value = ofd.view.AllPageNo;console.log('ofd2', ofd);const divEle1 = document.querySelector('#ofdContainer > :nth-child(2)');if (divEle1) {// 默认一屏展示const scale =Math.floor(((divEle1.clientHeight - 20) / ofd.height) * 100) /100;ofd.scaleCanvas(scale);}clearInterval(timer);}}, 500);}}},{immediate: true}
);// 缩小
const zoomIn = () => {console.log('缩小', ofd.zoomSize);if (ofd && ofd.zoomSize > 0.1) {ofd.ZoomIn();}
};// 放大
const zoomOut = () => {console.log('放大', ofd.zoomSize);if (ofd && ofd.zoomSize <= 2) {ofd.ZoomOut();}
};const getPage = () => {console.log('当前页', nowPage.value);if (nowPage.value < 1) {nowPage.value = 1;} else if (nowPage.value > totalPage.value) {nowPage.value = totalPage.value;}ofd.view.SetPage(nowPage.value);ofd.scaleCanvas(ofd.zoomSize);ofd.Draw();
};
</script><style lang="scss" scoped>
.ofd-preview {height: 100%;width: 100%;display: flex;flex-direction: column;.ofd-tool {height: 36px;display: flex;justify-content: flex-end;align-items: center;background-color: #f6f7fc;padding-right: 10px;color: rgb(12, 12, 13);.item {display: flex;align-items: center;}.zoom-btn {width: 28px;height: 28px;padding: 2px 6px 0;border-radius: 2px;user-select: none;cursor: default;&:hover {background-color: rgb(221, 222, 223);}}.now-page {width: 55px;height: 28px;:deep(.el-input__inner) {text-align: right;}}.line {min-width: 16px;padding: 7px;margin: 2px;border-radius: 2px;color: var(--main-color);font-size: 12px;line-height: 14px;text-align: left;}}#ofdBox {height: calc(100% - 36px);width: 100%;overflow: hidden;}
}
</style>
<style lang="scss">
#ofdContainer {height: 100% !important;overflow-y: hidden;border-radius: 8px;> div:nth-child(1) {border-bottom: 1px solid #ddd;display: none !important; // 隐藏原按钮
  }> div:nth-child(2) {background-color: #fff !important;max-width: 100% !important;height: 100% !important;max-height: none !important;box-sizing: border-box;}#ofdContainerselectButton {display: none;}.OfdButton {padding: 6px 8px;background-color: var(--el-color-primary);color: #fff;border: none;border-radius: 5px;cursor: pointer;margin-right: 10px;font-size: 12px;}
}
</style>
View Code

 7、多页文档自定义滚动,获取总页面后循环绘制每一页的canvas,并将canvas缓存为图片,然后展示图片,从而实现多页滚动,页码过多时,可以使用IndexedDB缓存文件,完整代码如下:

<template><div class="ofd-preview" v-loading="isShow && loading"><div class="ofd-tool"><div class="item"><el-icon class="zoom-btn" @click="zoomIn" title="缩小"><Minus /></el-icon>|<el-icon class="zoom-btn" @click="zoomOut" title="放大"><Plus /></el-icon></div><div class="item"><el-inputv-model="nowPage"style="width: 55px; height: 28px"type="number"placeholder="Please input"class="now-page"@keydown.enter="getPage" /><span class="line">/ {{ totalPage }}</span></div></div><div id="ofdBox" v-if="loading"></div><div id="ofdImageBox" v-if="!loading"><imgv-for="(url, i) in dataURLs":key="url":src="url"loading="lazy"class="ofd-image":style="`transform: scale(${imageScale});`":id="`ofdImage_${i}`" /></div></div>
</template><script setup lang="ts">
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue';
import { docFilePreview, downloadPdf } from '@/api/docLib';
import { Minus, Plus } from '@element-plus/icons-vue';
import EasyOFD from 'easyofd';
import ofdIndexedDB from '@/utils/ofdIndexedDB';const props = withDefaults(defineProps<{docId: string;isShow: boolean;}>(),{docId: '',isShow: true}
);
const loading = ref(true);
let ofd: any = null;
const nowPage = ref(1); // 当前页;
const totalPage = ref(1); // 总页;
const dataURLs = ref<string[]>([]);
const imageScale = ref(1);onMounted(() => {});watch(() => props.docId,async () => {loading.value = true;if (props.docId) {let data = await ofdIndexedDB.getData('ofdImageCache', props.docId);console.log('ofdImageCache', data);if (data) {totalPage.value = data.totalPage;dataURLs.value = [];for (let i = 1; i <= totalPage.value; i++) {dataURLs.value.push(data[i]);}loading.value = false;nextTick(() => {observeOfdImageBoxAdd();});return;}let ofdBox = document.getElementById('ofdBox');ofd = new EasyOFD('ofdContainer', ofdBox);let res = await downloadPdf(props.docId);if (ofd) {ofd.loadFromBlob(res, () => {console.log('加载完成');});console.log('ofd1', ofd);const timer = setInterval(() => {if (ofd.view.AllPageNo) {nowPage.value = ofd.view.pageNow;totalPage.value = ofd.view.AllPageNo;console.log('ofd2', ofd);const divEle1 = document.querySelector('#ofdContainer > :nth-child(2)');dataURLs.value = [];if (divEle1) {// 默认一屏展示const scale =Math.floor((divEle1.clientHeight / ofd.height) * 100) / 100;if (scale < 0.4) {// canvas比例太大imageScale.value = 0.5 + scale * 2;ofd.scaleCanvas(scale * 2);} else {imageScale.value = scale;}// 把每一页的数据转换成图片展示const ofdCacheObj = {docId: props.docId,totalPage: totalPage.value};for (let i = 1; i <= totalPage.value; i++) {ofd.view.SetPage(i);ofd.scaleCanvas(ofd.zoomSize);ofd.Draw();const canvas: any = document.querySelector('#ofdContainer-ofd-canvas');if (canvas) {const dataURL = canvas.toDataURL('image/png');console.log('i', i);dataURLs.value.push(dataURL);ofdCacheObj[i] = dataURL;}if (i === totalPage.value) {loading.value = false;nextTick(() => {observeOfdImageBoxAdd();});}}console.log('dataURLs', dataURLs.value);// 缓存数据// const ofdCacheObj = {//   docId: props.docId,//   totalPage: totalPage.value,//   dataURLs: dataURLs.value// };ofdIndexedDB.addData('ofdImageCache', props.docId, ofdCacheObj);}clearInterval(timer);}}, 500);}}},{immediate: true}
);// 缩小
const zoomIn = () => {console.log('缩小', imageScale.value);if (imageScale.value >= 0.1) {imageScale.value = imageScale.value - 0.1;}
};// 放大
const zoomOut = () => {console.log('放大', imageScale.value);if (imageScale.value <= 2) {imageScale.value = imageScale.value + 0.1;}
};const getPage = () => {console.log('当前页', nowPage.value);if (nowPage.value < 1) {nowPage.value = 1;} else if (nowPage.value > totalPage.value) {nowPage.value = totalPage.value;}const ofdImageEle = document.getElementById(`ofdImage_${nowPage.value - 1}`);if (ofdImageEle) {ofdImageEle.scrollIntoView({behavior: 'smooth', // 平滑滚动block: 'start' // 滚动到顶部对齐
    });}
};// 监听图片盒子滚动
const observeVisibilityChanges = (parentElement: HTMLDivElement,callback: Function
) => {const observer = new IntersectionObserver((entries, observer) => {entries.forEach(entry => {if (entry.isIntersecting) {callback(entry.target); // 当元素进入视野时调用回调函数
        }});},{ root: parentElement });// 开始观察所有子元素Array.from(parentElement.children).forEach(child => observer.observe(child));
};
const observeOfdImageBoxAdd = () => {const ofdImageBox = document.getElementById('ofdImageBox') as HTMLDivElement;if (ofdImageBox) {observeVisibilityChanges(ofdImageBox, (element: HTMLImageElement) => {console.log(`${element.id} is now in view.`);if (ofdImageBox.scrollTop > 0 && element.id && element.id.includes('_')) {nowPage.value = +element.id.split('_')[1] + 1 || 1;}});}
};
</script><style lang="scss" scoped>
.ofd-preview {height: 100%;width: 100%;display: flex;flex-direction: column;.ofd-tool {height: 36px;display: flex;justify-content: flex-end;align-items: center;background-color: #f6f7fc;padding-right: 10px;color: rgb(12, 12, 13);.item {display: flex;align-items: center;}.zoom-btn {width: 28px;height: 28px;padding: 2px 6px 0;border-radius: 2px;user-select: none;cursor: default;&:hover {background-color: rgb(221, 222, 223);}}.now-page {width: 55px;height: 28px;:deep(.el-input__inner) {text-align: right;}}.line {min-width: 16px;padding: 7px;margin: 2px;border-radius: 2px;color: var(--main-color);font-size: 12px;line-height: 14px;text-align: left;}}#ofdBox {height: calc(100% - 36px);width: 100%;overflow: hidden;}#ofdImageBox {display: flex;flex-direction: column;height: 100%;overflow: auto;> img {align-self: center;}}
}
</style>
<style lang="scss">
#ofdContainer {height: 100% !important;overflow-y: hidden;border-radius: 8px;> div:nth-child(1) {border-bottom: 1px solid #ddd;display: none !important;}> div:nth-child(2) {background-color: #fff !important;max-width: 100% !important;height: 100% !important;max-height: none !important;box-sizing: border-box;}#ofdContainerselectButton {display: none;}.OfdButton {padding: 6px 8px;background-color: var(--el-color-primary);color: #fff;border: none;border-radius: 5px;cursor: pointer;margin-right: 10px;font-size: 12px;}
}
</style>
View Code

8、最终效果展示如下:

 

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

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

相关文章

云服务器数据盘扩容失败,如何解决?

您好,在处理云服务器数据盘扩容时,遇到扩容未成功的情况是比较常见的。为了确保您的数据安全并顺利解决问题,建议您按照以下步骤进行排查和操作:确认扩容操作是否正确执行首先,请确认您是否已经按照官方文档中的说明正确执行了扩容操作。通常情况下,扩容操作需要通过控制…

点睛之篇

一、学期回顾 1.1对软工的印象 软件工程,我学的不是大数据吗?怎么还要修软工。这是我对软件工程课程的第一印象,当时的我只感觉软件工程“与我无瓜”。但是我的这份认知很快就被证实是错误的了。随着课程的推进,我发现软件工程是一门挺有意思的课程,在这门课上我能学习到许…

当当网图书信息获取

当当网图书信息获取 虽然现在是信息时代,大多数人都会选择电子读物,但是纸质图书仍是我们大多数学生不可或缺的. 准备工作: ​ 查看网站允许抓取权限:进行网页抓取时必须遵守网站的robots.txt规则.频繁地请求会给网站服务器带来负担,导致违反服务条款导致IP封禁 ​ https://www…

洛谷题单指南-线段树的进阶用法-P4587 [FJOI2016] 神秘数

原题链接:https://www.luogu.com.cn/problem/P4587 题意解读:对于序列a[n],查询m个区间[l,r]数值对应集合的神秘数。集合 S 的神秘数定义为最小的不能被 S 的子集的和表示的正整数。 解题思路: 对于区间[l,r],从小到大将数值选入集合,来观察神秘数的变化,设S当前的神秘数…

pandas-Series

一、Series特点 二、特点练习import pandas as pd import numpy as np #pandas---Series#默认索引从0开始且数组类型数据 data=np.array([张三,李四,王五,赵六]) s=pd.Series(data) print(s)#自定义索引 s=pd.Series(data,index=[100,101,102,103],name=series_name) print(s)…

软件系统验收全流程解析:从准备到后续保障,关键要点一览

验收准备阶段确定验收标准:在项目启动初期,就应根据项目需求和合同约定,制定详细的验收标准。验收标准应涵盖功能、性能、安全、兼容性、易用性等各个方面,确保软件系统满足用户的业务需求和预期。 组建验收团队:验收团队通常由用户方代表、项目负责人、技术专家、测试人员…

CDS标准视图:维护活动类型 I_MaintenanceActivityType

视图名称:维护活动类型 I_MaintenanceActivityType 视图类型:基础 视图代码:点击查看代码 @ObjectModel.supportedCapabilities: [ #ANALYTICAL_DIMENSION, #EXTRACTION_DATA_SOURCE, #CDS_MODELING_ASSOCIATION_TARGET,#SQL_DATA_SOURCE, #CDS_MODELING_DATA_SOURCE ]@Meta…

封闭几何形状中的静压液位测量——填充高度的计算

与开口容器不同,封闭几何形状(例如气密罐或者加压容器)中的液位计算需要通过静水压力测量来补偿封闭在液体上方的气体的压力。 对于封闭或加压环境下的液位测量,非通风的容器中的液位测量需要通过第二压力变送器对封闭的气体进行额外的压力测量。在一个密闭容器内,主要使用…

CDS标准视图:PM通知单 I_LocationAnalysisCube

视图名称:PM通知单 I_LocationAnalysisCube 视图类型:分析 视图代码:点击查看代码 @EndUserText.label: Location Analysis - Cube @VDM.viewType: #COMPOSITE @AbapCatalog.sqlViewName: ILOCANALYSISCUBE @AccessControl.authorizationCheck: #CHECK @ClientHandling.algo…

推荐一款集监控和埋点于一体的前端性能监控工具!开源、简单易用、功能强大!

在互联网的快速发展下,网站已成为企业和个人展示信息、提供服务的重要平台。然而,随之而来的网站性能问题也日益凸显,如加载速度慢、频繁出错、服务器故障、数据异常、网络攻击等。如何确保用户能够快速稳定地访问网站成为了一个亟待解决的问题。 为了帮助大家解决这一问题,…