Skip to content

Commit

Permalink
Improve zoom image on hover performance 🚀 (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
willnguyen1312 authored Nov 13, 2023
1 parent 74276d8 commit cafb5f8
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 73 deletions.
5 changes: 5 additions & 0 deletions .changeset/neat-experts-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zoom-image/core": minor
---

Improve zoom image on hover performance 🚀
104 changes: 42 additions & 62 deletions packages/core/src/createZoomImageHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export function createZoomImageHover(container: HTMLElement, options: ZoomImageH
zoomedImgWrapper.style.overflow = "hidden"
const zoomedImg = zoomedImgWrapper.appendChild(document.createElement("img"))
zoomedImg.style.maxWidth = "none"
zoomedImg.style.display = "none"
const zoomLens = container.appendChild(document.createElement("div"))
zoomLens.style.display = "none"

const finalOptions: Required<ZoomImageHoverOptions> = {
zoomImageSource: options.zoomImageSource || sourceImgElement.src,
Expand Down Expand Up @@ -58,7 +60,7 @@ export function createZoomImageHover(container: HTMLElement, options: ZoomImageH
enabled: true,
})

let offset: { left: number; top: number }
let offset: { left: number; top: number } = getOffset(sourceImgElement)

function getOffset(element: HTMLElement) {
const elRect = element.getBoundingClientRect()
Expand All @@ -83,63 +85,6 @@ export function createZoomImageHover(container: HTMLElement, options: ZoomImageH
return clamp(top, minY, getLimitY(minY)) - minY
}

function setZoomedImgSize() {
// Custom zoom available
if (customZoom) {
zoomedImgWrapper.style.width = customZoom.width + "px"
zoomedImgWrapper.style.height = customZoom.height + "px"
return
}

// Default zoom to source image size
zoomedImgWrapper.style.width = sourceImgElement.width + "px"
zoomedImgWrapper.style.height = sourceImgElement.height + "px"
}

function onSourceImageReady() {
setZoomedImgSize()
offset = getOffset(sourceImgElement)

zoomedImg.style.display = "block"
zoomedImg.style.display = "none"
zoomedImg.width = (sourceImgElement.width * scale) / zoomLensScale
zoomedImg.height = (sourceImgElement.height * scale) / zoomLensScale

// Setup default zoom lens style
const fromLeft = sourceImgElement.getBoundingClientRect().left - container.getBoundingClientRect().left
const fromTop = sourceImgElement.getBoundingClientRect().top - container.getBoundingClientRect().top
zoomLens.style.position = "absolute"
zoomLens.style.left = fromLeft + "px"
zoomLens.style.top = fromTop + "px"
zoomTarget.style.pointerEvents = "none"

if (!zoomLensClass) {
zoomLens.style.background = "rgba(238, 130, 238, 0.5)"
}

zoomLens.style.width = (customZoom.width / scale) * zoomLensScale + "px"
zoomLens.style.height = (customZoom.height / scale) * zoomLensScale + "px"
}

function setup() {
zoomLens.style.display = "none"

if (zoomLensClass) {
const classes = zoomLensClass.split(" ")
classes.forEach((className) => zoomLens.classList.add(className))
}

// setup event listeners
container.addEventListener("pointerdown", processZoom, { signal })
container.addEventListener("pointermove", processZoom, { signal })
container.addEventListener("pointerenter", handlePointerEnter, { signal })
container.addEventListener("pointerleave", handlePointerLeave, { signal })
window.addEventListener("scroll", handleScroll, { signal })

// Setup zoomed image position if zoom target is specified
zoomTarget.appendChild(zoomedImgWrapper)
}

function processZoom(event: PointerEvent) {
let offsetX: number
let offsetY: number
Expand Down Expand Up @@ -184,10 +129,45 @@ export function createZoomImageHover(container: HTMLElement, options: ZoomImageH
offset = getOffset(sourceImgElement)
}

if (sourceImgElement.complete) {
onSourceImageReady()
} else {
sourceImgElement.onload = onSourceImageReady
function setup() {
if (zoomLensClass) {
zoomLens.className = zoomLensClass
} else {
zoomLens.style.background = "rgba(238, 130, 238, 0.5)"
}

// setup event listeners
container.addEventListener("pointerdown", processZoom, { signal })
container.addEventListener("pointermove", processZoom, { signal })
container.addEventListener("pointerenter", handlePointerEnter, { signal })
container.addEventListener("pointerleave", handlePointerLeave, { signal })
window.addEventListener("scroll", handleScroll, { signal })

// Append zoomed image wrapper to zoom target
zoomTarget.appendChild(zoomedImgWrapper)

// Set up styles if custom zoom available
if (customZoom) {
zoomedImgWrapper.style.width = customZoom.width + "px"
zoomedImgWrapper.style.height = customZoom.height + "px"
} else {
// Else default zoom to source image size
zoomedImgWrapper.style.width = sourceImgElement.width + "px"
zoomedImgWrapper.style.height = sourceImgElement.height + "px"
}

zoomedImg.width = (sourceImgElement.width * scale) / zoomLensScale
zoomedImg.height = (sourceImgElement.height * scale) / zoomLensScale

// Setup default zoom lens style
const fromLeft = sourceImgElement.getBoundingClientRect().left - container.getBoundingClientRect().left
const fromTop = sourceImgElement.getBoundingClientRect().top - container.getBoundingClientRect().top
zoomTarget.style.pointerEvents = "none"
zoomLens.style.position = "absolute"
zoomLens.style.left = fromLeft + "px"
zoomLens.style.top = fromTop + "px"
zoomLens.style.width = (customZoom.width / scale) * zoomLensScale + "px"
zoomLens.style.height = (customZoom.height / scale) * zoomLensScale + "px"
}

setup()
Expand Down
22 changes: 11 additions & 11 deletions size.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"@zoom-image/core": {
"createZoomImageWheel": "1.49 KB",
"createZoomImageHover": "1.18 KB",
"createZoomImageHover": "1.14 KB",
"createZoomImageMove": "870 B",
"createZoomImageClick": "847 B",
"cropImage": "202 B",
Expand All @@ -10,44 +10,44 @@
},
"@zoom-image/react": {
"useZoomImageWheel": "1.79 KB",
"useZoomImageHover": "1.45 KB",
"useZoomImageHover": "1.42 KB",
"useZoomImageMove": "1.1 KB",
"useZoomImageClick": "1.08 KB"
},
"@zoom-image/preact": {
"useZoomImageWheel": "2.7 KB",
"useZoomImageHover": "2.38 KB",
"useZoomImageHover": "2.35 KB",
"useZoomImageMove": "2.06 KB",
"useZoomImageClick": "2.02 KB"
},
"@zoom-image/qwik": {
"useZoomImageWheel": "1.95 KB",
"useZoomImageHover": "1.58 KB",
"useZoomImageHover": "1.55 KB",
"useZoomImageMove": "1.23 KB",
"useZoomImageClick": "1.2 KB"
},
"@zoom-image/solid": {
"useZoomImageWheel": "2.98 KB",
"useZoomImageHover": "2.65 KB",
"useZoomImageHover": "2.62 KB",
"useZoomImageMove": "2.32 KB",
"useZoomImageClick": "2.29 KB"
},
"@zoom-image/svelte": {
"useZoomImageWheel": "1.98 KB",
"useZoomImageHover": "1.65 KB",
"useZoomImageHover": "1.63 KB",
"useZoomImageMove": "1.32 KB",
"useZoomImageClick": "1.29 KB"
},
"@zoom-image/vue": {
"useZoomImageWheel": "1.76 KB",
"useZoomImageHover": "1.44 KB",
"useZoomImageHover": "1.41 KB",
"useZoomImageMove": "1.08 KB",
"useZoomImageClick": "1.07 KB"
},
"@zoom-image/angular": {
"ZoomImageWheelService": "3.25 KB",
"ZoomImageClickService": "3.25 KB",
"ZoomImageMoveService": "3.25 KB",
"ZoomImageHoverService": "3.25 KB"
"ZoomImageHoverService": "3.23 KB",
"ZoomImageClickService": "3.23 KB",
"ZoomImageMoveService": "3.23 KB",
"ZoomImageWheelService": "3.23 KB"
}
}

0 comments on commit cafb5f8

Please sign in to comment.