前端-选中pdf中的文字并使用,显示一个悬浮的翻译按钮(本地pdfjs+iframe)不适用textlayer

使用pdfjs移步–

vue2使用pdfjs-dist实现pdf预览(iframe形式,不修改pdfjs原来的ui和控件,dom层可以用display去掉一部分组件)

方案1:获取选择文本内容的最前面的字符坐标的位置(这种写法会导致如果选择超出pdf容器的高度之后,导致按钮显示不出来,这种方法显示位置固定)

方案2:获取选择文本最后鼠标离开位置的坐标(目前没发现bug,这种方法显示的位置不固定)这个方法在最下面,核心位置计算方法handleTextSelectionPdf,updateToolPositionPdf

  1. 实现案例
    在这里插入图片描述
  2. pdf容器创建,悬浮盒子创建
	<iframe:src="pdfurl"class="pdfContent"ref="pdfViewer"frameborder="0"width="100%"height="850px"></iframe><divv-show="selectionToolsVisible"class="selection-tools":style="selectionPosition"@mousedown.prevent><div class="tool-item" @click.stop="getAihelper('entocn')"><img src="../../assets/detailImage/enToCn.png" alt="" /><span class="tool-text">翻译</span></div></div>
  1. data实例
	data() {return {selectionToolsVisible: false,selectionPosition: { top: '0px', left: '0px' },selectionTimer: null,};},
  1. mounted注册鼠标事件-注册pdf的监听
	this.$refs.pdfViewer.onload = () => {const iframeDoc =this.$refs.pdfViewer.contentDocument ||this.$refs.pdfViewer.contentWindow.document;iframeDoc.addEventListener('mouseup', this.handleTextSelectionPdf);};
  1. pdf监听代码-方案1
		handleTextSelectionPdf(e) {clearTimeout(this.selectionTimer);// 缓存关键事件属性const targetElement = e?.target || document.activeElement;// const cachedSelection = window.getSelection().toString().trim();this.selectionTimer = setTimeout(() => {const selection =this.$refs.pdfViewer.contentWindow.getSelection();// 增强型六重验证const isValid =selection.rangeCount > 0 &&!selection.isCollapsed &&selection.toString().trim().length >= 1 && // 允许单字符选择targetElement.closest('#viewerContainer');if (isValid) {// console.log(selection);const range = selection.getRangeAt(0);const rect = this.getAdjustedRectPdf(range);this.updateToolPositionPdf(rect);this.selectionToolsVisible = true;this.tempSelection = selection.toString();} else {this.selectionToolsVisible = false;}}, 50); // 优化响应时间},getAdjustedRectPdf(range) {const tempSpan = document.createElement('span');range.insertNode(tempSpan);const rect = tempSpan.getBoundingClientRect();tempSpan.remove();// 获取 iframe 在整个页面中的位置const iframeRect = this.$refs.pdfViewer.getBoundingClientRect();// **修正点:确保 top 计算正确**const absoluteTop = rect.top + iframeRect.top;const absoluteLeft = rect.left + iframeRect.left + window.scrollX;// console.log(`iframeRect:`, iframeRect);// console.log(`selection rect:`, rect);// console.log(// 	`absoluteTop: ${absoluteTop}, absoluteLeft: ${absoluteLeft}`// );return {top: absoluteTop,left: absoluteLeft,width: rect.width,height: rect.height,};},updateToolPositionPdf(rect) {const viewportWidth = window.innerWidth;const tooltipWidth = '';this.selectionPosition = {top: `${rect.top - 70}px`,left: `${Math.min(Math.max(rect.left, 10),viewportWidth - tooltipWidth - 10)}px`,maxWidth: `${tooltipWidth}px`,};// console.log(`Final tooltip position:`, this.selectionPosition);},
  1. 部分样式-按钮样式
<style scoped lang="less">
::v-deep .selection-tools {position: fixed;background: rgba(29, 115, 232, 1);border-radius: 5px;box-shadow: 0 4px 12px rgba(25, 118, 210, 0.15);padding: 8px;display: inline-flex;align-items: center;// gap: 6px;z-index: 9999;// opacity: 0;transform: translateY(-10px) scale(0.95);transition: all 0.1s cubic-bezier(0.4, 0, 0.2, 1);/* 移除默认的pointer-events限制 */pointer-events: auto !important;/* 修正激活状态逻辑 */&::after {content: '';position: absolute;bottom: -11px;left: 50%;transform: translateX(-50%);border: 6px solid transparent;border-top-color: #1d73e8;filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));}&.active {opacity: 1;transform: translateY(0) scale(1);}.tool-item {padding: 6px 12px;border-radius: 4px;cursor: pointer;display: flex;align-items: center;transition: all 0.2s;flex-direction: column;height: 50px;justify-content: space-between;// &:hover {// 	background: #f0f6ff;// 	transform: translateY(-1px);// 	.iconfont {// 		color: #0065cc;// 	}// 	.tool-text {// 		color: #003d82;// 	}// }.tool-text {font-family: Microsoft YaHei;font-weight: 400;font-size: 12px;color: #ffffff;// line-height: 41px;}}.tool-divider {width: 1px;height: 38px;background: #3f86dd;margin: 0 4px;}img {max-width: 24px;}
}
</style>

以上方法可能导致的问题就是如果选择内容超出了pdf框,会导致按钮显示不出来,所以改为选择之后的鼠标最后出现的位置上面,修改代码handleTextSelectionPdf,updateToolPositionPdf

pdf位置计算核心方法-方案2

	handleTextSelectionPdf(e) {clearTimeout(this.selectionTimer);// 捕获鼠标坐标(相对视口)const mouseX = e.clientX;const mouseY = e.clientY;this.selectionTimer = setTimeout(() => {// 获取 PDF iframe 的文档对象const pdfWindow = this.$refs.pdfViewer.contentWindow;const pdfDocument = pdfWindow.document;// 计算 PDF 容器在页面中的位置const iframeRect = this.$refs.pdfViewer.getBoundingClientRect();// console.log(mouseX, 'mouseX');// console.log(mouseY, 'mouseY');// console.log(iframeRect.left, 'iframeRect.left');// console.log(iframeRect.top, 'iframeRect.top');// console.log(pdfWindow.scrollX, 'pdfWindow.scrollX');// console.log(pdfWindow.scrollY, 'pdfWindow.scrollY');// console.log(window.scrollX, 'window.scrollX');// console.log(window.scrollY, 'window.scrollY');// 转换坐标到 PDF 文档坐标系const pdfX = mouseX;const pdfY = mouseY;// 在 PDF 文档中检测元素const targetElement = pdfDocument.elementFromPoint(pdfX, pdfY);const isValid = targetElement?.closest('#viewerContainer');// 其他验证逻辑保持不变const selection = pdfWindow.getSelection();const isTextValid =selection.rangeCount > 0 &&!selection.isCollapsed &&selection.toString().trim().length >= 1;if (isValid && isTextValid) {// 使用鼠标坐标定位工具框const viewportX = mouseX + iframeRect.left;const viewportY = mouseY + iframeRect.top;this.updateToolPositionPdf(viewportX, viewportY);this.selectionToolsVisible = true;this.tempSelection = selection.toString();} else {this.selectionToolsVisible = false;}}, 50);},updateToolPositionPdf(absoluteX, absoluteY) {const tooltipWidth = ''; // 根据实际工具框宽度调整// 边界检测逻辑let finalLeft = absoluteX - 35;let finalTop = absoluteY - 62.7 - 20; // 减去按钮元素高度 然后上移一点this.selectionPosition = {top: `${finalTop}px`,left: `${finalLeft}px`,maxWidth: `${tooltipWidth}px`,};console.log(this.selectionPosition);},

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

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

相关文章

生活电子常识-deepseek-r1本地化部署+ui界面搭建

前言 deepseek-r1 14b模型&#xff0c;32b模型部署在本地电脑上也能实现非常好的性能。 因此有兴趣研究了下如何在本地部署。 同时最新流行mauns工作流&#xff0c;他们提供一句话实现网页端任意应用的能力。实际上&#xff0c;你也可以用本地的模型来实现离线的ai工作流功能。…

mac丝滑安装Windows操作系统【丝滑简单免费】

mac丝滑安装Windows操作系统【丝滑&简单&免费】 记录mac丝滑安装windows系统1、安装免费版 VMware fusion 132、安装Windows镜像文件3、跳过联网安装&#xff08;完成1后将2拖入1 点点点 即可来到3的环节&#xff09;4、 安装vmware 工具【非常重要&#xff0c;涉及联网…

基于Spring Boot的企业内管信息化系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Pytorch实现之对称卷积神经网络结构实现超分辨率

简介 简介:针对传统的超分辨率重建技术所重建的图像过于光滑且缺乏细节的问题,作者提出了一种改进的生成对抗图像超分辨率网络。 该改进方法基于深度神经网络,其生成模型包含多层卷积模块和多层反卷积模块,其中在感知损失基础上增加了跳层连接和损失函数。 该判别模型由多…

Scikit-learn模型构建全流程解析:从数据预处理到超参数调优

模型选择与训练步骤及示例 1. 数据准备与探索 步骤说明&#xff1a;加载数据并初步探索其分布、缺失值、异常值等。 注意事项&#xff1a; 检查数据类型&#xff08;数值/类别&#xff09;、缺失值和异常值。对类别型特征进行编码&#xff08;如独热编码&#xff09;。 实例&…

001-JMeter的安装与配置

1.前期准备 下载好JMeter : https://jmeter.apache.org/download_jmeter.cgi 下载好JDK : :Java Downloads | Oracle 中国 下载图中圈蓝的JMeter和JDK就行&#xff0c;让它边下载&#xff0c;我们边往下看 2.为什么要下载并安装JDK ? JMeter 是基于 Java 开发的工具&#…

第2.2节 Android Jacoco插件覆盖率采集

JaCoCo&#xff08;Java Code Coverage&#xff09;是一款开源的代码覆盖率分析工具&#xff0c;适用于Java和Android项目。它通过插桩技术统计测试过程中代码的执行情况&#xff0c;生成可视化报告&#xff0c;帮助开发者评估测试用例的有效性。在github上开源的项目&#xff…

特征工程自动化(FeatureTools实战)

目录 特征工程自动化(FeatureTools实战)1. 引言2. 项目背景与意义2.1 特征工程的重要性2.2 自动化特征工程的优势2.3 工业级数据处理需求3. 数据集生成与介绍3.1 数据集构成3.2 数据生成方法4. 自动化特征工程理论基础4.1 特征工程的基本概念4.2 FeatureTools库简介4.3 关键公…

Scikit-learn模型评估全流程解析:从数据划分到交叉验证优化

模型评估的步骤、scikit-learn函数及实例说明 1. 数据划分&#xff08;Train-Test Split&#xff09; 函数&#xff1a;train_test_split使用场景&#xff1a;将数据分为训练集和测试集&#xff0c;避免模型过拟合。作用&#xff1a;确保模型在未见过的数据上验证性能。示例&…

Spring AI相关的面试题

以下是150道Spring AI相关的面试题目及答案&#xff1a; ### Spring AI基础概念类 **1. 什么是Spring AI&#xff1f;** Spring AI是Spring框架的扩展&#xff0c;旨在简化人工智能模型在Java应用中的集成与使用&#xff0c;提供与Spring生态无缝衔接的工具和抽象&#xff0c…

C++ 学习笔记(四)—— 类和对象

1、this指针 class Date { public&#xff1a;void Init(Date* this, int year, int month, int day){this->_year year;this->_month month;this->_day day;this->Print();// 这就是this指针&#xff0c;是编译器自己加的&#xff0c;是用来让成员函数找到成…

SpringMVC全局异常处理机制

异常处理机制 异常处理的两种方式&#xff1a; 编程式异常处理&#xff1a;是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理&#xff0c;例如使用 try-catch 块来捕获异常&#xff0c;然后在 catch 块中编写特定的处理代码&#xff0c;或者在 f…

深入LangChain:LLM交互机制与RAG集成的技术

本文将聚焦于 LangChain 如何集成检索增强生成&#xff08;RAG&#xff09;&#xff0c;了解其架构、主要组件&#xff0c;以及与 LLM 的交互 LangChain 架构概览 1、基础层 这是与各类 LLM 对接的 “桥梁”。LangChain 支持多种流行的 LLM&#xff0c;如 OpenAI 的系列模型、H…

本地部署 LangManus

本地部署 LangManus 0. 引言1. 部署 LangManus2. 部署 LangManus Web UI 0. 引言 LangManus 是一个社区驱动的 AI 自动化框架&#xff0c;它建立在开源社区的卓越工作基础之上。我们的目标是将语言模型与专业工具&#xff08;如网络搜索、爬虫和 Python 代码执行&#xff09;相…

SQL注入(SQL Injection)攻击原理与防御措施

SQL是一种代码注入技术&#xff0c;可使攻击者修改应用程序向数据库提供的查询。 迄今为止&#xff0c;最常见和最严重的应用 程序安全威胁总是隐藏在与数据库有某些连接的网络应用 程序中。 通过这种 SQL 注入&#xff0c;攻击者可以绕过登录程序&#xff0c;获取、更改甚至更…

【算法】十大排序算法(含时间复杂度、核心思想)

以下是 **十大经典排序算法** 的时间复杂度、空间复杂度及稳定性总结&#xff0c;适用于面试快速回顾&#xff1a;排序算法对比表 排序算法最佳时间复杂度平均时间复杂度最差时间复杂度空间复杂度稳定性核心思想冒泡排序O(n)O(n)O(n)O(1)稳定相邻元素交换&#xff0c;大数沉底…

LVS的 NAT 模式实现 3 台RS的轮询访问

使用LVS的 NAT 模式实现 3 台RS的轮询访问 1.配置 RS&#xff08;NAT模式&#xff09;2. 配置 LVS 主机&#xff08;仅主机、NAT模式&#xff09;2.1 配置仅主机网卡&#xff08;192.168.66.150/24 VIP &#xff09;2.2 配置 NAT 网卡&#xff08;192.168.88.6/24 DIP&#xff…

一、MySQL8的my.ini文件

MySQL8.0.11的安装版本my.ini配置文件默认存放在&#xff1a;C:/Program Files/MySQL/MySQL Server 8.0/ 目录下&#xff1b;而MySQL8.0.11绿色免安装版本是没有my.ini配置文件&#xff0c;用户可以自行构建后&#xff0c;再通过my.ini进行数据库的相关配置 一、MySQL8.0.11默…

微调这件小事:训练集中的输入数据该作为instruction还是input?从LLaMA-Factory的源码中寻找答案吧~

在之前的博文中,我们已经了解了LLaMA-Factory框架执行各类任务的流程。今天,我们将深入探讨SFT微调过程中关于数据集的两个关键问题: 数据集中的instruction和input是如何结合起来生成大模型可以理解的输入的?instruction是不是就是system prompt呢?(之所以会问这个问题,…

nacos-actuator漏洞

1、nacos配置文件添加以下配置 vim application.properties# 添加以下配置项 management.endpoints.enabled-by-defaultfalse management.server.port-12、重启Nacos systemctl restart nacos3、验证 打开地址http://ip:port/nacos/actuator查看是否有敏感信息输出&#xff0…