判断支持pointer-events

本文介绍了一个使用Modernizr进行pointer-events属性兼容性检测的代码片段。通过创建一个临时元素并设置样式,该方法判断浏览器是否支持pointer-events属性。

 

   本代码片段来自Modernizr

 

function test(){
    var elem = document.createElement('x');
    elem.style.cssText = 'pointer-events:auto';
    return elem.style.pointerEvents === 'auto';
}

 

 

我需要把放大和缩小的功能变为用鼠标滚轮控制,并且图片放大时,不能只固定在左上角,我还需要可以用数据拖拽到放大的其他区域,以下就是相关vue代码 <template> <Teleport to="body"> <div v-if="visible" class="pdf-mask" @mousedown="startDrag"> <div ref="modalRef" class="pdf-modal" :style="modalStyle"> <div class="pdf-header"> <span class="title">报关资料</span> <div class="tools"> <el-button size="small" @click="zoomIn">放大</el-button> <el-button size="small" @click="zoomOut">缩小</el-button> <el-button size="small" @click="close">关闭</el-button> </div> </div> <!-- 滚动容器 --> <el-tabs v-model="activeTab" class="pdf-tabs"> <el-tab-pane v-for="tab in tabs" :key="tab.id" :label="tab.name" :name="tab.id"> <!-- 单个 PDF 容器 --> <div class="pdf-body" ref="scrollRef" @wheel="onWheel" @mousedown="onPdfMouseDown"> <div :ref="(el) => setPdfContainer(tab.id, el)" /> </div> </el-tab-pane> </el-tabs> </div> </div> </Teleport> </template> <script setup lang="ts"> import { ref, reactive, watch, nextTick } from 'vue' import * as pdfjsLib from 'pdfjs-dist' import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?worker&url' pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker const emit = defineEmits<{ (e: 'update:visible', v: boolean): void (e: 'tabschange', id: string | number): void // 新增 }>() const pdfRealSize = ref({ width: 0, height: 0 }) /* tabs 数据 */ const tabs = ref<Array<{ id: string | number, name: string, url: string }>>([]) const activeTab = ref<string | number>('') async function openWithTabs( list: Array<{ code: string; name: string; url: string }> ) { tabs.value = list .filter((i) => i.name !== 'all_files') .map((i) => ({ id: i.code, name: i.name, url: i.url })) const firstId = tabs.value[0]?.id activeTab.value = firstId ?? '' // 手动让弹窗显示 emit('update:visible', true) // 等 DOM 更新完,立即渲染第一页 await nextTick() if (firstId) { const container = pdfContainerMap.get(firstId) const tab = tabs.value.find((t) => t.id === firstId) if (container && tab) { await renderSinglePDF(tab.url, container) } } // 弹框居中 const cw = window.innerWidth, ch = window.innerHeight modalStyle.left = (cw - 800) / 2 + 'px' modalStyle.top = (ch - 600) / 2 + 'px' } const pdfContainerMap = new Map<string | number, HTMLDivElement>() function setPdfContainer(id: string | number, el: any) { if (el) pdfContainerMap.set(id, el as HTMLDivElement) } watch(activeTab, (id) => { const tab = tabs.value.find(t => t.id === id) if (!tab) return renderSinglePDF(tab.url, pdfContainerMap.get(id)!) }) /* ---------------- props & emits ---------------- */ const props = defineProps<{ visible: boolean url?: string highlight?: { x0: number; y0: number; x1: number; y1: number } | null }>() /* ---------------- 弹窗位置 ---------------- */ const modalRef = ref<HTMLDivElement>() const pdfContainer = ref<HTMLDivElement>() const scrollRef = ref<HTMLDivElement>() // 新增:滚动容器 const modalStyle = reactive({ left: '0px', top: '0px', width: '800px', height: '600px', cursor: 'default' }) let dragStart: { x: number; y: number } | null = null /* ---------------- 拖拽 ---------------- */ function startDrag(e: MouseEvent) { const target = e.target as HTMLElement if (!target.classList.contains('pdf-mask') && !target.closest('.pdf-header')) return if (!modalRef.value) return dragStart = { x: e.clientX, y: e.clientY } modalStyle.cursor = 'grabbing' document.addEventListener('mousemove', onDrag) document.addEventListener('mouseup', stopDrag) e.preventDefault() } function onDrag(e: MouseEvent) { if (!dragStart) return const dx = e.clientX - dragStart.x const dy = e.clientY - dragStart.y modalStyle.left = parseFloat(modalStyle.left) + dx + 'px' modalStyle.top = parseFloat(modalStyle.top) + dy + 'px' dragStart = { x: e.clientX, y: e.clientY } } function stopDrag() { dragStart = null modalStyle.cursor = 'default' document.removeEventListener('mousemove', onDrag) document.removeEventListener('mouseup', stopDrag) if (modalRef.value) { modalStyle.width = modalRef.value.offsetWidth + 'px' modalStyle.height = modalRef.value.offsetHeight + 'px' } } /* ---------------- 缩放 ---------------- */ let scale = 1 // 1️⃣ 换掉全局 scale → 每个 tab 独立 const tabScale = new Map<string | number, number>() const getScale = (id: string | number) => tabScale.get(id) ?? 1 const setScale = (id: string | number, v: number) => tabScale.set(id, Math.min(Math.max(v, 0.5), 3)) // 2️⃣ 放大 / 缩小 const zoomIn = () => { setScale(activeTab.value, getScale(activeTab.value) + 0.2); rerenderActiveTab() } const zoomOut = () => { setScale(activeTab.value, getScale(activeTab.value) - 0.2); rerenderActiveTab() } // 3️⃣ 重新渲染当前 tab async function rerenderActiveTab() { const id = activeTab.value const tab = tabs.value.find(t => t.id === id) const container = pdfContainerMap.get(id) if (tab && container) await renderSinglePDF(tab.url, container) } // 4️⃣ renderSinglePDF 改用当前 tab 的 scale let baseViewport: pdfjsLib.PageViewport | null = null const tabBaseViewport = new Map<string | number, pdfjsLib.PageViewport>() async function renderSinglePDF(url: string, container: HTMLDivElement) { if (!container) return container.innerHTML = '' const pdf = await pdfjsLib.getDocument(url).promise const page = await pdf.getPage(1) baseViewport = page.getViewport({ scale: 1 }) // ← 保存原始 viewport tabBaseViewport.set(activeTab.value, baseViewport) const scale = getScale(activeTab.value) * (800 / baseViewport.width) const vp = page.getViewport({ scale }) const canvas = document.createElement('canvas') canvas.width = vp.width canvas.height = vp.height const ctx = canvas.getContext('2d')! container.appendChild(canvas) await page.render({ canvasContext: ctx, viewport: vp, canvas }).promise; // canvas.dataset.scale = String(scale) // ← 保存当前缩放比例 // 计算当前缩放后的真实尺寸 // 新增:更新滚动容器尺寸 canvas.dataset.scale = String(getScale(activeTab.value)) pdfRealSize.value = { width: vp.width, height: vp.height } const scrollBox = container.parentElement as HTMLDivElement if (scrollBox) { scrollBox.style.width = vp.width + 'px' scrollBox.style.height = vp.height + 'px' } } /* ---------------- PDF 逻辑宽高 ---------------- */ const pdfLogicWidth = ref(0) const pdfLogicHeight = ref(0) /* ---------------- PDF 渲染 ---------------- */ async function renderPDF() { if (!pdfContainer.value || !props.url) return pdfContainer.value.innerHTML = '' try { const pdf = await pdfjsLib.getDocument(props.url).promise const page = await pdf.getPage(1) const baseViewport = page.getViewport({ scale: 1 }) pdfLogicWidth.value = baseViewport.width pdfLogicHeight.value = baseViewport.height const viewport = page.getViewport({ scale: (800 / pdfLogicWidth.value) * scale }) const canvas = document.createElement('canvas') canvas.width = viewport.width canvas.height = viewport.height const ctx = canvas.getContext('2d')! pdfContainer.value.appendChild(canvas) await page.render({ canvasContext: ctx, viewport, canvas }).promise if (props.highlight) drawHighlight(props.highlight) } catch (err) { console.error('Failed to load PDF:', err) } } /* ---------------- 高亮 + 自动滚动 ---------------- */ function drawHighlight(rect: { x0: number; y0: number; x1: number; y1: number }) { const id = activeTab.value const container = pdfContainerMap.get(id) const scrollContainer = container?.parentElement as HTMLDivElement if (!container || !scrollContainer) return // 清除旧高亮 container.querySelectorAll('.pdf-highlight').forEach(el => el.remove()) const canvas = container.querySelector('canvas') if (!canvas) return const canvasScale = parseFloat(canvas.dataset.scale || '1') const baseViewport = tabBaseViewport.get(id) if (!baseViewport) return // 将 PDF 原始坐标 → canvas 渲染坐标 const sx = canvas.width / baseViewport.width const sy = canvas.height / baseViewport.height const hlLeft = rect.x0 * sx const hlTop = rect.y0 * sy const hlWidth = (rect.x1 - rect.x0) * sx const hlHeight = (rect.y1 - rect.y0) * sy const div = document.createElement('div') div.className = 'pdf-highlight' Object.assign(div.style, { position: 'absolute', left: hlLeft + 'px', top: hlTop + 'px', width: hlWidth + 'px', height: hlHeight + 'px', background: 'rgba(0,255,0,0.4)', border: '2px dashed #ff4d4f', pointerEvents: 'none', zIndex: 9999 }) container.style.position = 'relative' container.appendChild(div) /* 自动滚动 */ nextTick(() => { const contW = scrollContainer.clientWidth const contH = scrollContainer.clientHeight scrollContainer.scrollTo({ left: Math.max(0, hlLeft + hlWidth / 2 - contW / 2), top: Math.max(0, hlTop + hlHeight / 2 - contH / 2), behavior: 'smooth' }) }) } /* ---------------- 拖拽 PDF 内容 ---------------- */ let pdfDragging = false let pdfDragStart = { x: 0, y: 0 } let scrollStart = { left: 0, top: 0 } function onPdfMouseDown(e: MouseEvent) { const container = scrollRef.value if (!container) return // 只有在放大时才允许拖动 const scale = getScale(activeTab.value) if (scale <= 1) return pdfDragging = true pdfDragStart = { x: e.clientX, y: e.clientY } scrollStart = { left: container.scrollLeft, top: container.scrollTop } document.addEventListener('mousemove', onPdfMouseMove) document.addEventListener('mouseup', onPdfMouseUp) e.preventDefault() } function onPdfMouseMove(e: MouseEvent) { if (!pdfDragging) return const dx = e.clientX - pdfDragStart.x const dy = e.clientY - pdfDragStart.y const container = scrollRef.value if (container) { container.scrollTo({ left: scrollStart.left - dx, top: scrollStart.top - dy }) } } function onPdfMouseUp() { pdfDragging = false document.removeEventListener('mousemove', onPdfMouseMove) document.removeEventListener('mouseup', onPdfMouseUp) } /* ---------------- 阻止滚轮冒泡 ---------------- */ function onWheel(e: WheelEvent) { e.stopPropagation() } /* ---------------- 监听 visible ---------------- */ watch( () => props.visible, async (v, ov) => { if (!v) { scale = 1; return } await nextTick() if (ov === false) { const cw = window.innerWidth const ch = window.innerHeight const mw = 800 const mh = 600 modalStyle.left = (cw - mw) / 2 + 'px' modalStyle.top = (ch - mh) / 2 + 'px' } renderPDF() }, { immediate: true } ) /* ---------------- 关闭 ---------------- */ function close() { pdfRealSize.value = { width: 0, height: 0 } emit('update:visible', false) } /* ---------------- 监听 highlight ---------------- */ watch( () => props.highlight, (newVal) => { if (newVal) drawHighlight(newVal) }, { immediate: false } ) watch(activeTab, (id) => { const tab = tabs.value.find(t => t.id === id) if (!tab) return emit('tabschange', id) renderSinglePDF(tab.url, pdfContainerMap.get(id)!) }) function drawHighlightByCoords(rect: { x0: number; y0: number; x1: number; y1: number }) { drawHighlight(rect) } onMounted(async () => { await nextTick(); const scrollContainer = scrollRef.value; if (scrollContainer) { scrollContainer.addEventListener('mousedown', onPdfMouseDown); } }); defineExpose({ openWithTabs, drawHighlight: drawHighlightByCoords }) </script> <style scoped> .pdf-mask { position: fixed; inset: 0; z-index: 9999; pointer-events: none; } .pdf-modal { position: absolute; background: #fff; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); display: flex; flex-direction: column; pointer-events: auto; min-width: 400px; min-height: 300px; resize: both; overflow: hidden; } .pdf-header { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; background: #f5f5f5; cursor: grab; border-bottom: 1px solid #e4e4e4; user-select: none; } .pdf-header:active { cursor: grabbing; } /* .pdf-body { flex: 1; overflow: auto; } */ .pdf-body { flex: none; /* 不拉伸 */ overflow: auto; /* 出现滚动条 */ /* 宽高由 JS 动态设置,这里不再写死 */ } .pdf-body canvas { pointer-events: none; } .pdf-modal { height: 700px; } </style>
最新发布
08-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值