|
<script lang="ts">
|
|
import { getContext, onDestroy, onMount, tick } from "svelte";
|
|
|
|
import { type ToolContext, TOOL_KEY } from "./Tools.svelte";
|
|
import { type EditorContext, EDITOR_KEY } from "../ImageEditor.svelte";
|
|
import { crop_canvas, resize_and_reposition, type CropCommand } from "./crop";
|
|
|
|
import Cropper from "./Cropper.svelte";
|
|
|
|
const { active_tool, register_tool } = getContext<ToolContext>(TOOL_KEY);
|
|
const {
|
|
dimensions,
|
|
editor_box,
|
|
pixi,
|
|
crop,
|
|
command_manager,
|
|
current_history
|
|
} = getContext<EditorContext>(EDITOR_KEY);
|
|
|
|
let cropper: CropCommand | null;
|
|
|
|
export let crop_constraint: [number, number] | `${string}:${string}` | null;
|
|
|
|
let _crop_constraint: number | null = null;
|
|
$: {
|
|
if (typeof crop_constraint === "string") {
|
|
const [w, h] = crop_constraint.split(":");
|
|
_crop_constraint = parseInt(w) / parseInt(h);
|
|
} else if (Array.isArray(crop_constraint)) {
|
|
_crop_constraint = crop_constraint[0] / crop_constraint[1];
|
|
}
|
|
}
|
|
|
|
let w_p = 1;
|
|
let h_p = 1;
|
|
let l_p = 0;
|
|
let t_p = 0;
|
|
|
|
let current_opacity = 0;
|
|
|
|
let manually_cropped = false;
|
|
|
|
$: {
|
|
if (!manually_cropped && _crop_constraint && $pixi && $active_tool) {
|
|
requestAnimationFrame(() => {
|
|
initial_crop();
|
|
});
|
|
}
|
|
}
|
|
|
|
let c: CropCommand | null = null;
|
|
|
|
async function initial_crop(): Promise<void> {
|
|
if (c || $current_history.previous) return;
|
|
const { new_height, new_width, x_offset, y_offset } = resize_and_reposition(
|
|
$editor_box.child_width,
|
|
$editor_box.child_height,
|
|
"c",
|
|
_crop_constraint!,
|
|
|
|
$editor_box.child_width,
|
|
$editor_box.child_height
|
|
);
|
|
|
|
w_p = new_width / $editor_box.child_width;
|
|
h_p = new_height / $editor_box.child_height;
|
|
l_p = x_offset / $editor_box.child_width;
|
|
t_p = y_offset / $editor_box.child_height;
|
|
|
|
c = crop_canvas($pixi!.renderer, $pixi!.mask_container, crop, 0.2);
|
|
c.start(...$dimensions, current_crop, false);
|
|
|
|
c.continue(
|
|
[
|
|
l_p * $dimensions[0],
|
|
t_p * $dimensions[1],
|
|
w_p * $dimensions[0],
|
|
h_p * $dimensions[1]
|
|
],
|
|
false
|
|
);
|
|
c.stop();
|
|
|
|
c.execute();
|
|
c = null;
|
|
current_opacity = 0;
|
|
}
|
|
|
|
function handle_crop(
|
|
type: "start" | "stop" | "continue",
|
|
|
|
{
|
|
x,
|
|
y,
|
|
width,
|
|
height
|
|
}: {
|
|
x: number;
|
|
y: number;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
): void {
|
|
if (!$pixi) return;
|
|
|
|
if (type === "start") {
|
|
if (cropper) {
|
|
current_opacity = cropper.stop();
|
|
cropper = null;
|
|
}
|
|
|
|
cropper = crop_canvas(
|
|
$pixi?.renderer,
|
|
$pixi.mask_container,
|
|
crop,
|
|
current_opacity
|
|
);
|
|
cropper.start(...$dimensions, current_crop);
|
|
} else if (type === "continue") {
|
|
if (!cropper) return;
|
|
cropper.continue([
|
|
x * $dimensions[0],
|
|
y * $dimensions[1],
|
|
width * $dimensions[0],
|
|
height * $dimensions[1]
|
|
]);
|
|
} else if (type === "stop") {
|
|
if (!cropper) return;
|
|
command_manager.execute(cropper);
|
|
}
|
|
manually_cropped = true;
|
|
}
|
|
|
|
let measured = false;
|
|
|
|
function get_measurements(): void {
|
|
if (measured) return;
|
|
if (!$editor_box.child_height) return;
|
|
const _height = $editor_box.child_height;
|
|
const _width = $editor_box.child_width;
|
|
const _top = $editor_box.child_top - $editor_box.parent_top;
|
|
const _left = $editor_box.child_left - $editor_box.parent_left;
|
|
|
|
w_p = _width / $editor_box.child_width;
|
|
h_p = _height / $editor_box.child_height;
|
|
l_p =
|
|
(_left - $editor_box.child_left + $editor_box.parent_left) /
|
|
$editor_box.child_width;
|
|
t_p =
|
|
(_top - $editor_box.child_top + $editor_box.parent_top) /
|
|
$editor_box.child_height;
|
|
|
|
measured = true;
|
|
}
|
|
|
|
let current_crop: [number, number, number, number];
|
|
$: current_crop = [
|
|
l_p * $dimensions[0],
|
|
t_p * $dimensions[1],
|
|
w_p * $dimensions[0],
|
|
h_p * $dimensions[1]
|
|
];
|
|
|
|
$: {
|
|
l_p = $crop[0];
|
|
t_p = $crop[1];
|
|
w_p = $crop[2];
|
|
h_p = $crop[3];
|
|
}
|
|
|
|
$: $editor_box && get_measurements();
|
|
|
|
const unregister = register_tool("crop");
|
|
onDestroy(unregister);
|
|
</script>
|
|
|
|
{#if $active_tool === "crop" && measured}
|
|
<Cropper
|
|
crop_constraint={_crop_constraint}
|
|
{w_p}
|
|
{h_p}
|
|
{l_p}
|
|
{t_p}
|
|
{editor_box}
|
|
on:crop_start={({ detail }) => handle_crop("start", detail)}
|
|
on:crop_continue={({ detail }) => handle_crop("continue", detail)}
|
|
on:crop_end={({ detail }) => handle_crop("stop", detail)}
|
|
/>
|
|
{/if}
|
|
|