fragment-tools 0.1.17 → 0.1.19

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/bin/index.js CHANGED
@@ -28,6 +28,7 @@ prog.command('run [entry]', '', { default: true })
28
28
  .option('--outDir', 'Build output directory')
29
29
  .option('--emptyOutDir', 'Empty outDir before static build')
30
30
  .option('--base', 'Base public path when served in production', undefined)
31
+ .option('--config', 'Path to Fragment config file')
31
32
  .option('--prompts', 'Enable interactive prompts', true)
32
33
  .action((entry, options) => {
33
34
  if (options.new) {
@@ -43,6 +44,7 @@ prog.command('run [entry]', '', { default: true })
43
44
  emptyOutDir: options.emptyOutDir,
44
45
  base: options.base,
45
46
  prompts: options.prompts,
47
+ configFilepath: options.config,
46
48
  });
47
49
  }
48
50
 
@@ -50,6 +52,7 @@ prog.command('run [entry]', '', { default: true })
50
52
  development: options.development,
51
53
  exportDir: options.exportDir,
52
54
  port: options.port,
55
+ configFilepath: options.config,
53
56
  });
54
57
  });
55
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fragment-tools",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "A web development environment for creative coding",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "homepage": "https://github.com/raphaelameaume/fragment#readme",
23
23
  "dependencies": {
24
24
  "@clack/core": "^0.3.3",
25
- "@sveltejs/vite-plugin-svelte": "^2.4.5",
25
+ "@sveltejs/vite-plugin-svelte": "^3.1.1",
26
26
  "body-parser": "^1.20.0",
27
27
  "changedpi": "^1.0.4",
28
28
  "chokidar": "^3.5.2",
@@ -35,7 +35,7 @@
35
35
  "sade": "^1.7.4",
36
36
  "svelte": "^4.0.0",
37
37
  "svelte-json-tree": "^2.2.0",
38
- "vite": "^4.4.9",
38
+ "vite": "^5.3.3",
39
39
  "webm-writer": "^1.0.0",
40
40
  "ws": "^8.2.3"
41
41
  },
package/src/cli/build.js CHANGED
@@ -47,6 +47,10 @@ export async function build(entry, options) {
47
47
  handleCancelledPrompt(outDir, prefix);
48
48
  }
49
49
 
50
+ if (!outDir) {
51
+ outDir = '';
52
+ }
53
+
50
54
  let outDirPath = path.join(cwd, outDir);
51
55
 
52
56
  // create directory if it doesn't exist
@@ -91,13 +95,14 @@ export async function build(entry, options) {
91
95
  );
92
96
 
93
97
  const fragmentFilepath = await createFragmentFile(entries, cwd);
94
- const config = createConfig(
98
+ const config = await createConfig(
95
99
  entries,
96
100
  fragmentFilepath,
97
101
  {
98
102
  dev: options.development,
99
103
  build: true,
100
104
  },
105
+ options.configFilepath,
101
106
  cwd,
102
107
  );
103
108
 
@@ -1,11 +1,44 @@
1
1
  import path from 'node:path';
2
- import { defineConfig } from 'vite';
2
+ import fs from 'node:fs';
3
+ import { defineConfig, loadConfigFromFile, mergeConfig } from 'vite';
3
4
  import { svelte } from '@sveltejs/vite-plugin-svelte';
4
5
 
5
6
  import checkDependencies from './plugins/check-dependencies.js';
6
7
  import { file } from './utils.js';
7
8
  import { log } from './log.js';
8
9
 
10
+ export async function loadConfig({ cwd, filepath }) {
11
+ try {
12
+ let filename = `fragment.config.js`;
13
+ let configFile = filepath ? filepath : filename;
14
+ let configRoot = cwd;
15
+ let resolvedPath = path.resolve(cwd, configFile);
16
+
17
+ if (!fs.existsSync(resolvedPath)) {
18
+ if (filepath) {
19
+ log.error(`Config file not found: ${resolvedPath}`);
20
+ }
21
+ return {};
22
+ }
23
+
24
+ log.info(`Extending configuration from ${resolvedPath}`);
25
+
26
+ const { config } = await loadConfigFromFile(
27
+ {
28
+ command: 'build',
29
+ mode: 'dev',
30
+ },
31
+ configFile,
32
+ configRoot,
33
+ );
34
+
35
+ return config;
36
+ } catch (error) {
37
+ log.error(error);
38
+ return {};
39
+ }
40
+ }
41
+
9
42
  /**
10
43
  * Create Vite config from entries
11
44
  * @param {string[]} entries
@@ -16,10 +49,11 @@ import { log } from './log.js';
16
49
  * @param {string} [cwd=process.cwd()]
17
50
  * @returns {import('vite').UserConfig}
18
51
  */
19
- export function createConfig(
52
+ export async function createConfig(
20
53
  entries,
21
54
  fragmentFilepath,
22
55
  { dev = false, build = false } = {},
56
+ configFilepath,
23
57
  cwd = process.cwd(),
24
58
  ) {
25
59
  const entriesPaths = entries.map((entry) => path.join(cwd, entry));
@@ -28,56 +62,68 @@ export function createConfig(
28
62
 
29
63
  log.info(`Creating Vite configuration...`);
30
64
 
31
- return defineConfig({
32
- configFile: false,
33
- root,
34
- logLevel: dev ? 'info' : 'silent',
35
- resolve: {
36
- alias: [
37
- { find: '@fragment/sketches', replacement: fragmentFilepath },
38
- { find: '@fragment', replacement: app },
39
- {
40
- find: 'three',
41
- replacement: path.join(cwd, 'node_modules/three'),
42
- },
43
- { find: 'p5', replacement: path.join(cwd, 'node_modules/p5') },
44
- {
45
- find: 'ogl',
46
- replacement: path.join(cwd, 'node_modules/ogl'),
47
- },
48
- ],
49
- },
50
- plugins: [
51
- svelte({
52
- configFile: false,
53
- onwarn: (warning, handler) => {
54
- if (dev) {
55
- handler(warning);
56
- } else {
57
- return;
58
- }
59
- },
60
- }),
61
- checkDependencies({
62
- cwd,
63
- app,
64
- entriesPaths,
65
- build,
66
- }),
67
- ],
68
-
69
- define: {
70
- __CWD__: `${JSON.stringify(cwd)}`,
71
- __FRAGMENT_PORT__: undefined,
72
- __START_TIME__: Date.now(),
73
- __SEED__: Date.now(),
74
- __BUILD__: build,
75
- __DEV__: !build,
76
- global: {},
77
- },
78
- optimizeDeps: {
79
- include: ['convert-length', 'webm-writer', 'changedpi'],
80
- exclude: ['@fragment/sketches', ...entriesPaths],
81
- },
65
+ const config = await loadConfig({
66
+ filepath: configFilepath,
67
+ cwd,
82
68
  });
69
+
70
+ return mergeConfig(
71
+ defineConfig({
72
+ configFile: false,
73
+ root,
74
+ logLevel: dev ? 'info' : 'silent',
75
+ resolve: {
76
+ alias: [
77
+ {
78
+ find: '@fragment/sketches',
79
+ replacement: fragmentFilepath,
80
+ },
81
+ { find: '@fragment', replacement: app },
82
+ {
83
+ find: 'three',
84
+ replacement: path.join(cwd, 'node_modules/three'),
85
+ },
86
+ {
87
+ find: 'p5',
88
+ replacement: path.join(cwd, 'node_modules/p5'),
89
+ },
90
+ {
91
+ find: 'ogl',
92
+ replacement: path.join(cwd, 'node_modules/ogl'),
93
+ },
94
+ ],
95
+ },
96
+ plugins: [
97
+ svelte({
98
+ configFile: false,
99
+ onwarn: (warning, handler) => {
100
+ if (dev) {
101
+ handler(warning);
102
+ } else {
103
+ return;
104
+ }
105
+ },
106
+ }),
107
+ checkDependencies({
108
+ cwd,
109
+ app,
110
+ entriesPaths,
111
+ build,
112
+ }),
113
+ ],
114
+ define: {
115
+ __CWD__: `${JSON.stringify(cwd)}`,
116
+ __FRAGMENT_PORT__: undefined,
117
+ __START_TIME__: Date.now(),
118
+ __SEED__: Date.now(),
119
+ __BUILD__: build,
120
+ __DEV__: !build,
121
+ },
122
+ optimizeDeps: {
123
+ include: ['convert-length', 'webm-writer', 'changedpi'],
124
+ exclude: ['@fragment/sketches', ...entriesPaths],
125
+ },
126
+ }),
127
+ config.vite ?? {},
128
+ );
83
129
  }
@@ -37,17 +37,15 @@ export async function preview(dir, options = {}) {
37
37
 
38
38
  const { resolvedUrls } = previewServer;
39
39
 
40
- let urls = ``;
41
-
42
- for (const url of resolvedUrls.local) {
43
- urls += `${bold('Local')}: ${bold(cyan(url))}\n`;
44
- }
45
-
46
- for (const url of resolvedUrls.network) {
47
- urls += `${bold('Network')}: ${bold(cyan(url))}`;
48
- }
49
-
50
- p.note(urls);
40
+ let urls = [
41
+ ...resolvedUrls.local.map(
42
+ (url) => `${bold('Local')}: ${bold(cyan(url))}`,
43
+ ),
44
+ ...resolvedUrls.network.map(
45
+ (url) => `${bold('Network')}: ${bold(cyan(url))}`,
46
+ ),
47
+ ];
48
+ p.note(urls.join(`\n`));
51
49
  } catch (error) {
52
50
  log.error(`Error\n`, prefix);
53
51
  console.error(error);
package/src/cli/run.js CHANGED
@@ -62,13 +62,14 @@ export async function run(entry, options = {}) {
62
62
  cwd,
63
63
  });
64
64
 
65
- const config = createConfig(
65
+ const config = await createConfig(
66
66
  entries,
67
67
  fragmentFilepath,
68
68
  {
69
69
  dev: options.development,
70
70
  build: false,
71
71
  },
72
+ options.configFilepath,
72
73
  cwd,
73
74
  );
74
75
 
@@ -30,4 +30,9 @@ export let update = ({ width, height, time, deltaTime }) => {};
30
30
  * @param {number} params.height
31
31
  * @param {number} params.pixelRatio
32
32
  */
33
- export let resize = ({ width, height, pixelRatio }) => {};
33
+ export let resize = ({ canvas, width, height, pixelRatio }) => {
34
+ canvas.width = width * pixelRatio;
35
+ canvas.height = height * pixelRatio;
36
+ canvas.style.width = `${width}px`;
37
+ canvas.style.height = `${height}px`;
38
+ };
@@ -23,8 +23,8 @@ export let init = ({ canvas, context, width, height }) => {};
23
23
  * @param {number} params.playhead
24
24
  * @param {number} params.playcount
25
25
  */
26
- export let update = ({ context, width, height, time, deltaTime }) => {
27
- context.clearRect(0, 0, width, height);
26
+ export let update = ({ context, width, height, pixelRatio }) => {
27
+ context.clearRect(0, 0, width * pixelRatio, height * pixelRatio);
28
28
  };
29
29
 
30
30
  /**
@@ -137,6 +137,9 @@ class Renderer {
137
137
  this.canvas.width = this.state.width * this.state.pixelRatio;
138
138
  this.canvas.height = this.state.height * this.state.pixelRatio;
139
139
 
140
+ this.canvas.style.width = `${this.state.width}px`;
141
+ this.canvas.style.height = `${this.state.height}px`;
142
+
140
143
  this.setViewport();
141
144
  }
142
145
 
@@ -1,5 +1,34 @@
1
+ /**
2
+ * @typedef {object} MountParams2DRenderer
3
+ * @property {CanvasRenderingContext2D} context
4
+ */
5
+
6
+ /**
7
+ * @param {object} params
8
+ * @param {number} params.id
9
+ * @param {HTMLDivElement} params.container
10
+ * @param {HTMLCanvasElement} params.canvas
11
+ * @param {number} params.width
12
+ * @param {number} params.height
13
+ * @param {number} params.pixelRatio
14
+ * @returns {MountParams2DRenderer}
15
+ */
1
16
  export let onMountPreview = ({ canvas }) => {
2
17
  return {
3
18
  context: canvas.getContext('2d'),
4
19
  };
5
20
  };
21
+
22
+ /**
23
+ * @param {object} params
24
+ * @param {HTMLCanvasElement} params.canvas
25
+ * @param {number} params.width
26
+ * @param {number} params.height
27
+ * @param {number} params.pixelRatio
28
+ */
29
+ export let onResizePreview = ({ canvas, width, height, pixelRatio }) => {
30
+ canvas.width = width * pixelRatio;
31
+ canvas.height = height * pixelRatio;
32
+ canvas.style.width = `${width}px`;
33
+ canvas.style.height = `${height}px`;
34
+ };
@@ -3,8 +3,39 @@ import { client } from '@fragment/client';
3
3
  import { getShaderPath } from '../utils/glsl.utils';
4
4
  import { clearError } from '../stores/errors';
5
5
 
6
+ /**
7
+ * @typedef {object} PreviewP5GLRenderer
8
+ * @property {number} id
9
+ * @property {p5} p
10
+ * @property {boolean} rendered
11
+ */
12
+
13
+ /**
14
+ * @typedef {object} MountParamsP5GLRenderer
15
+ * @property {HTMLCanvasElement} canvas
16
+ * @property {p5} p
17
+ */
18
+
19
+ /**
20
+ * @typedef {object} PreviewParamsP5GLRenderer
21
+ * @property {number} params.id
22
+ * @property {HTMLDivElement} params.container
23
+ * @property {HTMLCanvasElement} params.canvas
24
+ */
25
+
26
+ /** @type {PreviewP5GLRenderer[]} */
6
27
  let previews = [];
7
28
 
29
+ /**
30
+ * @param {object} params
31
+ * @param {number} params.id
32
+ * @param {HTMLDivElement} params.container
33
+ * @param {HTMLCanvasElement} params.canvas
34
+ * @param {number} params.width
35
+ * @param {number} params.height
36
+ * @param {number} params.pixelRatio
37
+ * @returns {MountParamsP5GLRenderer}
38
+ */
8
39
  export let onMountPreview = ({ id, width, height }) => {
9
40
  const p = new p5((sketch) => {
10
41
  sketch.setup = () => {
@@ -12,9 +43,11 @@ export let onMountPreview = ({ id, width, height }) => {
12
43
  };
13
44
  });
14
45
 
46
+ /** @type {PreviewP5GLRenderer} */
15
47
  const preview = {
16
48
  id,
17
49
  p,
50
+ rendered: false,
18
51
  };
19
52
 
20
53
  previews.push(preview);
@@ -25,6 +58,9 @@ export let onMountPreview = ({ id, width, height }) => {
25
58
  };
26
59
  };
27
60
 
61
+ /**
62
+ * @param {PreviewParamsP5GLRenderer} params
63
+ */
28
64
  export let onBeforeUpdatePreview = ({ id }) => {
29
65
  const preview = previews.find((p) => p.id === id);
30
66
 
@@ -34,6 +70,9 @@ export let onBeforeUpdatePreview = ({ id }) => {
34
70
  }
35
71
  };
36
72
 
73
+ /**
74
+ * @param {PreviewParamsP5GLRenderer} params
75
+ */
37
76
  export let onAfterUpdatePreview = ({ id }) => {
38
77
  const preview = previews.find((p) => p.id === id);
39
78
 
@@ -49,15 +88,17 @@ export let onAfterUpdatePreview = ({ id }) => {
49
88
  }
50
89
  };
51
90
 
52
- export let onResizePreview = ({ id, width, height, pixelRatio }) => {
53
- const preview = previews.find((p) => p.id === id);
54
-
55
- if (preview) {
56
- preview.p.pixelDensity(pixelRatio);
57
- preview.p.resizeCanvas(width, height, false);
58
- }
91
+ /**
92
+ * @param {PreviewParamsP5GLRenderer} params
93
+ */
94
+ export let onResizePreview = ({ p, width, height, pixelRatio }) => {
95
+ p.pixelDensity(pixelRatio);
96
+ p.resizeCanvas(width, height, false);
59
97
  };
60
98
 
99
+ /**
100
+ * @param {PreviewParamsP5GLRenderer} params
101
+ */
61
102
  export let onDestroyPreview = ({ id }) => {
62
103
  const previewIndex = previews.findIndex((preview) => preview.id === id);
63
104
  const preview = previews[previewIndex];
@@ -30,13 +30,9 @@ export let onBeforeUpdatePreview = ({ id }) => {
30
30
  }
31
31
  };
32
32
 
33
- export let onResizePreview = ({ id, width, height, pixelRatio }) => {
34
- const preview = previews.find((p) => p.id === id);
35
-
36
- if (preview) {
37
- preview.p.pixelDensity(pixelRatio);
38
- preview.p.resizeCanvas(width, height, false);
39
- }
33
+ export let onResizePreview = ({ p, width, height, pixelRatio }) => {
34
+ p.pixelDensity(pixelRatio);
35
+ p.resizeCanvas(width, height, false);
40
36
  };
41
37
 
42
38
  export let onDestroyPreview = ({ id }) => {
@@ -4,8 +4,11 @@ import { client } from '@fragment/client';
4
4
  import { getShaderPath } from '../utils/glsl.utils';
5
5
  import { clearError } from '../stores/errors';
6
6
 
7
- let renderer;
8
7
  let previews = [];
8
+
9
+ /** @type {WebGLRenderer} */
10
+ let renderer;
11
+
9
12
  let fragmentShader = /* glsl */ `
10
13
  precision highp float;
11
14
  uniform sampler2D uSampler;
@@ -66,7 +69,7 @@ export let onMountPreview = ({ id, canvas, width, height, pixelRatio }) => {
66
69
  };
67
70
  };
68
71
 
69
- export let onDestroyPreview = ({ id, canvas }) => {
72
+ export let onDestroyPreview = ({ id }) => {
70
73
  const previewIndex = previews.findIndex((p) => p.id === id);
71
74
  const preview = previews[previewIndex];
72
75
 
@@ -107,9 +110,12 @@ export let onAfterUpdatePreview = ({ id }) => {
107
110
  export let resize = ({ width, height, pixelRatio }) => {
108
111
  renderer.setPixelRatio(pixelRatio);
109
112
  renderer.setSize(width, height);
113
+ };
114
+
115
+ export let onResizePreview = ({ id, width, height, pixelRatio }) => {
116
+ const preview = previews.find((p) => p.id === id);
110
117
 
111
- for (let i = 0; i < previews.length; i++) {
112
- const preview = previews[i];
118
+ if (preview) {
113
119
  preview.resize({ width, height, pixelRatio });
114
120
  }
115
121
  };
@@ -35,7 +35,9 @@
35
35
  export let paused = false;
36
36
  export let visible = true;
37
37
 
38
- let node, container;
38
+ let node;
39
+ /** @type {HTMLDivElement} */
40
+ let container;
39
41
  let framerate = 60;
40
42
  let elapsed = 0;
41
43
  let elapsedRenderingTime = 0;
@@ -60,44 +62,46 @@
60
62
  $: beforeRecordCallbacks = $beforeRecord.get(key) || [];
61
63
  $: afterRecordCallbacks = $afterRecord.get(key) || [];
62
64
 
63
- function checkForResize() {
65
+ function checkForResize(resizing = $rendering.resizing) {
64
66
  if (!node) return;
65
67
 
66
- let needsUpdate =
67
- $rendering.resizing === SIZES.WINDOW ||
68
- $rendering.resizing === SIZES.ASPECT_RATIO;
69
-
70
- let newWidth, newHeight;
71
-
72
- if ($rendering.resizing === SIZES.WINDOW) {
73
- newWidth = node.offsetWidth;
74
- newHeight = node.offsetHeight;
75
- } else if ($rendering.resizing === SIZES.ASPECT_RATIO) {
76
- const { offsetWidth, offsetHeight } = node;
77
- const aspectRatio = $rendering.aspectRatio;
78
- const monitorRatio = offsetWidth / offsetHeight;
79
-
80
- if (aspectRatio < monitorRatio) {
81
- newHeight = offsetHeight;
82
- newWidth = newHeight * aspectRatio;
83
- } else {
84
- newWidth = offsetWidth;
85
- newHeight = newWidth / aspectRatio;
68
+ let isWindowResize = resizing === SIZES.WINDOW;
69
+ let isAspectResize = resizing === SIZES.ASPECT_RATIO;
70
+ let canUpdate = isWindowResize || isAspectResize;
71
+
72
+ if (canUpdate) {
73
+ let newWidth, newHeight;
74
+
75
+ if (isWindowResize) {
76
+ newWidth = node.offsetWidth;
77
+ newHeight = node.offsetHeight;
78
+ } else if (isAspectResize) {
79
+ const { offsetWidth, offsetHeight } = node;
80
+ const aspectRatio = $rendering.aspectRatio;
81
+ const monitorRatio = offsetWidth / offsetHeight;
82
+
83
+ if (aspectRatio < monitorRatio) {
84
+ newHeight = offsetHeight;
85
+ newWidth = newHeight * aspectRatio;
86
+ } else {
87
+ newWidth = offsetWidth;
88
+ newHeight = newWidth / aspectRatio;
89
+ }
86
90
  }
87
- }
88
91
 
89
- needsUpdate =
90
- needsUpdate &&
91
- (newWidth !== $rendering.width || newHeight !== $rendering.height);
92
-
93
- if (needsUpdate) {
94
- rendering.update((curr) => {
95
- return {
96
- ...curr,
97
- width: newWidth,
98
- height: newHeight,
99
- };
100
- });
92
+ let needsUpdate =
93
+ newWidth !== $rendering.width ||
94
+ newHeight !== $rendering.height;
95
+
96
+ if (needsUpdate) {
97
+ rendering.update((curr) => {
98
+ return {
99
+ ...curr,
100
+ width: newWidth,
101
+ height: newHeight,
102
+ };
103
+ });
104
+ }
101
105
  }
102
106
  }
103
107
 
@@ -127,9 +131,6 @@
127
131
  });
128
132
 
129
133
  function createCanvas(canvas = document.createElement('canvas')) {
130
- canvas.width = $rendering.width * $rendering.pixelRatio;
131
- canvas.height = $rendering.height * $rendering.pixelRatio;
132
-
133
134
  canvas.onmousedown = (event) => checkForTriggersDown(event, key);
134
135
  canvas.onmousemove = (event) => checkForTriggersMove(event, key);
135
136
  canvas.onmouseup = (event) => checkForTriggersUp(event, key);
@@ -182,7 +183,7 @@
182
183
  async function createSketch(key) {
183
184
  _created = false;
184
185
 
185
- sketch?.dispose?.();
186
+ sketch?.dispose?.(params);
186
187
 
187
188
  sketch = $sketches[key];
188
189
 
@@ -202,7 +203,7 @@
202
203
 
203
204
  if (canvas) {
204
205
  if (renderer && typeof renderer.onDestroyPreview === 'function') {
205
- renderer.onDestroyPreview({ id, canvas });
206
+ renderer.onDestroyPreview({ id, container, canvas });
206
207
  }
207
208
 
208
209
  destroyCanvas(canvas);
@@ -487,30 +488,6 @@
487
488
  }
488
489
  }
489
490
 
490
- rendering.subscribe((current) => {
491
- if (canvas && _created) {
492
- if (current.resizing === SIZES.SCALE) {
493
- canvas.style.transform = `scale(${current.scale})`;
494
- } else {
495
- canvas.style.transform = null;
496
- }
497
-
498
- const { width, height, pixelRatio } = current;
499
- const resize = sketch.resize || noop;
500
-
501
- resize({
502
- canvas,
503
- width,
504
- height,
505
- pixelRatio,
506
- ...params,
507
- });
508
-
509
- _renderSketch = createRenderLoop();
510
- needsRender = true;
511
- }
512
- });
513
-
514
491
  async function save() {
515
492
  paused = true;
516
493
 
@@ -649,21 +626,38 @@
649
626
  $: {
650
627
  checkForResize();
651
628
 
629
+ const { width, height, pixelRatio, resizing, scale } = $rendering;
630
+
652
631
  if (renderer && typeof renderer.onResizePreview === 'function') {
653
632
  renderer.onResizePreview({
654
633
  id,
655
634
  container,
656
- width: $rendering.width,
657
- height: $rendering.height,
658
- pixelRatio: $rendering.pixelRatio,
635
+ width,
636
+ height,
637
+ pixelRatio,
638
+ ...params,
659
639
  });
660
640
  }
661
641
 
662
642
  if (canvas) {
663
- const pixelRatio = $rendering.pixelRatio;
643
+ if (resizing === SIZES.SCALE) {
644
+ canvas.style.transform = `scale(${scale})`;
645
+ } else {
646
+ canvas.style.transform = null;
647
+ }
648
+
649
+ if (_created) {
650
+ sketch?.resize?.({
651
+ canvas,
652
+ width,
653
+ height,
654
+ pixelRatio,
655
+ ...params,
656
+ });
664
657
 
665
- canvas.width = $rendering.width * pixelRatio;
666
- canvas.height = $rendering.height * pixelRatio;
658
+ _renderSketch = createRenderLoop();
659
+ _renderSketch();
660
+ }
667
661
  }
668
662
  }
669
663
 
@@ -680,6 +674,10 @@
680
674
  )) // if none of current monitors match the key
681
675
  ? $errors.get($errors.keys().next().value)
682
676
  : null;
677
+
678
+ $: isSquare = $rendering.width === $rendering.height;
679
+ $: isLandscape = $rendering.width > $rendering.height;
680
+ $: isPortrait = $rendering.width < $rendering.height;
683
681
  </script>
684
682
 
685
683
  <div
@@ -691,7 +689,7 @@
691
689
  >
692
690
  <div
693
691
  class="canvas-container"
694
- style="max-width: {$rendering.width}px; max-height: {$rendering.height}px;"
692
+ style="--aspect-ratio: {$rendering.width} / {$rendering.height}; --aspect-ratio-inverse: {$rendering.height} / {$rendering.width}; --width: {$rendering.width}px; --height: {$rendering.height}px;"
695
693
  bind:this={container}
696
694
  />
697
695
  {#if $recording}
@@ -709,7 +707,6 @@
709
707
 
710
708
  <style>
711
709
  .sketch-renderer {
712
- position: absolute;
713
710
  display: flex;
714
711
  width: 100%;
715
712
  height: 100%;
@@ -717,6 +714,8 @@
717
714
  align-items: center;
718
715
 
719
716
  background-color: var(--background-color, var(--color-lightblack));
717
+
718
+ container-type: size;
720
719
  }
721
720
 
722
721
  .sketch-renderer:not(.visible) {
@@ -724,13 +723,27 @@
724
723
  }
725
724
 
726
725
  .canvas-container {
726
+ --w: min(100cqw, calc(100cqh * var(--aspect-ratio)));
727
727
  position: relative;
728
- display: flex;
729
- flex-direction: column;
730
- justify-content: center;
731
- align-items: center;
732
- height: 100%;
733
- max-height: 100%;
728
+
729
+ max-width: var(--width);
730
+ max-height: var(--height);
731
+
732
+ width: var(--w);
733
+ height: calc(var(--w) * var(--aspect-ratio-inverse));
734
+
735
+ background-color: red;
736
+ }
737
+
738
+ :global(.canvas-container canvas) {
739
+ position: absolute;
740
+ top: 0;
741
+ left: 0;
742
+
743
+ width: 100% !important;
744
+ height: 100% !important;
745
+
746
+ background-color: var(--background-color, #000000);
734
747
  }
735
748
 
736
749
  .sketch-renderer.recording .canvas-container {
@@ -783,16 +796,4 @@
783
796
  opacity: 0;
784
797
  }
785
798
  }
786
-
787
- :global(.canvas-container canvas) {
788
- max-width: 100%;
789
- max-height: 100%;
790
-
791
- flex: none;
792
-
793
- width: auto !important;
794
- height: auto !important;
795
-
796
- background-color: var(--background-color, #000000);
797
- }
798
799
  </style>
@@ -1,45 +0,0 @@
1
- import * as HME from 'h264-mp4-encoder';
2
- import CanvasRecorder from './CanvasRecorder';
3
-
4
- class H264Recorder extends CanvasRecorder {
5
- getBitmapRGBA(bitmap, width = bitmap.width, height = bitmap.height) {
6
- this.tmpCanvas.width = width;
7
- this.tmpCanvas.height = height;
8
- this.tmpContext.clearRect(0, 0, width, height);
9
- this.tmpContext.drawImage(bitmap, 0, 0, width, height);
10
- return this.tmpContext.getImageData(0, 0, width, height).data;
11
- }
12
-
13
- async start() {
14
- this.encoder = await HME.createH264MP4Encoder();
15
-
16
- this.tmpCanvas = document.createElement('canvas');
17
- this.tmpContext = this.tmpCanvas.getContext('2d');
18
-
19
- this.encoder.width = this.canvas.width;
20
- this.encoder.height = this.canvas.height;
21
- this.encoder.frameRate = this.framerate;
22
- this.encoder.kbps = 30000;
23
- this.encoder.initialize();
24
-
25
- super.start();
26
- }
27
-
28
- tick() {
29
- const { width, height } = this.canvas;
30
- const pixels = this.getBitmapRGBA(this.canvas, width, height);
31
-
32
- this.encoder.addFrameRgba(pixels);
33
- }
34
-
35
- async end() {
36
- this.encoder.finalize();
37
-
38
- const data = this.encoder.FS.readFile(this.encoder.outputFilename);
39
- this.result = new Blob([data], { type: 'video/mp4' });
40
-
41
- super.end();
42
- }
43
- }
44
-
45
- export default H264Recorder;