@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.
- package/dist/EF_FRAMEGEN.js +0 -2
- package/dist/elements/EFAudio.d.ts +0 -1
- package/dist/elements/EFAudio.js +1 -5
- package/dist/elements/EFCaptions.js +1 -1
- package/dist/elements/EFImage.d.ts +2 -1
- package/dist/elements/EFImage.js +9 -3
- package/dist/elements/EFMedia.d.ts +8 -0
- package/dist/elements/EFMedia.js +247 -8
- package/dist/elements/EFTemporal.d.ts +7 -3
- package/dist/elements/EFTemporal.js +19 -1
- package/dist/elements/EFTimegroup.d.ts +1 -5
- package/dist/elements/EFTimegroup.js +5 -6
- package/dist/elements/EFWaveform.d.ts +16 -7
- package/dist/elements/EFWaveform.js +273 -163
- package/dist/elements/TargetController.d.ts +25 -0
- package/dist/elements/TargetController.js +164 -0
- package/dist/elements/TargetController.test.d.ts +19 -0
- package/dist/gui/EFPreview.d.ts +1 -1
- package/dist/gui/EFPreview.js +1 -0
- package/dist/gui/EFWorkbench.js +1 -1
- package/dist/gui/TWMixin.css.js +1 -1
- package/dist/style.css +3 -0
- package/package.json +10 -4
- package/src/elements/EFAudio.ts +1 -4
- package/src/elements/EFCaptions.ts +1 -1
- package/src/elements/EFImage.browsertest.ts +33 -2
- package/src/elements/EFImage.ts +10 -3
- package/src/elements/EFMedia.ts +304 -6
- package/src/elements/EFTemporal.ts +37 -5
- package/src/elements/EFTimegroup.ts +5 -7
- package/src/elements/EFWaveform.ts +341 -194
- package/src/elements/TargetController.test.ts +229 -0
- package/src/elements/TargetController.ts +219 -0
- package/src/gui/EFPreview.ts +10 -9
- package/src/gui/EFWorkbench.ts +1 -1
- package/types.json +1 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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.
|
|
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: () =>
|
|
36
|
+
args: () => {
|
|
37
|
+
return [
|
|
38
|
+
this.targetElement,
|
|
39
|
+
this.targetElement?.frequencyDataTask.value
|
|
40
|
+
];
|
|
41
|
+
},
|
|
32
42
|
task: async () => {
|
|
33
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
102
|
-
|
|
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 =
|
|
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
|
|
146
|
-
const waveHeight = canvas.height
|
|
147
|
-
const
|
|
148
|
-
const
|
|
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
|
|
152
|
-
const
|
|
153
|
-
const y =
|
|
154
|
-
|
|
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
|
|
160
|
-
const waveHeight = canvas.height
|
|
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
|
-
|
|
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
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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.
|
|
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
|
|
201
|
-
const waveHeight = canvas.height
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const
|
|
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
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
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
|
|
219
|
-
const waveHeight = canvas.height
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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
|
|
238
|
-
const waveHeight = canvas.height
|
|
256
|
+
const waveWidth = canvas.width;
|
|
257
|
+
const waveHeight = canvas.height;
|
|
239
258
|
ctx.clearRect(0, 0, waveWidth, waveHeight);
|
|
240
|
-
|
|
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
|
-
|
|
264
|
+
path.moveTo(x, y);
|
|
246
265
|
} else {
|
|
247
|
-
|
|
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 =
|
|
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
|
|
256
|
-
const waveHeight = canvas.height
|
|
278
|
+
const waveWidth = canvas.width;
|
|
279
|
+
const waveHeight = canvas.height;
|
|
257
280
|
const baseline = waveHeight / 2;
|
|
258
|
-
const barWidth = waveWidth / frequencyData.length
|
|
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 =
|
|
287
|
+
const barHeight = normalizedValue * (waveHeight / 2);
|
|
263
288
|
const y = baseline - barHeight;
|
|
264
|
-
|
|
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
|
|
270
|
-
const waveHeight = canvas.height
|
|
271
|
-
const
|
|
272
|
-
const
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
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,
|
|
330
|
-
], EFWaveform.prototype, "
|
|
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 {};
|