fix: eliminate visual shaking when adjusting crop region (#8896)

## Summary
Replace the dual-image approach (dimmed <img> + background-image in crop
box) with a single <img> and a box-shadow overlay on the crop box. The
previous approach required two independently positioned images to be
pixel-perfectly aligned, which caused visible jitter from sub-pixel
rounding differences, especially when zoomed in.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8896-fix-eliminate-visual-shaking-when-adjusting-crop-region-3086d73d365081309703d3ab0d4ad44d)
by [Unito](https://www.unito.io)
This commit is contained in:
Terry Jia
2026-02-17 19:56:46 -05:00
committed by GitHub
parent 596df0f0c6
commit cde872fcf7
2 changed files with 3 additions and 18 deletions

View File

@@ -28,7 +28,7 @@
:src="imageUrl"
:alt="$t('imageCrop.cropPreviewAlt')"
draggable="false"
class="block size-full object-contain select-none brightness-50"
class="block size-full object-contain select-none"
@load="handleImageLoad"
@error="handleImageError"
@dragstart.prevent
@@ -36,14 +36,12 @@
<div
v-if="imageUrl && !isLoading"
class="absolute box-content cursor-move overflow-hidden border-2 border-white"
class="absolute box-content cursor-move border-2 border-white shadow-[0_0_0_9999px_rgba(0,0,0,0.5)]"
:style="cropBoxStyle"
@pointerdown="handleDragStart"
@pointermove="handleDragMove"
@pointerup="handleDragEnd"
>
<div class="pointer-events-none size-full" :style="cropImageStyle" />
</div>
/>
<div
v-for="handle in resizeHandles"
@@ -131,7 +129,6 @@ const {
isLockEnabled,
cropBoxStyle,
cropImageStyle,
resizeHandles,
handleImageLoad,

View File

@@ -243,17 +243,6 @@ export function useImageCrop(nodeId: NodeId, options: UseImageCropOptions) {
height: `${cropHeight.value * scaleFactor.value}px`
}))
const cropImageStyle = computed(() => {
if (!imageUrl.value) return {}
return {
backgroundImage: `url(${imageUrl.value})`,
backgroundSize: `${displayedWidth.value}px ${displayedHeight.value}px`,
backgroundPosition: `-${cropX.value * scaleFactor.value}px -${cropY.value * scaleFactor.value}px`,
backgroundRepeat: 'no-repeat'
}
})
interface ResizeHandle {
direction: ResizeDirection
class: string
@@ -605,7 +594,6 @@ export function useImageCrop(nodeId: NodeId, options: UseImageCropOptions) {
isLockEnabled,
cropBoxStyle,
cropImageStyle,
resizeHandles,
handleImageLoad,