-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathEditCanvas.svelte
More file actions
127 lines (111 loc) · 3.14 KB
/
Copy pathEditCanvas.svelte
File metadata and controls
127 lines (111 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<script lang="ts">
import type { Page, Block } from '$lib/types';
import EditableBox from './EditableBox.svelte';
import { onMount } from 'svelte';
type ZoomMode = 'fit-screen' | 'fit-width' | 'original';
interface Props {
pageData: Page;
pageImage: string;
workingBlocks: Block[];
selectedIndex: number | null;
zoomMode: ZoomMode;
updateBlock: (index: number, block: Block) => void;
deleteBlock: (index: number) => void;
cloneBlock: (index: number) => number;
}
let {
pageData,
pageImage,
workingBlocks = $bindable(),
selectedIndex = $bindable(),
zoomMode,
updateBlock,
deleteBlock,
cloneBlock
}: Props = $props();
let containerRef: HTMLDivElement;
let scale = $state(1);
function handleSelect(index: number) {
selectedIndex = index;
}
function handleDeselect() {
selectedIndex = null;
}
function calculateZoom() {
if (!containerRef) return;
const container = containerRef;
const padding = 64; // 8 * 2 (p-8 on each side)
const containerWidth = container.clientWidth - padding;
const containerHeight = container.clientHeight - padding;
const pageWidth = pageData.img_width;
const pageHeight = pageData.img_height;
let newScale = 1;
switch (zoomMode) {
case 'fit-screen': {
const scaleX = containerWidth / pageWidth;
const scaleY = containerHeight / pageHeight;
newScale = Math.min(scaleX, scaleY);
break;
}
case 'fit-width': {
newScale = containerWidth / pageWidth;
break;
}
case 'original': {
newScale = 1;
break;
}
}
scale = newScale;
}
onMount(() => {
calculateZoom();
window.addEventListener('resize', calculateZoom);
return () => {
window.removeEventListener('resize', calculateZoom);
};
});
$effect(() => {
// Recalculate when zoom mode changes
zoomMode;
calculateZoom();
});
</script>
<div
bind:this={containerRef}
class="fixed top-16 right-0 bottom-0 left-0 flex items-center justify-center overflow-auto bg-gray-900 p-8"
>
<div
class="relative"
style:width="{pageData.img_width}px"
style:height="{pageData.img_height}px"
style:transform="scale({scale})"
style:transform-origin="center center"
>
<!-- Page Image -->
<img
src={pageImage}
alt="Manga page"
class="pointer-events-none absolute top-0 left-0 h-full w-full object-contain select-none"
draggable="false"
/>
<!-- Editable Text Boxes -->
{#each workingBlocks as block, index (index)}
<EditableBox
{block}
{index}
isSelected={selectedIndex === index}
imgWidth={pageData.img_width}
imgHeight={pageData.img_height}
onSelect={() => handleSelect(index)}
onUpdate={(updatedBlock) => updateBlock(index, updatedBlock)}
onDelete={() => deleteBlock(index)}
onClone={() => cloneBlock(index)}
bind:selectedIndex
/>
{/each}
<!-- Click outside to deselect -->
<button class="absolute inset-0 -z-10" onclick={handleDeselect} aria-label="Deselect box"
></button>
</div>
</div>