immich/web/src/lib/components/asset-viewer/editor/crop-tool/image-loading.ts
ilyaChuk 7f7fec2cea
feat(web): image editor - panel and cropping (#11074)
* cropping, panel

* fix presets

* types

* prettier

* fix lint

* fix aspect ratio, performance optimization

* improved tool selection, removed placeholder

* fix the mouse's exit from canvas

* fix error

* the "save" button and change tracking

* lint, format

* the mini functionality of the save button

* fix aspect ratio

* hide editor button on mobiles

* strict equality

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* Use the dollar sign syntax for stores inside components

* unobtrusive grid lines, circles at the corners

* more correct image load, handleError

* more strict equality

* fix styles. unused and tailwind

Co-Authored-By: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* dont store isShowEditor

* if showEditor - hide navbar & shortcuts

* crop-canvas decomposition (danger)

I could have accidentally broken something.. but I checked the work and it seems ok.

* fix lint

* fix ts

* callback function as props

* correctly disabling shortcuts

* convenient canvas borders

• you can use the mouse to go beyond the boundaries and freely change the crop.
• the circles on the corners of the canvas are not cut off.

* -the editor button for video files, -save button

* hide editor btn if panoramic || gif || live

* corners instead of circles (preview), fix lint&format

* confirm close editor without save

* vertical aspect ratios

* recovery after merge. editor's closing shortcut

* fix format

* move from canvas to html elements

* fix changes detections

* rotation

* hide detail panel if showing editor

* fix aspect ratios near min size

* fix crop area when changing image size when rotate

* fix of fix

* better layout - grouping

https://github.com/user-attachments/assets/48f15172-9666-4588-acb6-3cb5eda873a8

* hide the button

* fix i18n, format

* hide button

* hide button v2

---------

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-08-14 09:54:50 -05:00

117 lines
3.3 KiB
TypeScript

import { cropImageScale, cropImageSize, cropSettings, type CropSettings } from '$lib/stores/asset-editor.store';
import { get } from 'svelte/store';
import { cropAreaEl, cropFrame, imgElement } from './crop-store';
import { draw } from './drawing';
export function onImageLoad(resetSize: boolean = false) {
const img = get(imgElement);
const cropArea = get(cropAreaEl);
if (!cropArea || !img) {
return;
}
const containerWidth = cropArea.clientWidth ?? 0;
const containerHeight = cropArea.clientHeight ?? 0;
const scale = calculateScale(img, containerWidth, containerHeight);
cropImageSize.set([img.width, img.height]);
if (resetSize) {
cropSettings.update((crop) => {
crop.x = 0;
crop.y = 0;
crop.width = img.width * scale;
crop.height = img.height * scale;
return crop;
});
} else {
const cropFrameEl = get(cropFrame);
cropFrameEl?.classList.add('transition');
cropSettings.update((crop) => normalizeCropArea(crop, img, scale));
cropFrameEl?.classList.add('transition');
cropFrameEl?.addEventListener('transitionend', () => {
cropFrameEl?.classList.remove('transition');
});
}
cropImageScale.set(scale);
img.style.width = `${img.width * scale}px`;
img.style.height = `${img.height * scale}px`;
draw(get(cropSettings));
}
export function calculateScale(img: HTMLImageElement, containerWidth: number, containerHeight: number): number {
const imageAspectRatio = img.width / img.height;
let scale: number;
if (imageAspectRatio > 1) {
scale = containerWidth / img.width;
if (img.height * scale > containerHeight) {
scale = containerHeight / img.height;
}
} else {
scale = containerHeight / img.height;
if (img.width * scale > containerWidth) {
scale = containerWidth / img.width;
}
}
return scale;
}
export function normalizeCropArea(crop: CropSettings, img: HTMLImageElement, scale: number) {
const prevScale = get(cropImageScale);
const scaleRatio = scale / prevScale;
crop.x *= scaleRatio;
crop.y *= scaleRatio;
crop.width *= scaleRatio;
crop.height *= scaleRatio;
crop.width = Math.min(crop.width, img.width * scale);
crop.height = Math.min(crop.height, img.height * scale);
crop.x = Math.max(0, Math.min(crop.x, img.width * scale - crop.width));
crop.y = Math.max(0, Math.min(crop.y, img.height * scale - crop.height));
return crop;
}
export function resizeCanvas() {
const img = get(imgElement);
const cropArea = get(cropAreaEl);
if (!cropArea || !img) {
return;
}
const containerWidth = cropArea?.clientWidth ?? 0;
const containerHeight = cropArea?.clientHeight ?? 0;
const imageAspectRatio = img.width / img.height;
let scale;
if (imageAspectRatio > 1) {
scale = containerWidth / img.width;
if (img.height * scale > containerHeight) {
scale = containerHeight / img.height;
}
} else {
scale = containerHeight / img.height;
if (img.width * scale > containerWidth) {
scale = containerWidth / img.width;
}
}
img.style.width = `${img.width * scale}px`;
img.style.height = `${img.height * scale}px`;
const cropFrame = cropArea.querySelector('.crop-frame') as HTMLElement;
if (cropFrame) {
cropFrame.style.width = `${img.width * scale}px`;
cropFrame.style.height = `${img.height * scale}px`;
}
draw(get(cropSettings));
}