From 7a36116dd2f2f5170c0dfe7773b8ade7ca79a0d2 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 19 Jan 2026 23:12:51 -0300 Subject: [PATCH] fix: simplify code Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/components/PDFElements.vue | 102 ++++++++++++--------------------- src/utils/geometry.js | 20 +++++++ src/utils/measurements.js | 16 ++++++ src/utils/objectStore.js | 35 +++++++++++ src/utils/pageBounds.js | 13 +++++ src/utils/zoom.js | 8 +++ 6 files changed, 128 insertions(+), 66 deletions(-) create mode 100644 src/utils/geometry.js create mode 100644 src/utils/measurements.js create mode 100644 src/utils/objectStore.js create mode 100644 src/utils/pageBounds.js create mode 100644 src/utils/zoom.js diff --git a/src/components/PDFElements.vue b/src/components/PDFElements.vue index 80e021e..f0f8a32 100644 --- a/src/components/PDFElements.vue +++ b/src/components/PDFElements.vue @@ -138,6 +138,11 @@ SPDX-License-Identifier: AGPL-3.0-or-later import PDFPage from './PDFPage.vue' import DraggableElement from './DraggableElement.vue' import { readAsPDF, readAsArrayBuffer } from '../utils/asyncReader.js' +import { clampPosition, getVisibleArea } from '../utils/geometry.js' +import { getViewportWindow, isPageInViewport } from '../utils/pageBounds.js' +import { applyScaleToDocs } from '../utils/zoom.js' +import { objectIdExistsInDoc, findObjectPageIndex, updateObjectInDoc, removeObjectFromDoc } from '../utils/objectStore.js' +import { getCachedMeasurement } from '../utils/measurements.js' export default { name: 'PDFElements', @@ -238,9 +243,11 @@ export default { } }, created() { - this._pagesBoundingRects = {} - this._pagesBoundingRectsList = [] - this._pageMeasurementCache = {} + this._pagesBoundingRects = {} + this._pagesBoundingRectsList = [] + this._pageMeasurementCache = {} + this._lastPageBoundsScrollTop = 0 + this._lastPageBoundsClientHeight = 0 }, mounted() { this.boundHandleWheel = this.handleWheel.bind(this) @@ -436,8 +443,14 @@ export default { const container = this.$el const scrollTop = container?.scrollTop || 0 const viewHeight = container?.clientHeight || 0 - const minY = Math.max(0, scrollTop - 300) - const maxY = scrollTop + viewHeight + 300 + if (!this.isAddingMode && !this.isDraggingElement && + scrollTop === this._lastPageBoundsScrollTop && + viewHeight === this._lastPageBoundsClientHeight) { + return + } + this._lastPageBoundsScrollTop = scrollTop + this._lastPageBoundsClientHeight = viewHeight + const { minY, maxY } = getViewportWindow(scrollTop, viewHeight) for (let docIdx = 0; docIdx < this.pdfDocuments.length; docIdx++) { for (let pageIdx = 0; pageIdx < this.pdfDocuments[docIdx].pages.length; pageIdx++) { const canvas = this.getPageCanvasElement(docIdx, pageIdx) @@ -446,7 +459,7 @@ export default { const wrapper = canvas.closest('.page-wrapper') || canvas const offsetTop = wrapper.offsetTop || 0 const offsetHeight = wrapper.offsetHeight || 0 - if (offsetTop + offsetHeight < minY || offsetTop > maxY) { + if (!isPageInViewport(offsetTop, offsetHeight, minY, maxY)) { continue } } @@ -657,9 +670,7 @@ export default { this.scale = newScale - this.pdfDocuments.forEach((doc) => { - doc.pagesScale = doc.pagesScale.map(() => this.scale) - }) + applyScaleToDocs(this.pdfDocuments, this.scale) this._pageMeasurementCache = {} this.cachePageBounds() @@ -765,24 +776,15 @@ export default { const cacheKey = `${docIndex}-${objectId}` if (this.objectIndexCache[cacheKey] !== undefined) return true const doc = this.pdfDocuments[docIndex] - if (!doc) return false - return doc.allObjects.some(objects => objects.some(obj => obj.id === objectId)) + return objectIdExistsInDoc(doc, objectId) }, updateObjectInPage(docIndex, pageIndex, objectId, payload) { const doc = this.pdfDocuments[docIndex] - const objects = doc?.allObjects?.[pageIndex] - if (!objects) return - const objectIndex = objects.findIndex(object => object.id === objectId) - if (objectIndex === -1) return - objects.splice(objectIndex, 1, { ...objects[objectIndex], ...payload }) + updateObjectInDoc(doc, pageIndex, objectId, payload) }, removeObjectFromPage(docIndex, pageIndex, objectId) { const doc = this.pdfDocuments[docIndex] - const objects = doc?.allObjects?.[pageIndex] - if (!objects) return - const objectIndex = objects.findIndex(object => object.id === objectId) - if (objectIndex === -1) return - objects.splice(objectIndex, 1) + removeObjectFromDoc(doc, pageIndex, objectId) }, getAllObjects(docIndex = this.selectedDocIndex) { @@ -826,12 +828,10 @@ export default { let currentPageIndex = this.objectIndexCache[cacheKey] if (currentPageIndex === undefined) { - doc.allObjects.forEach((objects, pIndex) => { - if (objects.find(o => o.id === objectId)) { - currentPageIndex = pIndex - this.objectIndexCache[cacheKey] = pIndex - } - }) + currentPageIndex = findObjectPageIndex(doc, objectId) + if (currentPageIndex !== undefined) { + this.objectIndexCache[cacheKey] = currentPageIndex + } } if (currentPageIndex === undefined) return @@ -880,7 +880,7 @@ export default { const pageWidth = this.getPageWidth(docIndex, pIndex) const pageHeight = this.getPageHeight(docIndex, pIndex) - const visibleArea = this.getVisibleArea(newX, newY, objWidth, objHeight, pageWidth, pageHeight) + const visibleArea = getVisibleArea(newX, newY, objWidth, objHeight, pageWidth, pageHeight) if (visibleArea > maxVisibleArea) { maxVisibleArea = visibleArea bestPageIndex = pIndex @@ -889,7 +889,7 @@ export default { if (bestPageIndex !== currentPageIndex) { const { width: pageWidth, height: pageHeight } = this.getPageSize(docIndex, bestPageIndex) - const { x: adjustedX, y: adjustedY } = this.clampPosition(newX, newY, objWidth, objHeight, pageWidth, pageHeight) + const { x: adjustedX, y: adjustedY } = clampPosition(newX, newY, objWidth, objHeight, pageWidth, pageHeight) this.removeObjectFromPage(docIndex, currentPageIndex, objectId) const updatedObject = { @@ -949,12 +949,10 @@ export default { let currentPageIndex = this.objectIndexCache[cacheKey] if (currentPageIndex === undefined) { - doc.allObjects.forEach((objects, pIndex) => { - if (objects.find(o => o.id === objectId)) { - currentPageIndex = pIndex - this.objectIndexCache[cacheKey] = pIndex - } - }) + currentPageIndex = findObjectPageIndex(doc, objectId) + if (currentPageIndex !== undefined) { + this.objectIndexCache[cacheKey] = currentPageIndex + } } if (currentPageIndex === undefined) return undefined @@ -982,7 +980,7 @@ export default { const relY = (mouseY - targetPageRect.top - this.draggingElementShift.y) / pagesScale - (this.draggingInitialMouseOffset.y / pagesScale) const { width: pageWidth, height: pageHeight } = this.getPageSize(docIndex, targetPageIndex) - const { x: clampedX, y: clampedY } = this.clampPosition( + const { x: clampedX, y: clampedY } = clampPosition( relX, relY, targetObject.width, @@ -1039,37 +1037,11 @@ export default { height: this.getPageHeight(docIndex, pageIndex), } }, - clampPosition(x, y, width, height, pageWidth, pageHeight) { - return { - x: Math.max(0, Math.min(x, pageWidth - width)), - y: Math.max(0, Math.min(y, pageHeight - height)), - } - }, - getVisibleArea(newX, newY, objWidth, objHeight, pageWidth, pageHeight) { - const visibleLeft = Math.max(0, newX) - const visibleTop = Math.max(0, newY) - const visibleRight = Math.min(pageWidth, newX + objWidth) - const visibleBottom = Math.min(pageHeight, newY + objHeight) - if (visibleRight <= visibleLeft || visibleBottom <= visibleTop) { - return 0 - } - return (visibleRight - visibleLeft) * (visibleBottom - visibleTop) - }, getCachedMeasurement(docIndex, pageIndex, pageRef) { const cacheKey = `${docIndex}-${pageIndex}` - const cached = this._pageMeasurementCache[cacheKey] - if (cached) { - return cached - } const doc = this.pdfDocuments[docIndex] const pagesScale = doc.pagesScale[pageIndex] || 1 - const measurement = pageRef.getCanvasMeasurement() - const normalized = { - width: measurement.canvasWidth / pagesScale, - height: measurement.canvasHeight / pagesScale, - } - this._pageMeasurementCache[cacheKey] = normalized - return normalized + return getCachedMeasurement(this._pageMeasurementCache, cacheKey, pageRef, pagesScale) }, calculateOptimalScale(maxPageWidth) { const containerWidth = this.$el?.clientWidth || 0 @@ -1113,9 +1085,7 @@ export default { if (Math.abs(optimalScale - this.scale) > 0.01) { this.scale = optimalScale this.visualScale = optimalScale - this.pdfDocuments.forEach((doc) => { - doc.pagesScale = doc.pagesScale.map(() => this.scale) - }) + applyScaleToDocs(this.pdfDocuments, this.scale) this._pageMeasurementCache = {} this.cachePageBounds() } diff --git a/src/utils/geometry.js b/src/utils/geometry.js new file mode 100644 index 0000000..7e71d86 --- /dev/null +++ b/src/utils/geometry.js @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +export function clampPosition(x, y, width, height, pageWidth, pageHeight) { + return { + x: Math.max(0, Math.min(x, pageWidth - width)), + y: Math.max(0, Math.min(y, pageHeight - height)), + } +} + +export function getVisibleArea(newX, newY, objWidth, objHeight, pageWidth, pageHeight) { + const visibleLeft = Math.max(0, newX) + const visibleTop = Math.max(0, newY) + const visibleRight = Math.min(pageWidth, newX + objWidth) + const visibleBottom = Math.min(pageHeight, newY + objHeight) + if (visibleRight <= visibleLeft || visibleBottom <= visibleTop) { + return 0 + } + return (visibleRight - visibleLeft) * (visibleBottom - visibleTop) +} diff --git a/src/utils/measurements.js b/src/utils/measurements.js new file mode 100644 index 0000000..4657045 --- /dev/null +++ b/src/utils/measurements.js @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +export function getCachedMeasurement(cache, cacheKey, pageRef, pagesScale) { + const cached = cache[cacheKey] + if (cached) { + return cached + } + const measurement = pageRef.getCanvasMeasurement() + const normalized = { + width: measurement.canvasWidth / pagesScale, + height: measurement.canvasHeight / pagesScale, + } + cache[cacheKey] = normalized + return normalized +} diff --git a/src/utils/objectStore.js b/src/utils/objectStore.js new file mode 100644 index 0000000..7e03da8 --- /dev/null +++ b/src/utils/objectStore.js @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +export function objectIdExistsInDoc(doc, objectId) { + if (!doc || !objectId) return false + return doc.allObjects.some((objects) => objects.some((obj) => obj.id === objectId)) +} + +export function findObjectPageIndex(doc, objectId) { + if (!doc || !objectId) return undefined + for (let pageIndex = 0; pageIndex < doc.allObjects.length; pageIndex++) { + if (doc.allObjects[pageIndex].some((obj) => obj.id === objectId)) { + return pageIndex + } + } + return undefined +} + +export function updateObjectInDoc(doc, pageIndex, objectId, payload) { + const objects = doc?.allObjects?.[pageIndex] + if (!objects) return false + const objectIndex = objects.findIndex((obj) => obj.id === objectId) + if (objectIndex === -1) return false + objects.splice(objectIndex, 1, { ...objects[objectIndex], ...payload }) + return true +} + +export function removeObjectFromDoc(doc, pageIndex, objectId) { + const objects = doc?.allObjects?.[pageIndex] + if (!objects) return false + const objectIndex = objects.findIndex((obj) => obj.id === objectId) + if (objectIndex === -1) return false + objects.splice(objectIndex, 1) + return true +} diff --git a/src/utils/pageBounds.js b/src/utils/pageBounds.js new file mode 100644 index 0000000..eec203c --- /dev/null +++ b/src/utils/pageBounds.js @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +export function getViewportWindow(scrollTop, viewHeight, margin = 300) { + return { + minY: Math.max(0, scrollTop - margin), + maxY: scrollTop + viewHeight + margin, + } +} + +export function isPageInViewport(offsetTop, offsetHeight, minY, maxY) { + return !(offsetTop + offsetHeight < minY || offsetTop > maxY) +} diff --git a/src/utils/zoom.js b/src/utils/zoom.js new file mode 100644 index 0000000..f856fb8 --- /dev/null +++ b/src/utils/zoom.js @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +export function applyScaleToDocs(docs, scale) { + docs.forEach((doc) => { + doc.pagesScale = doc.pagesScale.map(() => scale) + }) +}