mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-02 03:30:04 +00:00
feat: add aspect ratio lock for ImageCrop widget (#8533)
## Summary Add ratio preset dropdown (1:1, 3:4, 4:3, 16:9, 9:16, custom) and lock toggle to the ImageCrop widget. When locked, only corner handles are shown and resizing maintains the selected aspect ratio using diagonal projection for smooth, jump-free interaction. Locking with custom selected captures the current crop's ratio. ## Screenshots (if applicable) https://github.com/user-attachments/assets/a7b5c0a0-c18c-4785-940f-59793702e892 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8533-feat-add-aspect-ratio-lock-for-ImageCrop-widget-2fa6d73d365081a98546e43ed0d7d4fe) by [Unito](https://www.unito.io) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Image crop widget adds a ratio selector with preset and custom options, plus a lock/unlock toggle to preserve aspect while cropping. * Constrained corner-resize behavior enforces locked aspect ratios during resizing. * **Documentation** * Added UI text entries for ratio controls (labels for ratio, lock/unlock, custom). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -57,6 +57,41 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<label class="text-xs text-muted-foreground">
|
||||
{{ $t('imageCrop.ratio') }}
|
||||
</label>
|
||||
<Select v-model="selectedRatio">
|
||||
<SelectTrigger class="h-7 w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="key in ratioKeys" :key="key" :value="key">
|
||||
{{ key === 'custom' ? $t('imageCrop.custom') : key }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
size="icon"
|
||||
:variant="isLockEnabled ? 'primary' : 'secondary'"
|
||||
class="size-7"
|
||||
:aria-label="
|
||||
isLockEnabled
|
||||
? $t('imageCrop.unlockRatio')
|
||||
: $t('imageCrop.lockRatio')
|
||||
"
|
||||
@click="isLockEnabled = !isLockEnabled"
|
||||
>
|
||||
<i
|
||||
:class="
|
||||
isLockEnabled
|
||||
? 'icon-[lucide--lock] size-3.5'
|
||||
: 'icon-[lucide--lock-open] size-3.5'
|
||||
"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<WidgetBoundingBox v-model="modelValue" class="shrink-0" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -65,7 +100,13 @@
|
||||
import { useTemplateRef } from 'vue'
|
||||
|
||||
import WidgetBoundingBox from '@/components/boundingbox/WidgetBoundingBox.vue'
|
||||
import { useImageCrop } from '@/composables/useImageCrop'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Select from '@/components/ui/select/Select.vue'
|
||||
import SelectContent from '@/components/ui/select/SelectContent.vue'
|
||||
import SelectItem from '@/components/ui/select/SelectItem.vue'
|
||||
import SelectTrigger from '@/components/ui/select/SelectTrigger.vue'
|
||||
import SelectValue from '@/components/ui/select/SelectValue.vue'
|
||||
import { ASPECT_RATIOS, useImageCrop } from '@/composables/useImageCrop'
|
||||
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
|
||||
import type { Bounds } from '@/renderer/core/layout/types'
|
||||
|
||||
@@ -80,10 +121,15 @@ const modelValue = defineModel<Bounds>({
|
||||
const imageEl = useTemplateRef<HTMLImageElement>('imageEl')
|
||||
const containerEl = useTemplateRef<HTMLDivElement>('containerEl')
|
||||
|
||||
const ratioKeys = Object.keys(ASPECT_RATIOS)
|
||||
|
||||
const {
|
||||
imageUrl,
|
||||
isLoading,
|
||||
|
||||
selectedRatio,
|
||||
isLockEnabled,
|
||||
|
||||
cropBoxStyle,
|
||||
cropImageStyle,
|
||||
resizeHandles,
|
||||
|
||||
Reference in New Issue
Block a user