@editframe/elements 0.14.0-beta.3 → 0.15.0-beta.10

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.
Files changed (36) hide show
  1. package/dist/EF_FRAMEGEN.js +0 -2
  2. package/dist/elements/EFAudio.d.ts +0 -1
  3. package/dist/elements/EFAudio.js +1 -5
  4. package/dist/elements/EFCaptions.js +1 -1
  5. package/dist/elements/EFImage.d.ts +2 -1
  6. package/dist/elements/EFImage.js +9 -3
  7. package/dist/elements/EFMedia.d.ts +8 -0
  8. package/dist/elements/EFMedia.js +247 -8
  9. package/dist/elements/EFTemporal.d.ts +7 -3
  10. package/dist/elements/EFTemporal.js +19 -1
  11. package/dist/elements/EFTimegroup.d.ts +1 -5
  12. package/dist/elements/EFTimegroup.js +5 -6
  13. package/dist/elements/EFWaveform.d.ts +16 -7
  14. package/dist/elements/EFWaveform.js +273 -163
  15. package/dist/elements/TargetController.d.ts +25 -0
  16. package/dist/elements/TargetController.js +164 -0
  17. package/dist/elements/TargetController.test.d.ts +19 -0
  18. package/dist/gui/EFPreview.d.ts +1 -1
  19. package/dist/gui/EFPreview.js +1 -0
  20. package/dist/gui/EFWorkbench.js +1 -1
  21. package/dist/gui/TWMixin.css.js +1 -1
  22. package/dist/style.css +3 -0
  23. package/package.json +10 -4
  24. package/src/elements/EFAudio.ts +1 -4
  25. package/src/elements/EFCaptions.ts +1 -1
  26. package/src/elements/EFImage.browsertest.ts +33 -2
  27. package/src/elements/EFImage.ts +10 -3
  28. package/src/elements/EFMedia.ts +304 -6
  29. package/src/elements/EFTemporal.ts +37 -5
  30. package/src/elements/EFTimegroup.ts +5 -7
  31. package/src/elements/EFWaveform.ts +341 -194
  32. package/src/elements/TargetController.test.ts +229 -0
  33. package/src/elements/TargetController.ts +219 -0
  34. package/src/gui/EFPreview.ts +10 -9
  35. package/src/gui/EFWorkbench.ts +1 -1
  36. package/types.json +1 -0
@@ -1,13 +1,14 @@
1
- import { EFAudio } from "./EFAudio.js";
1
+ import { CSSStyleObserver } from "@bramus/style-observer";
2
2
  import { Task } from "@lit/task";
3
3
  import { html, css, LitElement } from "lit";
4
- import { property, customElement } from "lit/decorators.js";
4
+ import { property, state, customElement } from "lit/decorators.js";
5
5
  import { createRef, ref } from "lit/directives/ref.js";
6
6
  import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
7
+ import { EF_RENDERING } from "../EF_RENDERING.js";
7
8
  import { TWMixin } from "../gui/TWMixin.js";
8
9
  import { CrossUpdateController } from "./CrossUpdateController.js";
9
10
  import { EFTemporal } from "./EFTemporal.js";
10
- import { EFVideo } from "./EFVideo.js";
11
+ import { TargetController } from "./TargetController.js";
11
12
  var __defProp = Object.defineProperty;
12
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
14
  var __decorateClass = (decorators, target, key, kind) => {
@@ -23,83 +24,80 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
23
24
  super(...arguments);
24
25
  this.canvasRef = createRef();
25
26
  this.ctx = null;
27
+ this.styleObserver = null;
26
28
  this.mode = "bars";
27
29
  this.color = "currentColor";
28
- this.targetSelector = "";
30
+ this.target = "";
31
+ this.targetElement = null;
32
+ this.lineWidth = 4;
33
+ this.targetController = new TargetController(this);
29
34
  this.frameTask = new Task(this, {
30
35
  autoRun: EF_INTERACTIVE,
31
- args: () => [this.targetElement.audioBufferTask.status],
36
+ args: () => {
37
+ return [
38
+ this.targetElement,
39
+ this.targetElement?.frequencyDataTask.value
40
+ ];
41
+ },
32
42
  task: async () => {
33
- await this.targetElement.audioBufferTask.taskComplete;
43
+ if (!this.targetElement) return;
44
+ await this.targetElement.frequencyDataTask.taskComplete;
34
45
  this.ctx ||= this.initCanvas();
35
46
  const ctx = this.ctx;
36
47
  if (!ctx) return;
37
- if (!this.targetElement.audioBufferTask.value) return;
38
- if (this.targetElement.trimAdjustedOwnCurrentTimeMs > 0) {
39
- const FRAME_DURATION_MS = 48e3 / 1e3;
40
- const FRAME_SMEAR_MS = FRAME_DURATION_MS * 1;
41
- const FRAME_SMEAR_S = FRAME_SMEAR_MS / 1e3;
42
- const audioContext = new OfflineAudioContext(2, 48e3 / 25, 48e3);
43
- const audioBufferSource = audioContext.createBufferSource();
44
- audioBufferSource.buffer = this.targetElement.audioBufferTask.value.buffer;
45
- const analyser = audioContext.createAnalyser();
46
- analyser.fftSize = 128 * 8;
47
- audioBufferSource.connect(analyser);
48
- const startTime = Math.max(
49
- 0,
50
- (this.targetElement.trimAdjustedOwnCurrentTimeMs - this.targetElement.audioBufferTask.value.startOffsetMs) / 1e3
51
- );
52
- audioBufferSource.start(0, startTime, FRAME_SMEAR_S);
53
- await audioContext.startRendering();
54
- const frameData = new Uint8Array(analyser.frequencyBinCount);
55
- analyser.getByteFrequencyData(frameData);
56
- const smoothedData = frameData.slice(0, frameData.length / 2);
57
- if (this.color === "currentColor") {
58
- const computedStyle = getComputedStyle(this);
59
- const currentColor = computedStyle.color;
60
- ctx.strokeStyle = currentColor;
61
- ctx.fillStyle = currentColor;
62
- }
63
- switch (this.mode) {
64
- case "bars":
65
- this.drawBars(ctx, smoothedData);
66
- break;
67
- case "bricks":
68
- this.drawBricks(ctx, smoothedData);
69
- break;
70
- case "curve":
71
- this.drawCurve(ctx, smoothedData);
72
- break;
73
- case "line":
74
- this.drawLine(ctx, smoothedData);
75
- break;
76
- case "pixel":
77
- this.drawPixel(ctx, smoothedData);
78
- break;
79
- case "wave":
80
- this.drawWave(ctx, smoothedData);
81
- break;
82
- case "roundBars":
83
- this.drawRoundBars(ctx, smoothedData);
84
- break;
85
- case "equalizer":
86
- this.drawEqualizer(ctx, smoothedData);
87
- break;
88
- }
48
+ const frequencyData = this.targetElement.frequencyDataTask.value;
49
+ const byteTimeData = this.targetElement.byteTimeDomainTask.value;
50
+ if (!frequencyData || !byteTimeData) return;
51
+ ctx.save();
52
+ if (this.color === "currentColor") {
53
+ const computedStyle = getComputedStyle(this);
54
+ const currentColor = computedStyle.color;
55
+ ctx.strokeStyle = currentColor;
56
+ ctx.fillStyle = currentColor;
57
+ } else {
58
+ ctx.strokeStyle = this.color;
59
+ ctx.fillStyle = this.color;
89
60
  }
61
+ switch (this.mode) {
62
+ case "bars":
63
+ this.drawBars(ctx, frequencyData);
64
+ break;
65
+ case "bricks":
66
+ this.drawBricks(ctx, frequencyData);
67
+ break;
68
+ case "line":
69
+ this.drawLine(ctx, byteTimeData);
70
+ break;
71
+ case "curve":
72
+ this.drawCurve(ctx, byteTimeData);
73
+ break;
74
+ case "pixel":
75
+ this.drawPixel(ctx, frequencyData);
76
+ break;
77
+ case "wave":
78
+ this.drawWave(ctx, frequencyData);
79
+ break;
80
+ case "spikes":
81
+ this.drawSpikes(ctx, frequencyData);
82
+ break;
83
+ case "roundBars":
84
+ this.drawRoundBars(ctx, frequencyData);
85
+ break;
86
+ }
87
+ ctx.restore();
90
88
  }
91
89
  });
92
90
  }
93
91
  render() {
94
92
  return html`<canvas ${ref(this.canvasRef)}></canvas>`;
95
93
  }
96
- set target(value) {
97
- this.targetSelector = value;
98
- }
99
94
  connectedCallback() {
100
95
  super.connectedCallback();
101
- if (this.targetElement) {
102
- new CrossUpdateController(this.targetElement, this);
96
+ try {
97
+ if (this.targetElement) {
98
+ new CrossUpdateController(this.targetElement, this);
99
+ }
100
+ } catch (e) {
103
101
  }
104
102
  this.resizeObserver = new ResizeObserver(() => {
105
103
  this.resizeCanvas();
@@ -113,11 +111,18 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
113
111
  }
114
112
  });
115
113
  this.mutationObserver.observe(this, { attributes: true });
114
+ if (!EF_RENDERING()) {
115
+ this.styleObserver = new CSSStyleObserver(["color"], () => {
116
+ this.frameTask.run();
117
+ });
118
+ this.styleObserver.attach(this);
119
+ }
116
120
  }
117
121
  disconnectedCallback() {
118
122
  super.disconnectedCallback();
119
123
  this.resizeObserver?.disconnect();
120
124
  this.mutationObserver?.disconnect();
125
+ this.styleObserver?.detach();
121
126
  }
122
127
  resizeCanvas() {
123
128
  this.ctx = this.initCanvas();
@@ -128,7 +133,10 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
128
133
  initCanvas() {
129
134
  const canvas = this.canvasRef.value;
130
135
  if (!canvas) return null;
131
- const rect = this.getBoundingClientRect();
136
+ const rect = {
137
+ width: this.offsetWidth,
138
+ height: this.offsetHeight
139
+ };
132
140
  const dpr = window.devicePixelRatio;
133
141
  canvas.style.width = `${rect.width}px`;
134
142
  canvas.style.height = `${rect.height}px`;
@@ -137,163 +145,259 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
137
145
  const ctx = canvas.getContext("2d");
138
146
  if (!ctx) return null;
139
147
  ctx.reset();
140
- ctx.scale(dpr, dpr);
141
148
  return ctx;
142
149
  }
143
150
  drawBars(ctx, frequencyData) {
144
151
  const canvas = ctx.canvas;
145
- const waveWidth = canvas.width / devicePixelRatio;
146
- const waveHeight = canvas.height / devicePixelRatio;
147
- const barWidth = waveWidth / frequencyData.length * 0.8;
148
- const baseline = waveHeight / 2;
152
+ const waveWidth = canvas.width;
153
+ const waveHeight = canvas.height;
154
+ const totalBars = frequencyData.length;
155
+ const paddingInner = 0.5;
156
+ const paddingOuter = 0.01;
157
+ const availableWidth = waveWidth;
158
+ const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
149
159
  ctx.clearRect(0, 0, waveWidth, waveHeight);
160
+ const path = new Path2D();
150
161
  frequencyData.forEach((value, i) => {
151
- const height = value / 255 * (waveHeight / 2);
152
- const x = i * (waveWidth / frequencyData.length);
153
- const y = baseline - height;
154
- ctx.fillRect(x, y, barWidth, Math.max(height * 2, 2));
162
+ const normalizedValue = Math.min(value / 255 * 2, 1);
163
+ const barHeight = normalizedValue * waveHeight;
164
+ const y = (waveHeight - barHeight) / 2;
165
+ const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
166
+ path.rect(x, y, barWidth, barHeight);
155
167
  });
168
+ ctx.fill(path);
156
169
  }
157
170
  drawBricks(ctx, frequencyData) {
158
171
  const canvas = ctx.canvas;
159
- const waveWidth = canvas.width / devicePixelRatio;
160
- const waveHeight = canvas.height / devicePixelRatio;
161
- const brickWidth = waveWidth / frequencyData.length;
162
- const brickHeightFactor = waveHeight / 255 / 2;
163
- const brickPadding = 2;
164
- const midHeight = waveHeight / 2;
165
- ctx.clearRect(0, 0, waveWidth, waveHeight);
166
- ctx.beginPath();
167
- ctx.setLineDash([2]);
168
- ctx.lineWidth = 4;
169
- ctx.moveTo(0, midHeight);
170
- ctx.lineTo(waveWidth, midHeight);
171
- ctx.stroke();
172
- ctx.setLineDash([]);
173
- frequencyData.forEach((value, i) => {
174
- const x = i * brickWidth;
175
- const height = value * brickHeightFactor * 2;
176
- const y = midHeight - height / 2;
177
- ctx.fillRect(x, y, brickWidth - brickPadding, height);
178
- });
179
- }
180
- drawLine(ctx, frequencyData) {
181
- const canvas = ctx.canvas;
182
- const waveWidth = canvas.width / devicePixelRatio;
183
- const waveHeight = canvas.height / devicePixelRatio;
172
+ const waveWidth = canvas.width;
173
+ const waveHeight = canvas.height;
184
174
  ctx.clearRect(0, 0, waveWidth, waveHeight);
185
- ctx.beginPath();
175
+ const path = new Path2D();
176
+ const columnWidth = waveWidth / frequencyData.length;
177
+ const boxSize = columnWidth * 0.9;
178
+ const verticalGap = boxSize * 0.2;
179
+ const maxBricks = Math.floor(waveHeight / (boxSize + verticalGap));
186
180
  frequencyData.forEach((value, i) => {
187
- const x = i / frequencyData.length * waveWidth;
188
- const y = (1 - value / 255) * waveHeight;
189
- if (i === 0) {
190
- ctx.moveTo(x, y);
191
- } else {
192
- ctx.lineTo(x, y);
181
+ const normalizedValue = Math.min(value / 255 * 2, 1);
182
+ const brickCount = Math.floor(normalizedValue * maxBricks);
183
+ for (let j = 0; j < brickCount; j++) {
184
+ const x = columnWidth * i;
185
+ const y = waveHeight - (j + 1) * (boxSize + verticalGap);
186
+ path.rect(x, y, boxSize, boxSize);
193
187
  }
194
188
  });
195
- ctx.lineWidth = 4;
196
- ctx.stroke();
189
+ ctx.fill(path);
197
190
  }
198
191
  drawRoundBars(ctx, frequencyData) {
199
192
  const canvas = ctx.canvas;
200
- const waveWidth = canvas.width / devicePixelRatio;
201
- const waveHeight = canvas.height / devicePixelRatio;
202
- const barWidth = waveWidth / frequencyData.length * 0.5;
203
- const baseline = waveHeight / 2;
204
- const maxHeight = waveHeight / 2;
193
+ const waveWidth = canvas.width;
194
+ const waveHeight = canvas.height;
195
+ const totalBars = frequencyData.length;
196
+ const paddingInner = 0.5;
197
+ const paddingOuter = 0.01;
198
+ const availableWidth = waveWidth;
199
+ const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
205
200
  ctx.clearRect(0, 0, waveWidth, waveHeight);
201
+ const path = new Path2D();
206
202
  frequencyData.forEach((value, i) => {
207
- const height = value / 255 * maxHeight;
208
- const x = i * (waveWidth / frequencyData.length);
209
- const y = baseline - height;
210
- const radius = barWidth / 2;
211
- ctx.beginPath();
212
- ctx.roundRect(x, y, barWidth, Math.max(height * 2, 2), radius);
213
- ctx.fill();
203
+ const normalizedValue = Math.min(value / 255 * 2, 1);
204
+ const height = normalizedValue * waveHeight;
205
+ const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
206
+ const y = (waveHeight - height) / 2;
207
+ path.roundRect(x, y, barWidth, height, barWidth / 2);
214
208
  });
209
+ ctx.fill(path);
215
210
  }
216
211
  drawEqualizer(ctx, frequencyData) {
217
212
  const canvas = ctx.canvas;
218
- const waveWidth = canvas.width / devicePixelRatio;
219
- const waveHeight = canvas.height / devicePixelRatio;
220
- const barWidth = waveWidth / frequencyData.length * 0.8;
213
+ const waveWidth = canvas.width;
214
+ const waveHeight = canvas.height;
221
215
  const baseline = waveHeight / 2;
216
+ const barWidth = waveWidth / frequencyData.length * 0.8;
222
217
  ctx.clearRect(0, 0, waveWidth, waveHeight);
223
- ctx.beginPath();
224
- ctx.lineWidth = 2;
225
- ctx.moveTo(0, baseline);
226
- ctx.lineTo(waveWidth, baseline);
227
- ctx.stroke();
218
+ const baselinePath = new Path2D();
219
+ const barsPath = new Path2D();
220
+ baselinePath.moveTo(0, baseline);
221
+ baselinePath.lineTo(waveWidth, baseline);
228
222
  frequencyData.forEach((value, i) => {
229
223
  const height = value / 255 * (waveHeight / 2);
230
224
  const x = i * (waveWidth / frequencyData.length);
231
225
  const y = baseline - height;
232
- ctx.fillRect(x, y, barWidth, Math.max(height * 2, 1));
226
+ barsPath.rect(x, y, barWidth, Math.max(height * 2, 1));
233
227
  });
228
+ ctx.lineWidth = 2;
229
+ ctx.stroke(baselinePath);
230
+ ctx.fill(barsPath);
231
+ }
232
+ drawLine(ctx, frequencyData) {
233
+ const canvas = ctx.canvas;
234
+ const waveWidth = canvas.width;
235
+ const waveHeight = canvas.height;
236
+ ctx.clearRect(0, 0, waveWidth, waveHeight);
237
+ const path = new Path2D();
238
+ const sampleRate = 4;
239
+ for (let i = 0; i < frequencyData.length; i += sampleRate) {
240
+ const x = i / frequencyData.length * waveWidth;
241
+ const y = (1 - (frequencyData[i] ?? 0) / 255) * waveHeight;
242
+ if (i === 0) {
243
+ path.moveTo(x, y);
244
+ } else {
245
+ path.lineTo(x, y);
246
+ }
247
+ }
248
+ const lastX = waveWidth;
249
+ const lastY = (1 - (frequencyData[frequencyData.length - 1] ?? 0) / 255) * waveHeight;
250
+ path.lineTo(lastX, lastY);
251
+ ctx.lineWidth = this.lineWidth;
252
+ ctx.stroke(path);
234
253
  }
235
254
  drawCurve(ctx, frequencyData) {
236
255
  const canvas = ctx.canvas;
237
- const waveWidth = canvas.width / devicePixelRatio;
238
- const waveHeight = canvas.height / devicePixelRatio;
256
+ const waveWidth = canvas.width;
257
+ const waveHeight = canvas.height;
239
258
  ctx.clearRect(0, 0, waveWidth, waveHeight);
240
- ctx.beginPath();
259
+ const path = new Path2D();
241
260
  frequencyData.forEach((value, i) => {
242
261
  const x = i / frequencyData.length * waveWidth;
243
262
  const y = (1 - value / 255) * waveHeight;
244
263
  if (i === 0) {
245
- ctx.moveTo(x, y);
264
+ path.moveTo(x, y);
246
265
  } else {
247
- ctx.lineTo(x, y);
266
+ const prevX = (i - 1) / frequencyData.length * waveWidth;
267
+ const prevY = (1 - (frequencyData[i - 1] ?? 0) / 255) * waveHeight;
268
+ const xc = (prevX + x) / 2;
269
+ const yc = (prevY + y) / 2;
270
+ path.quadraticCurveTo(prevX, prevY, xc, yc);
248
271
  }
249
272
  });
250
- ctx.lineWidth = 4;
251
- ctx.stroke();
273
+ ctx.lineWidth = this.lineWidth;
274
+ ctx.stroke(path);
252
275
  }
253
276
  drawPixel(ctx, frequencyData) {
254
277
  const canvas = ctx.canvas;
255
- const waveWidth = canvas.width / devicePixelRatio;
256
- const waveHeight = canvas.height / devicePixelRatio;
278
+ const waveWidth = canvas.width;
279
+ const waveHeight = canvas.height;
257
280
  const baseline = waveHeight / 2;
258
- const barWidth = waveWidth / frequencyData.length * 0.97;
281
+ const barWidth = waveWidth / frequencyData.length;
259
282
  ctx.clearRect(0, 0, waveWidth, waveHeight);
283
+ const path = new Path2D();
260
284
  frequencyData.forEach((value, i) => {
285
+ const normalizedValue = Math.min(value / 255 * 2, 1);
261
286
  const x = i * (waveWidth / frequencyData.length);
262
- const barHeight = value / 255 * baseline;
287
+ const barHeight = normalizedValue * (waveHeight / 2);
263
288
  const y = baseline - barHeight;
264
- ctx.fillRect(x, y, barWidth, barHeight * 2);
289
+ path.rect(x, y, barWidth, barHeight * 2);
265
290
  });
291
+ ctx.fill(path);
266
292
  }
267
293
  drawWave(ctx, frequencyData) {
268
294
  const canvas = ctx.canvas;
269
- const waveWidth = canvas.width / devicePixelRatio;
270
- const waveHeight = canvas.height / devicePixelRatio;
271
- const baseline = waveHeight / 2;
272
- const barWidth = waveWidth / frequencyData.length * 0.97;
295
+ const waveWidth = canvas.width;
296
+ const waveHeight = canvas.height;
297
+ const paddingOuter = 0.01;
298
+ const availableWidth = waveWidth * (1 - 2 * paddingOuter);
299
+ const startX = waveWidth * paddingOuter;
273
300
  ctx.clearRect(0, 0, waveWidth, waveHeight);
274
- ctx.beginPath();
275
- ctx.moveTo(0, baseline);
276
- ctx.lineTo(waveWidth, baseline);
277
- ctx.strokeStyle = this.color;
278
- ctx.lineWidth = 1;
279
- ctx.stroke();
301
+ const path = new Path2D();
302
+ const firstValue = Math.min((frequencyData[0] ?? 0) / 255 * 2, 1);
303
+ const firstY = (waveHeight - firstValue * waveHeight) / 2;
304
+ path.moveTo(startX, firstY);
280
305
  frequencyData.forEach((value, i) => {
281
- const x = i * (waveWidth / frequencyData.length);
282
- const barHeight = value / 255 * (waveHeight / 2);
283
- const y = baseline - barHeight;
284
- ctx.fillRect(x, y, barWidth, barHeight * 2);
306
+ const normalizedValue = Math.min(value / 255 * 2, 1);
307
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
308
+ const barHeight = normalizedValue * waveHeight;
309
+ const y = (waveHeight - barHeight) / 2;
310
+ if (i === 0) {
311
+ path.moveTo(x, y);
312
+ } else {
313
+ const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
314
+ const prevValue = Math.min((frequencyData[i - 1] ?? 0) / 255 * 2, 1);
315
+ const prevBarHeight = prevValue * waveHeight;
316
+ const prevY = (waveHeight - prevBarHeight) / 2;
317
+ const xc = (prevX + x) / 2;
318
+ const yc = (prevY + y) / 2;
319
+ path.quadraticCurveTo(prevX, prevY, xc, yc);
320
+ }
285
321
  });
322
+ for (let i = frequencyData.length - 1; i >= 0; i--) {
323
+ const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
324
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
325
+ const barHeight = normalizedValue * waveHeight;
326
+ const y = (waveHeight + barHeight) / 2;
327
+ if (i === frequencyData.length - 1) {
328
+ path.lineTo(x, y);
329
+ } else {
330
+ const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
331
+ const nextValue = Math.min((frequencyData[i + 1] ?? 0) / 255 * 2, 1);
332
+ const nextBarHeight = nextValue * waveHeight;
333
+ const nextY = (waveHeight + nextBarHeight) / 2;
334
+ const xc = (nextX + x) / 2;
335
+ const yc = (nextY + y) / 2;
336
+ path.quadraticCurveTo(nextX, nextY, xc, yc);
337
+ }
338
+ }
339
+ const lastY = (waveHeight + firstValue * waveHeight) / 2;
340
+ const controlX = startX;
341
+ const controlY = (lastY + firstY) / 2;
342
+ path.quadraticCurveTo(controlX, controlY, startX, firstY);
343
+ ctx.fill(path);
344
+ }
345
+ drawSpikes(ctx, frequencyData) {
346
+ const canvas = ctx.canvas;
347
+ const waveWidth = canvas.width;
348
+ const waveHeight = canvas.height;
349
+ const paddingOuter = 0.01;
350
+ const availableWidth = waveWidth * (1 - 2 * paddingOuter);
351
+ const startX = waveWidth * paddingOuter;
352
+ ctx.clearRect(0, 0, waveWidth, waveHeight);
353
+ const path = new Path2D();
354
+ const firstValue = (frequencyData[0] ?? 0) / 255;
355
+ const firstY = (waveHeight - firstValue * waveHeight) / 2;
356
+ path.moveTo(startX, firstY);
357
+ frequencyData.forEach((value, i) => {
358
+ const normalizedValue = Math.min(value / 255 * 2, 1);
359
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
360
+ const barHeight = normalizedValue * (waveHeight / 2);
361
+ const y = (waveHeight - barHeight * 2) / 2;
362
+ if (i === 0) {
363
+ path.moveTo(x, y);
364
+ } else {
365
+ const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
366
+ const prevValue = (frequencyData[i - 1] ?? 0) / 255;
367
+ const prevBarHeight = prevValue * (waveHeight / 2);
368
+ const prevY = (waveHeight - prevBarHeight * 2) / 2;
369
+ const xc = (prevX + x) / 2;
370
+ const yc = (prevY + y) / 2;
371
+ path.quadraticCurveTo(prevX, prevY, xc, yc);
372
+ }
373
+ });
374
+ for (let i = frequencyData.length - 1; i >= 0; i--) {
375
+ const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
376
+ const x = startX + i / (frequencyData.length - 1) * availableWidth;
377
+ const barHeight = normalizedValue * (waveHeight / 2);
378
+ const y = (waveHeight + barHeight * 2) / 2;
379
+ if (i === frequencyData.length - 1) {
380
+ path.lineTo(x, y);
381
+ } else {
382
+ const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
383
+ const nextValue = (frequencyData[i + 1] ?? 0) / 255;
384
+ const nextBarHeight = nextValue * (waveHeight / 2);
385
+ const nextY = (waveHeight + nextBarHeight * 2) / 2;
386
+ const xc = (nextX + x) / 2;
387
+ const yc = (nextY + y) / 2;
388
+ path.quadraticCurveTo(nextX, nextY, xc, yc);
389
+ }
390
+ }
391
+ const lastY = (waveHeight + firstValue * waveHeight) / 2;
392
+ const controlX = startX;
393
+ const controlY = (lastY + firstY) / 2;
394
+ path.quadraticCurveTo(controlX, controlY, startX, firstY);
395
+ ctx.fill(path);
286
396
  }
287
397
  get durationMs() {
398
+ if (!this.targetElement) return 0;
288
399
  return this.targetElement.durationMs;
289
400
  }
290
- get targetElement() {
291
- const target = document.getElementById(this.targetSelector ?? "");
292
- if (target instanceof EFAudio || target instanceof EFVideo) {
293
- return target;
294
- }
295
- throw new Error("Invalid target, must be an EFAudio or EFVideo element");
296
- }
297
401
  updated(changedProperties) {
298
402
  super.updated(changedProperties);
299
403
  if (changedProperties.size > 0) {
@@ -326,8 +430,14 @@ __decorateClass([
326
430
  property({ type: String })
327
431
  ], EFWaveform.prototype, "color", 2);
328
432
  __decorateClass([
329
- property({ type: String, attribute: "target", reflect: true })
330
- ], EFWaveform.prototype, "targetSelector", 2);
433
+ property({ type: String, reflect: true })
434
+ ], EFWaveform.prototype, "target", 2);
435
+ __decorateClass([
436
+ state()
437
+ ], EFWaveform.prototype, "targetElement", 2);
438
+ __decorateClass([
439
+ property({ type: Number, attribute: "line-width" })
440
+ ], EFWaveform.prototype, "lineWidth", 2);
331
441
  EFWaveform = __decorateClass([
332
442
  customElement("ef-waveform")
333
443
  ], EFWaveform);
@@ -0,0 +1,25 @@
1
+ import { LitElement, ReactiveController } from 'lit';
2
+ type Constructor<T = {}> = new (...args: any[]) => T;
3
+ export declare class TargetableMixinInterface {
4
+ id: string;
5
+ }
6
+ export declare const isEFTargetable: (obj: any) => obj is TargetableMixinInterface;
7
+ export declare const EFTargetable: <T extends Constructor<LitElement>>(superClass: T) => T;
8
+ export declare class TargetController implements ReactiveController {
9
+ private host;
10
+ private targetController;
11
+ private currentTargetString;
12
+ constructor(host: LitElement & {
13
+ targetElement: Element | null;
14
+ target: string;
15
+ });
16
+ private registryCallback;
17
+ private updateTarget;
18
+ private connectToTarget;
19
+ private disconnectFromTarget;
20
+ private get registry();
21
+ hostDisconnected(): void;
22
+ hostConnected(): void;
23
+ hostUpdate(): void;
24
+ }
25
+ export {};