@gradio/imageeditor 0.4.11 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/ImageEditor.stories.svelte +6 -2
- package/Index.svelte +42 -13
- package/package.json +11 -8
- package/shared/Controls.svelte +2 -2
- package/shared/ImageEditor.svelte +35 -12
- package/shared/InteractiveImageEditor.svelte +97 -20
- package/shared/layers/Layers.svelte +113 -56
- package/shared/layers/utils.ts +44 -4
- package/shared/tools/Brush.svelte +42 -51
- package/shared/tools/{BrushColor.svelte → BrushOptions.svelte} +73 -32
- package/shared/tools/BrushSize.svelte +13 -20
- package/shared/tools/ColorSwatch.svelte +66 -34
- package/shared/tools/Crop.svelte +12 -17
- package/shared/tools/Cropper.svelte +2 -0
- package/shared/tools/Handle.svelte +1 -1
- package/shared/tools/Sources.svelte +45 -14
- package/shared/tools/Tools.svelte +42 -57
- package/shared/tools/crop.ts +9 -4
- package/shared/utils/events.ts +2 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export let selected_color: string;
|
|
5
5
|
export let colors: string[];
|
|
6
6
|
export let user_colors: (string | null)[] | null = [];
|
|
7
|
+
import { Palette } from "@gradio/icons";
|
|
7
8
|
|
|
8
9
|
export let show_empty = false;
|
|
9
10
|
export let current_mode: "hex" | "rgb" | "hsl" = "hex";
|
|
@@ -50,44 +51,75 @@
|
|
|
50
51
|
}
|
|
51
52
|
</script>
|
|
52
53
|
|
|
53
|
-
{#if
|
|
54
|
-
<
|
|
55
|
-
{#each user_colors as color, i}
|
|
56
|
-
<button
|
|
57
|
-
on:click={() => handle_select("edit", { index: i, color })}
|
|
58
|
-
class="color"
|
|
59
|
-
class:empty={color === null}
|
|
60
|
-
style="background-color: {color}"
|
|
61
|
-
class:selected={`edit-${i}` === current_index}
|
|
62
|
-
></button>
|
|
63
|
-
{/each}
|
|
64
|
-
|
|
65
|
-
<button
|
|
66
|
-
on:click={handle_picker_click}
|
|
67
|
-
class="color colorpicker"
|
|
68
|
-
class:hidden={!color_picker}
|
|
69
|
-
></button>
|
|
70
|
-
</div>
|
|
54
|
+
{#if !color_picker}
|
|
55
|
+
<span class:lg={user_colors}></span>
|
|
71
56
|
{/if}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
57
|
+
|
|
58
|
+
<div class="swatch-wrap">
|
|
59
|
+
<span class="icon-wrap">
|
|
60
|
+
<Palette />
|
|
61
|
+
</span>
|
|
62
|
+
<div>
|
|
63
|
+
{#if user_colors}
|
|
64
|
+
<div class="swatch">
|
|
65
|
+
{#each user_colors as color, i}
|
|
66
|
+
<button
|
|
67
|
+
on:click={() => handle_select("edit", { index: i, color })}
|
|
68
|
+
class="color"
|
|
69
|
+
class:empty={color === null}
|
|
70
|
+
style="background-color: {color}"
|
|
71
|
+
class:selected={`edit-${i}` === current_index}
|
|
72
|
+
></button>
|
|
73
|
+
{/each}
|
|
74
|
+
|
|
75
|
+
<button
|
|
76
|
+
on:click={handle_picker_click}
|
|
77
|
+
class="color colorpicker"
|
|
78
|
+
class:hidden={!color_picker}
|
|
79
|
+
></button>
|
|
80
|
+
</div>
|
|
81
|
+
{/if}
|
|
82
|
+
<menu class="swatch">
|
|
83
|
+
{#each _colors as color, i}
|
|
84
|
+
<button
|
|
85
|
+
on:click={() => handle_select("select", { index: i, color })}
|
|
86
|
+
class="color"
|
|
87
|
+
class:empty={color === null}
|
|
88
|
+
style="background-color: {color}"
|
|
89
|
+
class:selected={`select-${i}` === current_index}
|
|
90
|
+
></button>
|
|
91
|
+
{/each}
|
|
92
|
+
</menu>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
83
95
|
|
|
84
96
|
<style>
|
|
97
|
+
.icon-wrap {
|
|
98
|
+
width: 18px;
|
|
99
|
+
margin-top: 6px;
|
|
100
|
+
|
|
101
|
+
margin-left: var(--size-4);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.swatch-wrap {
|
|
105
|
+
display: flex;
|
|
106
|
+
justify-content: space-between;
|
|
107
|
+
align-items: flex-start;
|
|
108
|
+
}
|
|
109
|
+
span {
|
|
110
|
+
margin-top: 0px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
span.lg {
|
|
114
|
+
margin-top: var(--spacing-xl);
|
|
115
|
+
}
|
|
85
116
|
.swatch {
|
|
86
117
|
display: flex;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
margin-
|
|
118
|
+
gap: var(--size-2-5);
|
|
119
|
+
justify-content: space-around;
|
|
120
|
+
margin-bottom: var(--size-2);
|
|
121
|
+
margin-right: var(--size-6);
|
|
122
|
+
padding-left: var(--size-2);
|
|
91
123
|
}
|
|
92
124
|
|
|
93
125
|
.empty {
|
|
@@ -96,7 +128,7 @@
|
|
|
96
128
|
display: flex;
|
|
97
129
|
justify-content: center;
|
|
98
130
|
align-items: center;
|
|
99
|
-
padding-top:
|
|
131
|
+
padding-top: 4px;
|
|
100
132
|
text-align: center;
|
|
101
133
|
font-size: var(--scale-0);
|
|
102
134
|
cursor: pointer;
|
package/shared/tools/Crop.svelte
CHANGED
|
@@ -68,14 +68,17 @@
|
|
|
68
68
|
t_p = y_offset / $editor_box.child_height;
|
|
69
69
|
|
|
70
70
|
c = crop_canvas($pixi!.renderer, $pixi!.mask_container, crop, 0.2);
|
|
71
|
-
c.start(...$dimensions, current_crop);
|
|
72
|
-
|
|
73
|
-
c.continue(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
c.start(...$dimensions, current_crop, false);
|
|
72
|
+
|
|
73
|
+
c.continue(
|
|
74
|
+
[
|
|
75
|
+
l_p * $dimensions[0],
|
|
76
|
+
t_p * $dimensions[1],
|
|
77
|
+
w_p * $dimensions[0],
|
|
78
|
+
h_p * $dimensions[1]
|
|
79
|
+
],
|
|
80
|
+
false
|
|
81
|
+
);
|
|
79
82
|
c.stop();
|
|
80
83
|
|
|
81
84
|
c.execute();
|
|
@@ -167,12 +170,7 @@
|
|
|
167
170
|
|
|
168
171
|
$: $editor_box && get_measurements();
|
|
169
172
|
|
|
170
|
-
onMount(() =>
|
|
171
|
-
register_tool("crop", {
|
|
172
|
-
default: "crop",
|
|
173
|
-
options: []
|
|
174
|
-
})
|
|
175
|
-
);
|
|
173
|
+
onMount(() => register_tool("crop"));
|
|
176
174
|
</script>
|
|
177
175
|
|
|
178
176
|
{#if $active_tool === "crop" && measured}
|
|
@@ -188,6 +186,3 @@
|
|
|
188
186
|
on:crop_end={({ detail }) => handle_crop("stop", detail)}
|
|
189
187
|
/>
|
|
190
188
|
{/if}
|
|
191
|
-
|
|
192
|
-
<style>
|
|
193
|
-
</style>
|
|
@@ -332,6 +332,7 @@
|
|
|
332
332
|
left: 0;
|
|
333
333
|
width: 100%;
|
|
334
334
|
height: 100%;
|
|
335
|
+
|
|
335
336
|
/* transform: translateY(-20px); */
|
|
336
337
|
}
|
|
337
338
|
|
|
@@ -340,5 +341,6 @@
|
|
|
340
341
|
width: 100%;
|
|
341
342
|
height: 100%;
|
|
342
343
|
border: 1px solid black;
|
|
344
|
+
z-index: var(--layer-top);
|
|
343
345
|
}
|
|
344
346
|
</style>
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { getContext, onMount, tick } from "svelte";
|
|
2
|
+
import { getContext, onMount, tick, createEventDispatcher } from "svelte";
|
|
3
3
|
import { type ToolContext, TOOL_KEY } from "./Tools.svelte";
|
|
4
4
|
import { type EditorContext, EDITOR_KEY } from "../ImageEditor.svelte";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
Image as ImageIcon,
|
|
7
7
|
Webcam as WebcamIcon,
|
|
8
8
|
ImagePaste
|
|
9
9
|
} from "@gradio/icons";
|
|
10
10
|
import { Upload } from "@gradio/upload";
|
|
11
11
|
import { Webcam } from "@gradio/image";
|
|
12
12
|
import { type I18nFormatter } from "@gradio/utils";
|
|
13
|
+
import { IconButton } from "@gradio/atoms";
|
|
13
14
|
|
|
14
15
|
import { add_bg_color, add_bg_image } from "./sources";
|
|
15
16
|
import type { FileData } from "@gradio/client";
|
|
@@ -24,21 +25,27 @@
|
|
|
24
25
|
export let mirror_webcam = true;
|
|
25
26
|
export let i18n: I18nFormatter;
|
|
26
27
|
|
|
27
|
-
const { active_tool
|
|
28
|
+
const { active_tool } = getContext<ToolContext>(TOOL_KEY);
|
|
28
29
|
const { pixi, dimensions, register_context, reset, editor_box } =
|
|
29
30
|
getContext<EditorContext>(EDITOR_KEY);
|
|
30
31
|
|
|
31
|
-
let active_mode: "webcam" | "color" | null = null;
|
|
32
|
+
export let active_mode: "webcam" | "color" | null = null;
|
|
32
33
|
let background: Blob | File | null;
|
|
33
34
|
|
|
35
|
+
const dispatch = createEventDispatcher<{
|
|
36
|
+
upload: never;
|
|
37
|
+
}>();
|
|
38
|
+
|
|
34
39
|
const sources_meta = {
|
|
35
40
|
upload: {
|
|
36
|
-
icon:
|
|
41
|
+
icon: ImageIcon,
|
|
37
42
|
label: "Upload",
|
|
38
43
|
order: 0,
|
|
39
44
|
id: "bg_upload",
|
|
40
45
|
cb() {
|
|
41
46
|
upload.open_file_upload();
|
|
47
|
+
|
|
48
|
+
$active_tool = "bg";
|
|
42
49
|
}
|
|
43
50
|
},
|
|
44
51
|
webcam: {
|
|
@@ -48,6 +55,7 @@
|
|
|
48
55
|
id: "bg_webcam",
|
|
49
56
|
cb() {
|
|
50
57
|
active_mode = "webcam";
|
|
58
|
+
$active_tool = "bg";
|
|
51
59
|
}
|
|
52
60
|
},
|
|
53
61
|
clipboard: {
|
|
@@ -57,6 +65,7 @@
|
|
|
57
65
|
id: "bg_clipboard",
|
|
58
66
|
cb() {
|
|
59
67
|
process_clipboard();
|
|
68
|
+
$active_tool = null;
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
} as const;
|
|
@@ -148,18 +157,25 @@
|
|
|
148
157
|
},
|
|
149
158
|
reset_fn: () => {}
|
|
150
159
|
});
|
|
151
|
-
|
|
152
|
-
onMount(() => {
|
|
153
|
-
return register_tool("bg", {
|
|
154
|
-
default: "bg_upload",
|
|
155
|
-
options: sources_list || []
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
160
|
</script>
|
|
159
161
|
|
|
160
162
|
<svelte:window on:keydown={handle_key} />
|
|
161
163
|
|
|
162
|
-
{#if
|
|
164
|
+
{#if sources.length}
|
|
165
|
+
<div class="source-wrap">
|
|
166
|
+
{#each sources_list as { icon, label, id, cb } (id)}
|
|
167
|
+
<IconButton
|
|
168
|
+
Icon={icon}
|
|
169
|
+
size="medium"
|
|
170
|
+
padded={false}
|
|
171
|
+
label={label + " button"}
|
|
172
|
+
hasPopup={true}
|
|
173
|
+
transparent={true}
|
|
174
|
+
on:click={cb}
|
|
175
|
+
/>
|
|
176
|
+
{/each}
|
|
177
|
+
<span class="sep"></span>
|
|
178
|
+
</div>
|
|
163
179
|
<div class="upload-container">
|
|
164
180
|
<Upload
|
|
165
181
|
hidden={true}
|
|
@@ -203,7 +219,6 @@
|
|
|
203
219
|
width: 100%;
|
|
204
220
|
left: 0;
|
|
205
221
|
right: 0;
|
|
206
|
-
background-color: rgba(0, 0, 0, 0.9);
|
|
207
222
|
margin: auto;
|
|
208
223
|
z-index: var(--layer-top);
|
|
209
224
|
display: flex;
|
|
@@ -213,4 +228,20 @@
|
|
|
213
228
|
.modal-inner {
|
|
214
229
|
width: 100%;
|
|
215
230
|
}
|
|
231
|
+
|
|
232
|
+
.sep {
|
|
233
|
+
height: 12px;
|
|
234
|
+
background-color: var(--block-border-color);
|
|
235
|
+
width: 1px;
|
|
236
|
+
display: block;
|
|
237
|
+
margin-left: var(--spacing-xl);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.source-wrap {
|
|
241
|
+
display: flex;
|
|
242
|
+
justify-content: center;
|
|
243
|
+
align-items: center;
|
|
244
|
+
margin-left: var(--spacing-lg);
|
|
245
|
+
height: 100%;
|
|
246
|
+
}
|
|
216
247
|
</style>
|
|
@@ -28,18 +28,16 @@
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export interface ToolContext {
|
|
31
|
-
register_tool: (type: tool,
|
|
31
|
+
register_tool: (type: tool, opts?: { cb: () => void }) => () => void;
|
|
32
32
|
active_tool: {
|
|
33
|
+
set: (tool: tool) => void;
|
|
33
34
|
subscribe(
|
|
34
35
|
this: void,
|
|
35
36
|
run: Subscriber<tool | null>,
|
|
36
37
|
invalidate?: Invalidator<tool | null>
|
|
37
38
|
): Unsubscriber;
|
|
38
39
|
};
|
|
39
|
-
|
|
40
|
-
sub_tool: upload_tool | transform_tool | brush_tool | eraser_tool | null,
|
|
41
|
-
cb?: (...args: any[]) => void
|
|
42
|
-
) => void;
|
|
40
|
+
|
|
43
41
|
current_color: Writable<string>;
|
|
44
42
|
}
|
|
45
43
|
</script>
|
|
@@ -52,28 +50,22 @@
|
|
|
52
50
|
import { Image, Crop, Brush, Erase } from "@gradio/icons";
|
|
53
51
|
import { type I18nFormatter } from "@gradio/utils";
|
|
54
52
|
|
|
55
|
-
const {
|
|
53
|
+
const { active_tool, toolbar_box, editor_box } =
|
|
56
54
|
getContext<EditorContext>(EDITOR_KEY);
|
|
57
55
|
|
|
58
56
|
export let i18n: I18nFormatter;
|
|
59
57
|
|
|
60
58
|
let tools: tool[] = [];
|
|
61
59
|
|
|
62
|
-
const
|
|
63
|
-
draw: null,
|
|
64
|
-
erase: null,
|
|
65
|
-
crop: null,
|
|
66
|
-
bg: null
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
$: sub_menu = $active_tool && metas[$active_tool];
|
|
60
|
+
const cbs: Record<string, () => void> = {};
|
|
70
61
|
let current_color = writable("#000000");
|
|
71
|
-
let sub_tool: upload_tool | transform_tool | brush_tool | eraser_tool | null;
|
|
72
62
|
const tool_context: ToolContext = {
|
|
73
63
|
current_color,
|
|
74
|
-
register_tool: (type: tool,
|
|
64
|
+
register_tool: (type: tool, opts?: { cb: () => void }) => {
|
|
75
65
|
tools = [...tools, type];
|
|
76
|
-
|
|
66
|
+
if (opts?.cb) {
|
|
67
|
+
cbs[type] = opts.cb;
|
|
68
|
+
}
|
|
77
69
|
|
|
78
70
|
return () => {
|
|
79
71
|
tools = tools.filter((tool) => tool !== type);
|
|
@@ -81,15 +73,8 @@
|
|
|
81
73
|
},
|
|
82
74
|
|
|
83
75
|
active_tool: {
|
|
84
|
-
subscribe: active_tool.subscribe
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
activate_subtool: (
|
|
88
|
-
_sub_tool: upload_tool | transform_tool | brush_tool | eraser_tool | null,
|
|
89
|
-
cb?: (...args: any[]) => void
|
|
90
|
-
) => {
|
|
91
|
-
sub_tool = _sub_tool;
|
|
92
|
-
if (cb) cb();
|
|
76
|
+
subscribe: active_tool.subscribe,
|
|
77
|
+
set: active_tool.set
|
|
93
78
|
}
|
|
94
79
|
};
|
|
95
80
|
|
|
@@ -103,11 +88,6 @@
|
|
|
103
88
|
icon: typeof Image;
|
|
104
89
|
}
|
|
105
90
|
> = {
|
|
106
|
-
bg: {
|
|
107
|
-
order: 0,
|
|
108
|
-
label: i18n("Image"),
|
|
109
|
-
icon: Image
|
|
110
|
-
},
|
|
111
91
|
crop: {
|
|
112
92
|
order: 1,
|
|
113
93
|
label: i18n("Transform"),
|
|
@@ -122,44 +102,49 @@
|
|
|
122
102
|
order: 2,
|
|
123
103
|
label: i18n("Erase"),
|
|
124
104
|
icon: Erase
|
|
105
|
+
},
|
|
106
|
+
bg: {
|
|
107
|
+
order: 0,
|
|
108
|
+
label: i18n("Background"),
|
|
109
|
+
icon: Image
|
|
125
110
|
}
|
|
126
111
|
} as const;
|
|
112
|
+
|
|
113
|
+
let toolbar_width: number;
|
|
114
|
+
let toolbar_wrap: HTMLDivElement;
|
|
115
|
+
|
|
116
|
+
$: toolbar_width, $editor_box, get_dimensions();
|
|
117
|
+
|
|
118
|
+
function get_dimensions(): void {
|
|
119
|
+
if (!toolbar_wrap) return;
|
|
120
|
+
$toolbar_box = toolbar_wrap.getBoundingClientRect();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function handle_click(e: Event, tool: tool): void {
|
|
124
|
+
e.stopPropagation();
|
|
125
|
+
$active_tool = tool;
|
|
126
|
+
cbs[tool] && cbs[tool]();
|
|
127
|
+
}
|
|
127
128
|
</script>
|
|
128
129
|
|
|
129
130
|
<slot />
|
|
130
131
|
|
|
131
|
-
<div
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
highlight={sub_tool === meta.id && meta.id !== "brush_size"}
|
|
137
|
-
color={$active_tool === "draw" && meta.id === "brush_size"
|
|
138
|
-
? $current_color
|
|
139
|
-
: undefined}
|
|
140
|
-
on:click={() => tool_context.activate_subtool(meta.id, meta.cb)}
|
|
141
|
-
Icon={meta.icon}
|
|
142
|
-
size="large"
|
|
143
|
-
padded={false}
|
|
144
|
-
label={meta.label + " button"}
|
|
145
|
-
hasPopup={true}
|
|
146
|
-
transparent={true}
|
|
147
|
-
/>
|
|
148
|
-
{/each}
|
|
149
|
-
{/if}
|
|
150
|
-
</Toolbar>
|
|
151
|
-
|
|
132
|
+
<div
|
|
133
|
+
class="toolbar-wrap"
|
|
134
|
+
bind:clientWidth={toolbar_width}
|
|
135
|
+
bind:this={toolbar_wrap}
|
|
136
|
+
>
|
|
152
137
|
<Toolbar show_border={false}>
|
|
153
138
|
{#each tools as tool (tool)}
|
|
154
139
|
<IconButton
|
|
155
|
-
disabled={tool === "bg" && !!$current_history.previous}
|
|
156
140
|
highlight={$active_tool === tool}
|
|
157
|
-
on:click={() => (
|
|
141
|
+
on:click={(e) => handle_click(e, tool)}
|
|
158
142
|
Icon={tools_meta[tool].icon}
|
|
159
|
-
size="
|
|
143
|
+
size="medium"
|
|
160
144
|
padded={false}
|
|
161
145
|
label={tools_meta[tool].label + " button"}
|
|
162
146
|
transparent={true}
|
|
147
|
+
offset={tool === "draw" ? -2 : tool === "erase" ? -6 : 0}
|
|
163
148
|
/>
|
|
164
149
|
{/each}
|
|
165
150
|
</Toolbar>
|
|
@@ -168,9 +153,9 @@
|
|
|
168
153
|
<style>
|
|
169
154
|
.toolbar-wrap {
|
|
170
155
|
display: flex;
|
|
171
|
-
flex-direction: column;
|
|
172
156
|
justify-content: center;
|
|
173
157
|
align-items: center;
|
|
174
|
-
|
|
158
|
+
margin-left: var(--spacing-xl);
|
|
159
|
+
height: 100%;
|
|
175
160
|
}
|
|
176
161
|
</style>
|
package/shared/tools/crop.ts
CHANGED
|
@@ -14,10 +14,14 @@ export interface CropCommand extends Command {
|
|
|
14
14
|
width: number,
|
|
15
15
|
height: number,
|
|
16
16
|
previous_crop: [number, number, number, number],
|
|
17
|
+
preview?: boolean,
|
|
17
18
|
set_previous?: boolean
|
|
18
19
|
) => void;
|
|
19
20
|
stop: () => number;
|
|
20
|
-
continue: (
|
|
21
|
+
continue: (
|
|
22
|
+
crop_size: [number, number, number, number],
|
|
23
|
+
preview?: boolean
|
|
24
|
+
) => void;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export function crop_canvas(
|
|
@@ -74,6 +78,7 @@ export function crop_canvas(
|
|
|
74
78
|
_width: number,
|
|
75
79
|
_height: number,
|
|
76
80
|
_previous_crop: [number, number, number, number],
|
|
81
|
+
_preview = true,
|
|
77
82
|
set_previous = true
|
|
78
83
|
) => {
|
|
79
84
|
clean = false;
|
|
@@ -82,7 +87,7 @@ export function crop_canvas(
|
|
|
82
87
|
height: _height
|
|
83
88
|
});
|
|
84
89
|
|
|
85
|
-
crop_mask(_width, _height, _previous_crop,
|
|
90
|
+
crop_mask(_width, _height, _previous_crop, _preview);
|
|
86
91
|
sprite = new Sprite(text);
|
|
87
92
|
mask_container.mask = sprite;
|
|
88
93
|
width = _width;
|
|
@@ -90,10 +95,10 @@ export function crop_canvas(
|
|
|
90
95
|
if (set_previous)
|
|
91
96
|
previous_crop = JSON.parse(JSON.stringify(_previous_crop));
|
|
92
97
|
},
|
|
93
|
-
continue: (crop_size: [number, number, number, number]) => {
|
|
98
|
+
continue: (crop_size: [number, number, number, number], preview = true) => {
|
|
94
99
|
final_crop = JSON.parse(JSON.stringify(crop_size));
|
|
95
100
|
if (spring_value === 0.2) {
|
|
96
|
-
crop_mask(width, height, final_crop,
|
|
101
|
+
crop_mask(width, height, final_crop, preview);
|
|
97
102
|
} else {
|
|
98
103
|
alpha_spring.set(0.2);
|
|
99
104
|
}
|