fragment-tools 0.2.11 → 0.2.13
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/package.json +12 -11
- package/src/cli/build.js +1 -0
- package/src/cli/create.js +22 -4
- package/src/cli/createConfig.js +2 -2
- package/src/cli/getEntries.js +10 -1
- package/src/cli/plugins/hot-shader-replacement.js +54 -16
- package/src/cli/plugins/save.js +97 -38
- package/src/cli/prompts.js +89 -36
- package/src/cli/run.js +1 -1
- package/src/cli/templates/blank/index.ts +1 -1
- package/src/cli/templates/default/index.js +10 -2
- package/src/cli/templates/default/index.ts +5 -2
- package/src/cli/templates/fragment-gl/index.ts +1 -1
- package/src/cli/templates/p5/index.ts +1 -1
- package/src/cli/templates/p5-webgl/index.ts +1 -1
- package/src/cli/templates/three-fragment/index.js +5 -3
- package/src/cli/templates/three-fragment/index.ts +5 -4
- package/src/cli/templates/three-orthographic/index.js +6 -1
- package/src/cli/templates/three-orthographic/index.ts +6 -2
- package/src/cli/templates/three-perspective/index.js +6 -1
- package/src/cli/templates/three-perspective/index.ts +6 -2
- package/src/client/app/actions/resize.js +8 -1
- package/src/client/app/attachments/draggable.js +93 -0
- package/src/client/app/client.js +90 -18
- package/src/client/app/components/IconFlip.svelte +46 -0
- package/src/client/app/hooks.js +25 -1
- package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +95 -3
- package/src/client/app/lib/canvas-recorder/FrameRecorder.js +45 -3
- package/src/client/app/lib/canvas-recorder/GIFRecorder.js +72 -13
- package/src/client/app/lib/canvas-recorder/MediaBunnyRecorder.js +43 -9
- package/src/client/app/lib/canvas-recorder/utils.js +18 -9
- package/src/client/app/modules/Params.svelte +1 -0
- package/src/client/app/renderers/2DRenderer.js +20 -16
- package/src/client/app/renderers/P5GLRenderer.js +13 -5
- package/src/client/app/renderers/P5Renderer.js +9 -1
- package/src/client/app/renderers/THREERenderer.js +63 -48
- package/src/client/app/state/Sketch.svelte.js +150 -10
- package/src/client/app/state/errors.svelte.js +19 -0
- package/src/client/app/state/exports.svelte.js +14 -1
- package/src/client/app/state/rendering.svelte.js +90 -13
- package/src/client/app/state/sketches.svelte.js +43 -7
- package/src/client/app/state/utils.svelte.js +49 -0
- package/src/client/app/ui/Field.svelte +63 -16
- package/src/client/app/ui/FieldSection.svelte +4 -4
- package/src/client/app/ui/ParamsOutput.svelte +7 -5
- package/src/client/app/ui/SketchRenderer.svelte +21 -0
- package/src/client/app/ui/fields/ButtonInput.svelte +2 -0
- package/src/client/app/ui/fields/CheckboxInput.svelte +13 -11
- package/src/client/app/ui/fields/ColorInput.svelte +16 -11
- package/src/client/app/ui/fields/GradientInput.svelte +607 -0
- package/src/client/app/ui/fields/Input.svelte +10 -6
- package/src/client/app/ui/fields/IntervalInput.svelte +27 -35
- package/src/client/app/ui/fields/NumberInput.svelte +51 -13
- package/src/client/app/ui/fields/PaletteInput.svelte +181 -0
- package/src/client/app/ui/fields/ProgressInput.svelte +44 -16
- package/src/client/app/ui/fields/TextareaInput.svelte +93 -0
- package/src/client/app/utils/canvas.utils.js +105 -28
- package/src/client/app/utils/color.utils.js +74 -17
- package/src/client/app/utils/fields.utils.js +70 -17
- package/src/client/app/utils/file.utils.js +86 -31
- package/src/client/app/utils/glsl.utils.js +11 -2
- package/src/client/app/utils/glslErrors.js +31 -21
- package/src/client/app/utils/index.js +28 -12
- package/src/client/main.js +7 -1
- package/src/types/global.d.ts +143 -0
- package/src/types/props.d.ts +41 -15
- package/tsconfig.json +1 -1
package/src/cli/prompts.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
1
2
|
import { ConfirmPrompt, SelectPrompt, TextPrompt } from '@clack/core';
|
|
2
3
|
import isUnicodeSupported from 'is-unicode-supported';
|
|
3
4
|
import * as color from 'kleur/colors';
|
|
@@ -23,6 +24,7 @@ const S_BAR = s('│', '|');
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* @param {object} opts
|
|
27
|
+
* @param {string} opts.message
|
|
26
28
|
* @param {string} opts.active
|
|
27
29
|
* @param {string} opts.inactive
|
|
28
30
|
* @param {boolean} opts.initialValue
|
|
@@ -67,9 +69,9 @@ export const confirm = (opts) => {
|
|
|
67
69
|
* @param {string} [opts.placeholder]
|
|
68
70
|
* @param {string} [opts.defaultValue]
|
|
69
71
|
* @param {string} [opts.initialValue]
|
|
70
|
-
* @param {string} [opts.
|
|
71
|
-
* @param {
|
|
72
|
-
* @returns {Promise<string|symbol>}
|
|
72
|
+
* @param {string} [opts.hint]
|
|
73
|
+
* @param {(value: string | undefined) => any} [opts.validate]
|
|
74
|
+
* @returns {Promise<string | symbol | undefined>}
|
|
73
75
|
*/
|
|
74
76
|
export const text = (opts) => {
|
|
75
77
|
const { hint = '' } = opts;
|
|
@@ -82,24 +84,36 @@ export const text = (opts) => {
|
|
|
82
84
|
render() {
|
|
83
85
|
const title = `${opts.message}\n`;
|
|
84
86
|
const placeholder = opts.placeholder
|
|
85
|
-
?
|
|
86
|
-
|
|
87
|
-
:
|
|
88
|
-
const
|
|
89
|
-
? `${placeholder} ${
|
|
90
|
-
: this.
|
|
87
|
+
? styleText('inverse', opts.placeholder[0]) +
|
|
88
|
+
styleText('dim', opts.placeholder.slice(1))
|
|
89
|
+
: styleText(['inverse', 'hidden'], '_');
|
|
90
|
+
const userInput = !this.userInput
|
|
91
|
+
? `${placeholder} ${styleText('dim', hint)}`.trim()
|
|
92
|
+
: this.userInputWithCursor;
|
|
93
|
+
const value = this.value ?? '';
|
|
91
94
|
|
|
92
95
|
switch (this.state) {
|
|
93
|
-
case 'error':
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
case 'error': {
|
|
97
|
+
const errorText = this.error
|
|
98
|
+
? ` ${styleText('yellow', this.error)}`
|
|
99
|
+
: '';
|
|
100
|
+
return `${title.trim()}\n${userInput}\n${errorText}\n`;
|
|
101
|
+
}
|
|
102
|
+
case 'submit': {
|
|
103
|
+
const valueText = value
|
|
104
|
+
? `${styleText('green', value)}`
|
|
105
|
+
: '';
|
|
106
|
+
return `${styleText('dim', `${title}`)} ${valueText}\n`;
|
|
107
|
+
}
|
|
108
|
+
case 'cancel': {
|
|
109
|
+
const valueText = value
|
|
110
|
+
? ` ${styleText(['strikethrough', 'dim'], value)}`
|
|
111
|
+
: '';
|
|
112
|
+
return `${title}${valueText}${value.trim() ? `\n` : ''}`;
|
|
113
|
+
}
|
|
114
|
+
default: {
|
|
115
|
+
return `${title}${userInput}\n\n`;
|
|
116
|
+
}
|
|
103
117
|
}
|
|
104
118
|
},
|
|
105
119
|
}).prompt();
|
|
@@ -140,6 +154,22 @@ const limitOptions = (params) => {
|
|
|
140
154
|
});
|
|
141
155
|
};
|
|
142
156
|
|
|
157
|
+
/**
|
|
158
|
+
*
|
|
159
|
+
* @param {string} label
|
|
160
|
+
* @param {(text: string) => string} format
|
|
161
|
+
* @returns {string}
|
|
162
|
+
*/
|
|
163
|
+
const computeLabel = (label, format) => {
|
|
164
|
+
if (!label.includes('\n')) {
|
|
165
|
+
return format(label);
|
|
166
|
+
}
|
|
167
|
+
return label
|
|
168
|
+
.split('\n')
|
|
169
|
+
.map((line) => format(line))
|
|
170
|
+
.join('\n');
|
|
171
|
+
};
|
|
172
|
+
|
|
143
173
|
/**
|
|
144
174
|
* @typedef {string|boolean|number} Value
|
|
145
175
|
*/
|
|
@@ -149,12 +179,13 @@ const limitOptions = (params) => {
|
|
|
149
179
|
* @property {string} value
|
|
150
180
|
* @property {string} [label]
|
|
151
181
|
* @property {string} [hint]
|
|
182
|
+
* @property {boolean} [disabled]
|
|
152
183
|
*/
|
|
153
184
|
|
|
154
185
|
/**
|
|
155
186
|
* @param {object} opts
|
|
156
187
|
* @param {string} opts.message
|
|
157
|
-
* @param {
|
|
188
|
+
* @param {import('@clack/core').SelectOptions<Value>} opts.options
|
|
158
189
|
* @param {Value} [opts.initialValue]
|
|
159
190
|
* @param {number} [opts.maxItems]
|
|
160
191
|
* @returns {Promise<boolean|symbol>}
|
|
@@ -162,47 +193,69 @@ const limitOptions = (params) => {
|
|
|
162
193
|
export const select = (opts) => {
|
|
163
194
|
/**
|
|
164
195
|
*
|
|
165
|
-
* @param {Option
|
|
166
|
-
* @param {'inactive' | 'active' | 'selected' | 'cancelled'} state
|
|
196
|
+
* @param {Option} option
|
|
197
|
+
* @param {'inactive' | 'active' | 'selected' | 'cancelled' | 'disabled'} state
|
|
167
198
|
* @returns {string}
|
|
168
199
|
*/
|
|
169
200
|
const opt = (option, state) => {
|
|
170
201
|
const label = option.label ?? String(option.value);
|
|
171
202
|
switch (state) {
|
|
203
|
+
case 'disabled':
|
|
204
|
+
return `${styleText('gray', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('gray', text))}${
|
|
205
|
+
option.hint
|
|
206
|
+
? ` ${styleText('dim', `(${option.hint ?? 'disabled'})`)}`
|
|
207
|
+
: ''
|
|
208
|
+
}`;
|
|
172
209
|
case 'selected':
|
|
173
|
-
return `${label}`;
|
|
210
|
+
return `${computeLabel(label, (text) => text)}`;
|
|
174
211
|
case 'active':
|
|
175
|
-
return `${
|
|
176
|
-
option.hint
|
|
212
|
+
return `${styleText('green', S_RADIO_ACTIVE)} ${label}${
|
|
213
|
+
option.hint
|
|
214
|
+
? ` ${styleText('dim', `(${option.hint})`)}`
|
|
215
|
+
: ''
|
|
177
216
|
}`;
|
|
178
217
|
case 'cancelled':
|
|
179
|
-
return `${
|
|
218
|
+
return `${computeLabel(label, (str) => styleText(['strikethrough', 'dim'], str))}`;
|
|
180
219
|
default:
|
|
181
|
-
return `${
|
|
220
|
+
return `${styleText('dim', S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;
|
|
182
221
|
}
|
|
183
222
|
};
|
|
184
223
|
|
|
185
224
|
return new SelectPrompt({
|
|
186
225
|
options: opts.options,
|
|
226
|
+
signal: opts.signal,
|
|
227
|
+
input: opts.input,
|
|
228
|
+
output: opts.output,
|
|
187
229
|
initialValue: opts.initialValue,
|
|
188
230
|
render() {
|
|
189
231
|
const title = `${opts.message}\n`;
|
|
190
232
|
|
|
191
233
|
switch (this.state) {
|
|
192
|
-
case 'submit':
|
|
193
|
-
return `${
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)}\n`;
|
|
234
|
+
case 'submit': {
|
|
235
|
+
return `${styleText('dim', title)} ${styleText('green', opt(this.options[this.cursor], 'selected'))}\n`;
|
|
236
|
+
}
|
|
237
|
+
case 'cancel': {
|
|
238
|
+
return `${title}${opt(this.options[this.cursor], 'cancelled')}`;
|
|
239
|
+
}
|
|
199
240
|
default: {
|
|
200
|
-
|
|
241
|
+
const titleLineCount = title.split('\n').length;
|
|
242
|
+
const footerLineCount = 1;
|
|
243
|
+
return `${title}${limitOptions({
|
|
244
|
+
output: opts.output,
|
|
201
245
|
cursor: this.cursor,
|
|
202
246
|
options: this.options,
|
|
203
247
|
maxItems: opts.maxItems,
|
|
248
|
+
columnPadding: 0,
|
|
249
|
+
rowPadding: titleLineCount + footerLineCount,
|
|
204
250
|
style: (item, active) =>
|
|
205
|
-
opt(
|
|
251
|
+
opt(
|
|
252
|
+
item,
|
|
253
|
+
item.disabled
|
|
254
|
+
? 'disabled'
|
|
255
|
+
: active
|
|
256
|
+
? 'active'
|
|
257
|
+
: 'inactive',
|
|
258
|
+
),
|
|
206
259
|
}).join(`\n`)}\n`;
|
|
207
260
|
}
|
|
208
261
|
}
|
package/src/cli/run.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
FRAGMENT_DIRECTORY,
|
|
8
8
|
} from './createFragmentFile.js';
|
|
9
9
|
import { getEntries } from './getEntries.js';
|
|
10
|
-
import { log, magenta, bold, cyan
|
|
10
|
+
import { log, magenta, bold, cyan } from './log.js';
|
|
11
11
|
import save from './plugins/save.js';
|
|
12
12
|
import * as p from './prompts.js';
|
|
13
13
|
import { prettifyTime } from './utils.js';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
let resolution = { x: 0, y: 0 };
|
|
2
|
+
|
|
1
3
|
export const props = {};
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -24,8 +26,11 @@ export const init = ({ canvas, context, width, height }) => {};
|
|
|
24
26
|
* @param {number} params.playcount
|
|
25
27
|
*/
|
|
26
28
|
export const update = ({ context, width, height, pixelRatio }) => {
|
|
29
|
+
const w = width * pixelRatio;
|
|
30
|
+
const h = height * pixelRatio;
|
|
31
|
+
|
|
27
32
|
context.fillStyle = 'rgb(0, 255, 0)';
|
|
28
|
-
context.fillRect(0, 0,
|
|
33
|
+
context.fillRect(0, 0, w, h);
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
/**
|
|
@@ -35,6 +40,9 @@ export const update = ({ context, width, height, pixelRatio }) => {
|
|
|
35
40
|
* @param {number} params.height
|
|
36
41
|
* @param {number} params.pixelRatio
|
|
37
42
|
*/
|
|
38
|
-
export const resize = ({ width, height }) => {
|
|
43
|
+
export const resize = ({ width, height, pixelRatio }) => {
|
|
44
|
+
resolution.x = width * pixelRatio;
|
|
45
|
+
resolution.y = height * pixelRatio;
|
|
46
|
+
};
|
|
39
47
|
|
|
40
48
|
export const rendering = '2d';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
1
|
+
import type { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
2
2
|
import { defineProps } from '@fragment/types/utils';
|
|
3
3
|
|
|
4
4
|
export const props = defineProps({});
|
|
@@ -11,8 +11,11 @@ export const update: Update<'2d'> = ({
|
|
|
11
11
|
height,
|
|
12
12
|
pixelRatio,
|
|
13
13
|
}) => {
|
|
14
|
+
const w = width * pixelRatio;
|
|
15
|
+
const h = height * pixelRatio;
|
|
16
|
+
|
|
14
17
|
context.fillStyle = 'rgb(0, 255, 0)';
|
|
15
|
-
context.fillRect(0, 0,
|
|
18
|
+
context.fillRect(0, 0, w, h);
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
export const resize: Resize<'2d'> = ({}) => {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import p5, { type Shader } from 'p5';
|
|
2
2
|
|
|
3
|
-
import { Init, Rendering, Update } from '@fragment/types';
|
|
3
|
+
import type { Init, Rendering, Update } from '@fragment/types';
|
|
4
4
|
import { defineProps } from '@fragment/types/utils';
|
|
5
5
|
|
|
6
6
|
import fragmentShader from './fragment.fs';
|
|
@@ -6,9 +6,11 @@ import fragmentShader from './fragment.fs';
|
|
|
6
6
|
let scene;
|
|
7
7
|
/** @type {THREE.OrthographicCamera} */
|
|
8
8
|
let camera;
|
|
9
|
+
/** @type {THREE.Vector2} */
|
|
10
|
+
let resolution = new THREE.Vector2();
|
|
9
11
|
|
|
10
12
|
let uniforms = {
|
|
11
|
-
uResolution: { value:
|
|
13
|
+
uResolution: { value: resolution },
|
|
12
14
|
uTime: { value: 0 },
|
|
13
15
|
};
|
|
14
16
|
|
|
@@ -87,8 +89,8 @@ export const update = ({ renderer, time, deltaTime }) => {
|
|
|
87
89
|
* @param {number} params.pixelRatio
|
|
88
90
|
*/
|
|
89
91
|
export const resize = ({ width, height, pixelRatio }) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
resolution.x = width * pixelRatio;
|
|
93
|
+
resolution.y = height * pixelRatio;
|
|
92
94
|
|
|
93
95
|
camera.left = -width * 0.5;
|
|
94
96
|
camera.right = width * 0.5;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
|
|
3
|
-
import { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
3
|
+
import type { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
4
4
|
|
|
5
5
|
import fragmentShader from './fragment.fs';
|
|
6
6
|
|
|
7
7
|
let scene: THREE.Scene;
|
|
8
8
|
let camera: THREE.OrthographicCamera;
|
|
9
|
+
let resolution = new THREE.Vector2();
|
|
9
10
|
let uniforms = {
|
|
10
|
-
uResolution: { value:
|
|
11
|
+
uResolution: { value: resolution },
|
|
11
12
|
uTime: { value: 0 },
|
|
12
13
|
};
|
|
13
14
|
|
|
@@ -54,8 +55,8 @@ export const update: Update<'three'> = ({ renderer, time }) => {
|
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export const resize: Resize<'three'> = ({ width, height, pixelRatio }) => {
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
resolution.x = width * pixelRatio;
|
|
59
|
+
resolution.y = height * pixelRatio;
|
|
59
60
|
|
|
60
61
|
camera.left = -width * 0.5;
|
|
61
62
|
camera.right = width * 0.5;
|
|
@@ -4,6 +4,8 @@ import * as THREE from 'three';
|
|
|
4
4
|
let scene;
|
|
5
5
|
/** @type {THREE.OrthographicCamera} */
|
|
6
6
|
let camera;
|
|
7
|
+
/** @type {THREE.Vector2} */
|
|
8
|
+
let resolution = new THREE.Vector2();
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @param {object} params
|
|
@@ -49,7 +51,10 @@ export const update = ({ renderer, time, deltaTime }) => {
|
|
|
49
51
|
* @param {number} params.height
|
|
50
52
|
* @param {number} params.pixelRatio
|
|
51
53
|
*/
|
|
52
|
-
export const resize = ({ width, height }) => {
|
|
54
|
+
export const resize = ({ width, height, pixelRatio }) => {
|
|
55
|
+
resolution.x = width * pixelRatio;
|
|
56
|
+
resolution.y = height * pixelRatio;
|
|
57
|
+
|
|
53
58
|
camera.left = -width * 0.5;
|
|
54
59
|
camera.right = width * 0.5;
|
|
55
60
|
camera.top = height * 0.5;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
|
|
3
|
-
import { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
3
|
+
import type { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
4
4
|
|
|
5
5
|
let scene: THREE.Scene;
|
|
6
6
|
let camera: THREE.OrthographicCamera;
|
|
7
|
+
let resolution = new THREE.Vector2();
|
|
7
8
|
|
|
8
9
|
export const init: Init<'three'> = ({}) => {
|
|
9
10
|
scene = new THREE.Scene();
|
|
@@ -17,7 +18,10 @@ export const update: Update<'three'> = ({ renderer }) => {
|
|
|
17
18
|
renderer.render(scene, camera);
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
export const resize: Resize<'three'> = ({ width, height }) => {
|
|
21
|
+
export const resize: Resize<'three'> = ({ width, height, pixelRatio }) => {
|
|
22
|
+
resolution.x = width * pixelRatio;
|
|
23
|
+
resolution.y = height * pixelRatio;
|
|
24
|
+
|
|
21
25
|
camera.left = -width * 0.5;
|
|
22
26
|
camera.right = width * 0.5;
|
|
23
27
|
camera.top = height * 0.5;
|
|
@@ -4,6 +4,8 @@ import * as THREE from 'three';
|
|
|
4
4
|
let scene;
|
|
5
5
|
/** @type {THREE.OrthographicCamera} */
|
|
6
6
|
let camera;
|
|
7
|
+
/** @type {THREE.Vector2} */
|
|
8
|
+
let resolution = new THREE.Vector2();
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @param {object} params
|
|
@@ -50,7 +52,10 @@ export const update = ({ renderer, time, deltaTime }) => {
|
|
|
50
52
|
* @param {number} params.height
|
|
51
53
|
* @param {number} params.pixelRatio
|
|
52
54
|
*/
|
|
53
|
-
export const resize = ({ width, height }) => {
|
|
55
|
+
export const resize = ({ width, height, pixelRatio }) => {
|
|
56
|
+
resolution.x = width * pixelRatio;
|
|
57
|
+
resolution.y = height * pixelRatio;
|
|
58
|
+
|
|
54
59
|
camera.aspect = width / height;
|
|
55
60
|
camera.updateProjectionMatrix();
|
|
56
61
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
|
|
3
|
-
import { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
3
|
+
import type { Init, Rendering, Resize, Update } from '@fragment/types';
|
|
4
4
|
|
|
5
5
|
let scene: THREE.Scene;
|
|
6
6
|
let camera: THREE.PerspectiveCamera;
|
|
7
|
+
let resolution = new THREE.Vector2();
|
|
7
8
|
|
|
8
9
|
export const init: Init<'three'> = ({}) => {
|
|
9
10
|
scene = new THREE.Scene();
|
|
@@ -18,7 +19,10 @@ export const update: Update<'three'> = ({ renderer }) => {
|
|
|
18
19
|
renderer.render(scene, camera);
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
export const resize: Resize<'three'> = ({ width, height }) => {
|
|
22
|
+
export const resize: Resize<'three'> = ({ width, height, pixelRatio }) => {
|
|
23
|
+
resolution.x = width * pixelRatio;
|
|
24
|
+
resolution.y = height * pixelRatio;
|
|
25
|
+
|
|
22
26
|
camera.aspect = width / height;
|
|
23
27
|
camera.updateProjectionMatrix();
|
|
24
28
|
};
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {HTMLElement} node
|
|
4
|
+
* @param {() => void} callback
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
1
7
|
export function resize(node, callback) {
|
|
2
8
|
if (typeof callback !== 'function') return;
|
|
3
9
|
|
|
10
|
+
/** @type {ResizeObserver | null} */
|
|
4
11
|
let observer = new ResizeObserver(callback);
|
|
5
12
|
|
|
6
13
|
observer.observe(node);
|
|
7
14
|
|
|
8
15
|
return {
|
|
9
16
|
destroy: () => {
|
|
10
|
-
observer
|
|
17
|
+
observer?.disconnect();
|
|
11
18
|
observer = null;
|
|
12
19
|
},
|
|
13
20
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {object} options
|
|
4
|
+
* @param {Function} options.onDragStart
|
|
5
|
+
* @param {Function} options.onDrag
|
|
6
|
+
* @param {Function} options.onDragEnd
|
|
7
|
+
* @returns {import('svelte/attachments').Attachment} */
|
|
8
|
+
export function draggable({ onDragStart, onDrag, onDragEnd } = {}) {
|
|
9
|
+
return (node) => {
|
|
10
|
+
let isDragging = false;
|
|
11
|
+
/** @type {MouseEvent | undefined} */
|
|
12
|
+
let eventStart;
|
|
13
|
+
/** @type {DOMRect | undefined} */
|
|
14
|
+
let rect;
|
|
15
|
+
let classNameDragging = 'fragment-dragging';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param {MouseEvent} event
|
|
20
|
+
*/
|
|
21
|
+
function handleMouseDown(event) {
|
|
22
|
+
isDragging = true;
|
|
23
|
+
eventStart = event;
|
|
24
|
+
|
|
25
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
26
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
27
|
+
|
|
28
|
+
document.body.classList.add(classNameDragging);
|
|
29
|
+
|
|
30
|
+
rect = node.getBoundingClientRect();
|
|
31
|
+
|
|
32
|
+
const params = computeDrag(event);
|
|
33
|
+
|
|
34
|
+
onDragStart?.(event, params);
|
|
35
|
+
onDrag?.(event, params);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param {MouseEvent} event
|
|
41
|
+
*/
|
|
42
|
+
function handleMouseMove(event) {
|
|
43
|
+
onDrag?.(event, computeDrag(event));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param {MouseEvent} event
|
|
49
|
+
*/
|
|
50
|
+
function handleMouseUp(event) {
|
|
51
|
+
document.body.classList.remove(classNameDragging);
|
|
52
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
53
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
54
|
+
|
|
55
|
+
isDragging = false;
|
|
56
|
+
|
|
57
|
+
const params = computeDrag(event);
|
|
58
|
+
onDrag?.(event, params);
|
|
59
|
+
onDragEnd?.(event, params);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {MouseEvent} event
|
|
65
|
+
*/
|
|
66
|
+
function computeDrag(event) {
|
|
67
|
+
let distanceX = event.clientX - eventStart.clientX;
|
|
68
|
+
let distanceY = event.clientY - eventStart.clientY;
|
|
69
|
+
|
|
70
|
+
let distance = Math.sqrt(
|
|
71
|
+
distanceX * distanceX + distanceY * distanceY,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
distanceX,
|
|
76
|
+
distanceY,
|
|
77
|
+
distance,
|
|
78
|
+
isDragging,
|
|
79
|
+
rect,
|
|
80
|
+
node,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
node.addEventListener('mousedown', handleMouseDown);
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
node.removeEventListener('mousedown', handleMouseDown);
|
|
88
|
+
|
|
89
|
+
rect = undefined;
|
|
90
|
+
eventStart = undefined;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
}
|