add pinch zoom and fix touch editing

This commit is contained in:
Alex Turchyn authored and Pete Matsyburka committed 2026-10-03 14:05:38 +03:00
1 parent 57c8be33ce
commit aa7d6e9aaa
7 files changed
+185 -43

No files matched your search

+77 -15
View File
@@ -587,9 +587,14 @@
class="w-full mt-0.5 pt-0.5"
:class="[
isMobile ? 'overflow-y-auto' : 'overflow-y-hidden md:overflow-y-auto',
zoomLevel > 1 ? 'overflow-x-auto' : 'overflow-x-hidden'
zoomLevel > 1 ? 'overflow-x-auto' : 'overflow-x-hidden',
{ 'touch-pan-x touch-pan-y': hasDynamicDocuments && !isVisualViewportZoomed }
]"
@wheel="onPagesWheel"
@touchstart="onPagesTouchStart"
@touchmove="onPagesTouchMove"
@touchend="onPagesTouchEnd"
@touchcancel="onPagesTouchEnd"
>
<div
ref="documents"
@@ -893,20 +898,20 @@
leave-to-class="translate-y-4 opacity-0"
>
<div
v-if="zoomLevel > 1"
v-if="(pinchZoomLevel || zoomLevel) > 1"
class="sticky bottom-0 z-10 pointer-events-none"
>
<div class="absolute left-0 right-0 bottom-6 md:bottom-4 flex justify-center">
<div class="join shadow pointer-events-auto">
<span class="join-item bg-base-content text-white pl-2 pr-2.5 h-9 items-center text-sm font-medium cursor-default w-16 flex justify-end">
<span>
{{ Math.round(zoomLevel * 100) }}%
{{ Math.round((pinchZoomLevel || zoomLevel) * 100) }}%
</span>
</span>
<button
type="button"
class="join-item bg-base-content text-white h-9 pl-2 pr-3 inline-flex items-center justify-center cursor-pointer hover:opacity-90 border-l border-white/20"
@click="zoomLevel = 1"
@click="resetZoomLevel"
>
<IconX class="w-4 h-4 stroke-2" />
</button>
@@ -1394,10 +1399,15 @@ export default {
editModalDocumentUuid: null,
revisions: [],
beforeRevisionSnapshot: null,
zoomLevel: 1
zoomLevel: 1,
pinchZoomLevel: null,
isVisualViewportZoomed: false
}
},
computed: {
hasDynamicDocuments () {
return this.template.schema.some((item) => item.dynamic)
},
submitterDefaultNames: FieldSubmitter.computed.names,
isSelectModeRef: () => ref(false),
isCmdKeyRef: () => ref(false),
@@ -1653,6 +1663,7 @@ export default {
window.addEventListener('keydown', this.onKeyDown)
window.addEventListener('dragleave', this.onWindowDragLeave)
window.visualViewport?.addEventListener('resize', this.onVisualViewportResize)
this.$nextTick(() => {
if (document.location.search?.includes('stripe_connect_success')) {
@@ -1677,6 +1688,7 @@ export default {
this.resizeObserver.disconnect()
window.removeEventListener('dragleave', this.onWindowDragLeave)
window.visualViewport?.removeEventListener('resize', this.onVisualViewportResize)
},
beforeUpdate () {
this.documentRefs = []
@@ -2464,23 +2476,73 @@ export default {
event.preventDefault()
const oldZoom = this.zoomLevel
const nextZoom = Math.max(1, Math.min(3, oldZoom - event.deltaY * 0.006))
this.setZoomLevel(this.zoomLevel - event.deltaY * 0.006, event.clientX, event.clientY)
},
onVisualViewportResize () {
if (!this.hasDynamicDocuments) return
if (nextZoom === oldZoom) return
this.isVisualViewportZoomed = window.visualViewport.scale > 1.01
},
onPagesTouchStart (event) {
if (event.touches.length === 2 && this.hasDynamicDocuments && !this.isVisualViewportZoomed) {
const [a, b] = event.touches
const rect = this.$refs.documents.getBoundingClientRect()
const x = (a.clientX + b.clientX) / 2
const y = (a.clientY + b.clientY) / 2
this.pinch = { distance: Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY), zoomLevel: this.zoomLevel, x, y }
this.pinchZoomLevel = this.zoomLevel
this.$refs.documents.style.transformOrigin = `${x - rect.left}px ${y - rect.top}px`
}
},
onPagesTouchMove (event) {
if (!this.pinch || event.touches.length !== 2) return
event.preventDefault()
const [a, b] = event.touches
this.pinchZoomLevel = Math.max(1, Math.min(3, this.pinch.zoomLevel * Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY) / this.pinch.distance))
this.$refs.documents.style.transform = `scale(${this.pinchZoomLevel / this.pinch.zoomLevel})`
},
onPagesTouchEnd (event) {
if (!this.pinch || event.touches.length === 2) return
this.$refs.documents.style.transform = ''
this.$refs.documents.style.transformOrigin = ''
this.setZoomLevel(this.pinchZoomLevel, this.pinch.x, this.pinch.y)
this.pinch = null
this.pinchZoomLevel = null
},
resetZoomLevel () {
const rect = this.$refs.pagesContainer.getBoundingClientRect()
const cursorX = event.clientX - rect.left
const cursorY = event.clientY - rect.top
const ratio = nextZoom / oldZoom
const nextScrollLeft = (this.$refs.pagesContainer.scrollLeft + cursorX) * ratio - cursorX
const nextScrollTop = (this.$refs.pagesContainer.scrollTop + cursorY) * ratio - cursorY
this.setZoomLevel(1, rect.left + rect.width / 2, (Math.max(rect.top, 0) + Math.min(rect.bottom, window.innerHeight)) / 2)
},
setZoomLevel (zoomLevel, clientX, clientY) {
const nextZoom = Math.max(1, Math.min(3, zoomLevel))
if (nextZoom === this.zoomLevel) return
const container = this.$refs.pagesContainer
const scrollContainer = container.scrollHeight > container.clientHeight ? container : document.scrollingElement
const rect = this.$refs.documents.getBoundingClientRect()
this.zoomLevel = nextZoom
this.$nextTick(() => {
this.$refs.pagesContainer.scrollLeft = nextScrollLeft
this.$refs.pagesContainer.scrollTop = nextScrollTop
this.documentRefs.filter((ref) => ref.isDynamic).forEach((ref) => ref.updateContainerWidth())
this.$nextTick(() => {
const nextRect = this.$refs.documents.getBoundingClientRect()
container.scrollLeft += nextRect.left - clientX + (clientX - rect.left) * nextRect.width / rect.width
scrollContainer.scrollTop += nextRect.top - clientY + (clientY - rect.top) * nextRect.height / rect.height
})
})
},
setDocumentRefs (el) {