| <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} | |