Keep cover crop image visible while zooming.
Do not revoke the preview blob URL until the dialog closes, so resizing the image on zoom does not blank it. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -158,6 +158,7 @@
|
|||||||
const cropState = {
|
const cropState = {
|
||||||
targetInput: null,
|
targetInput: null,
|
||||||
hint: null,
|
hint: null,
|
||||||
|
objectUrl: '',
|
||||||
naturalW: 0,
|
naturalW: 0,
|
||||||
naturalH: 0,
|
naturalH: 0,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
@@ -207,6 +208,13 @@
|
|||||||
|
|
||||||
const w = cropState.naturalW * cropState.scale
|
const w = cropState.naturalW * cropState.scale
|
||||||
const h = cropState.naturalH * cropState.scale
|
const h = cropState.naturalH * cropState.scale
|
||||||
|
// Cover the crop window so zoom/pan never leaves it empty.
|
||||||
|
const minX = size.maskLeft + size.maskW - w
|
||||||
|
const maxX = size.maskLeft
|
||||||
|
const minY = size.maskTop + size.maskH - h
|
||||||
|
const maxY = size.maskTop
|
||||||
|
cropState.x = Math.min(maxX, Math.max(minX, cropState.x))
|
||||||
|
cropState.y = Math.min(maxY, Math.max(minY, cropState.y))
|
||||||
img.style.width = `${w}px`
|
img.style.width = `${w}px`
|
||||||
img.style.height = `${h}px`
|
img.style.height = `${h}px`
|
||||||
img.style.left = `${cropState.x}px`
|
img.style.left = `${cropState.x}px`
|
||||||
@@ -250,31 +258,35 @@
|
|||||||
function openCoverCrop(file, targetInput, hint) {
|
function openCoverCrop(file, targetInput, hint) {
|
||||||
const modal = document.getElementById('cropModal')
|
const modal = document.getElementById('cropModal')
|
||||||
const img = document.getElementById('cropImage')
|
const img = document.getElementById('cropImage')
|
||||||
|
if (cropState.objectUrl) URL.revokeObjectURL(cropState.objectUrl)
|
||||||
cropState.targetInput = targetInput
|
cropState.targetInput = targetInput
|
||||||
cropState.hint = hint
|
cropState.hint = hint
|
||||||
cropState.scale = 0
|
cropState.scale = 1
|
||||||
cropState.x = 0
|
cropState.x = 0
|
||||||
cropState.y = 0
|
cropState.y = 0
|
||||||
const url = URL.createObjectURL(file)
|
// Keep the blob URL alive until the dialog closes. Revoking it on load
|
||||||
|
// makes the image vanish the next time width/height change (zoom).
|
||||||
|
cropState.objectUrl = URL.createObjectURL(file)
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
cropState.naturalW = img.naturalWidth
|
cropState.naturalW = img.naturalWidth
|
||||||
cropState.naturalH = img.naturalHeight
|
cropState.naturalH = img.naturalHeight
|
||||||
URL.revokeObjectURL(url)
|
|
||||||
modal.classList.add('on')
|
modal.classList.add('on')
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (!fitCropImage()) {
|
if (!fitCropImage()) setTimeout(() => fitCropImage(), 50)
|
||||||
setTimeout(() => fitCropImage(), 50)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
img.src = url
|
img.src = cropState.objectUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeCoverCrop() {
|
function closeCoverCrop() {
|
||||||
document.getElementById('cropModal').classList.remove('on')
|
document.getElementById('cropModal').classList.remove('on')
|
||||||
document.getElementById('cropImage').removeAttribute('src')
|
const img = document.getElementById('cropImage')
|
||||||
|
img.removeAttribute('src')
|
||||||
|
if (cropState.objectUrl) {
|
||||||
|
URL.revokeObjectURL(cropState.objectUrl)
|
||||||
|
cropState.objectUrl = ''
|
||||||
|
}
|
||||||
cropState.targetInput = null
|
cropState.targetInput = null
|
||||||
cropState.scale = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportCroppedFile() {
|
function exportCroppedFile() {
|
||||||
|
|||||||
Reference in New Issue
Block a user