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

发布于:2025-03-21 ⋅ 阅读:(27) ⋅ 点赞:(0)

使用pdfjs移步–

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

  1. 实现案例
    在这里插入图片描述
  2. pdf容器创建,悬浮盒子创建
	<iframe
	:src="pdfurl"
			class="pdfContent"
			ref="pdfViewer"
			frameborder="0"
			width="100%"
			height="850px"
		></iframe>
		<div
			v-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监听代码
		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>


网站公告

今日签到

点亮在社区的每一天
去签到