@editframe/elements 0.16.0-beta.1 → 0.16.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,9 @@ declare global {
19
19
  declare class TriggerCanvas {
20
20
  private canvas;
21
21
  private ctx;
22
+ private canvasInitialized;
22
23
  constructor();
24
+ initialize(): void;
23
25
  trigger(): void;
24
26
  }
25
27
  export declare class EFFramegen {
@@ -3,27 +3,30 @@ import { deepGetElementsWithFrameTasks } from "./elements/EFTemporal.js";
3
3
  import { shallowGetTimegroups } from "./elements/EFTimegroup.js";
4
4
  class TriggerCanvas {
5
5
  constructor() {
6
+ this.canvasInitialized = false;
6
7
  this.canvas = document.createElement("canvas");
8
+ const ctx = this.canvas.getContext("2d", { willReadFrequently: true });
9
+ if (!ctx) throw new Error("Canvas 2d context not ready");
10
+ this.ctx = ctx;
11
+ this.ctx.fillStyle = "transparent";
12
+ }
13
+ initialize() {
14
+ if (this.canvasInitialized) return;
15
+ this.canvasInitialized = true;
7
16
  this.canvas.width = 1;
8
17
  this.canvas.height = 1;
9
18
  Object.assign(this.canvas.style, {
10
19
  position: "fixed",
11
20
  top: "0px",
12
21
  left: "0px",
13
- width: "1px",
14
- height: "1px",
22
+ width: "100%",
23
+ height: "100%",
15
24
  zIndex: "100000"
16
25
  });
17
26
  document.body.appendChild(this.canvas);
18
- const ctx = this.canvas.getContext("2d", { willReadFrequently: true });
19
- if (!ctx) throw new Error("Canvas 2d context not ready");
20
- this.ctx = ctx;
21
- this.ctx.fillStyle = "transparent";
22
27
  }
23
28
  trigger() {
24
- console.log("TRIGGERING CANVAS");
25
29
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
26
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
27
30
  }
28
31
  }
29
32
  class EFFramegen {
@@ -119,13 +122,9 @@ class EFFramegen {
119
122
  }
120
123
  this.time = this.renderOptions.encoderOptions.fromMs + frameNumber * this.frameDurationMs;
121
124
  firstGroup.currentTimeMs = this.time;
122
- console.log("trace: Awaiting initialBusyTasks");
123
125
  await this.initialBusyTasks;
124
- console.log("trace: Awaiting microtask");
125
126
  await new Promise(queueMicrotask);
126
- console.log("trace: Awaiting frame tasks");
127
127
  const now = performance.now();
128
- console.log("trace: HTML", document.body.innerHTML);
129
128
  await Promise.all(
130
129
  temporals.filter((temporal) => temporal.frameTask.status < TaskStatus.COMPLETE).map((temporal) => {
131
130
  return temporal.frameTask;
@@ -259,6 +259,7 @@ const EFTemporal = (superClass) => {
259
259
  );
260
260
  return previous.startTimeMs + previous.durationMs - parentTimegroup.overlapMs;
261
261
  }
262
+ case "fit":
262
263
  case "contain":
263
264
  case "fixed":
264
265
  startTimeMsCache.set(
@@ -6,7 +6,7 @@ export declare class EFTimegroup extends EFTimegroup_base {
6
6
  #private;
7
7
  static styles: import('lit').CSSResult;
8
8
  _timeGroupContext: this;
9
- mode: "fixed" | "sequence" | "contain";
9
+ mode: "fit" | "fixed" | "sequence" | "contain";
10
10
  overlapMs: number;
11
11
  fit: "none" | "contain" | "cover";
12
12
  set currentTime(time: number);
@@ -19,6 +19,7 @@ export declare class EFTimegroup extends EFTimegroup_base {
19
19
  disconnectedCallback(): void;
20
20
  get storageKey(): string;
21
21
  get intrinsicDurationMs(): number | undefined;
22
+ get hasOwnDuration(): boolean;
22
23
  get durationMs(): number;
23
24
  /**
24
25
  * Wait for all media elements to load their initial segments.
@@ -128,26 +128,42 @@ let EFTimegroup = class extends EFTemporal(LitElement) {
128
128
  }
129
129
  return void 0;
130
130
  }
131
+ get hasOwnDuration() {
132
+ return this.mode === "contain" || this.mode === "sequence" || this.mode === "fixed" && this.hasExplicitDuration;
133
+ }
131
134
  get durationMs() {
132
135
  switch (this.mode) {
136
+ case "fit": {
137
+ if (!this.parentTimegroup) {
138
+ return 0;
139
+ }
140
+ return this.parentTimegroup.durationMs;
141
+ }
133
142
  case "fixed":
134
143
  return super.durationMs;
135
144
  case "sequence": {
136
145
  let duration = 0;
137
- this.childTemporals.forEach((node, index) => {
146
+ this.childTemporals.forEach((child, index) => {
147
+ if (child instanceof EFTimegroup && child.mode === "fit") {
148
+ return;
149
+ }
138
150
  if (index > 0) {
139
151
  duration -= this.overlapMs;
140
152
  }
141
- duration += node.durationMs;
153
+ duration += child.durationMs;
142
154
  });
143
155
  return duration;
144
156
  }
145
157
  case "contain": {
146
158
  let maxDuration = 0;
147
- for (const node of this.childTemporals) {
148
- if (node.intrinsicDurationMs !== void 0) {
149
- maxDuration = Math.max(maxDuration, node.durationMs);
159
+ for (const child of this.childTemporals) {
160
+ if (child instanceof EFTimegroup && child.mode === "fit") {
161
+ continue;
162
+ }
163
+ if (!child.hasOwnDuration) {
164
+ continue;
150
165
  }
166
+ maxDuration = Math.max(maxDuration, child.durationMs);
151
167
  }
152
168
  return maxDuration;
153
169
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.16.0-beta.1",
3
+ "version": "0.16.2-beta.0",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "license": "UNLICENSED",
28
28
  "dependencies": {
29
29
  "@bramus/style-observer": "^1.3.0",
30
- "@editframe/assets": "0.16.0-beta.1",
30
+ "@editframe/assets": "0.16.2-beta.0",
31
31
  "@lit/context": "^1.1.2",
32
32
  "@lit/task": "^1.0.1",
33
33
  "d3": "^7.9.0",
@@ -556,6 +556,7 @@ export const EFTemporal = <T extends Constructor<LitElement>>(
556
556
  parentTimegroup.overlapMs
557
557
  );
558
558
  }
559
+ case "fit":
559
560
  case "contain":
560
561
  case "fixed":
561
562
  startTimeMsCache.set(
@@ -55,6 +55,72 @@ const renderTimegroup = (result: TemplateResult) => {
55
55
  return firstChild;
56
56
  };
57
57
 
58
+ describe(`<ef-timegroup mode='fit'>`, () => {
59
+ test("duration is zero when there is no parent to fit into", () => {
60
+ const timegroup = renderTimegroup(
61
+ html`<ef-timegroup mode="fit"></ef-timegroup>`,
62
+ );
63
+ assert.equal(timegroup.durationMs, 0);
64
+ });
65
+
66
+ test("duration is zero when there is no parent to fit into, even if there are children with duration", () => {
67
+ const timegroup = renderTimegroup(
68
+ html`<ef-timegroup mode="fit">
69
+ <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
70
+ </ef-timegroup>`,
71
+ );
72
+ assert.equal(timegroup.durationMs, 0);
73
+ });
74
+
75
+ test("duration is the duration of the parent timegroup", () => {
76
+ const timegroup = renderTimegroup(
77
+ html`<ef-timegroup mode="fixed" duration="10s">
78
+ <ef-timegroup id="child" mode="fit"></ef-timegroup>
79
+ </ef-timegroup>`,
80
+ );
81
+ const child = timegroup.querySelector("#child") as EFTimegroup;
82
+ assert.equal(child.durationMs, 10_000);
83
+ });
84
+
85
+ test("fit mode items inside a sequence are given zero duration and do not factor into the duration of the sequence", () => {
86
+ const timegroup = renderTimegroup(
87
+ html`<ef-timegroup mode="sequence">
88
+ <ef-timegroup id="child" mode="fit"></ef-timegroup>
89
+ </ef-timegroup>`,
90
+ );
91
+ const child = timegroup.querySelector("#child") as EFTimegroup;
92
+ assert.equal(child.durationMs, 0);
93
+ assert.equal(timegroup.durationMs, 0);
94
+ });
95
+
96
+ test("fit mode can be used to constrain a 'background' timegroup into a 'foreground' sequence", async () => {
97
+ const timegroup = renderTimegroup(
98
+ html`
99
+ <ef-timegroup mode="contain">
100
+ <ef-timegroup id="foreground" mode="sequence">
101
+ <ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
102
+ <ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
103
+ </ef-timegroup>
104
+ <ef-timegroup id="background" mode="fit">
105
+ <ef-timegroup mode="fixed" duration="30s"></ef-timegroup>
106
+ </ef-timegroup>
107
+ </ef-timegroup>
108
+ `,
109
+ );
110
+
111
+ const foreground = timegroup.querySelector("#foreground") as EFTimegroup;
112
+ const background = timegroup.querySelector("#background") as EFTimegroup;
113
+ assert.equal(foreground.durationMs, 20_000);
114
+ console.log("INFO", {
115
+ mode: background.mode,
116
+ duration: background.durationMs,
117
+ parentTimegroup: background.parentTimegroup,
118
+ });
119
+ console.log(background.parentTimegroup?.durationMs);
120
+ assert.equal(background.durationMs, 20_000);
121
+ });
122
+ });
123
+
58
124
  describe(`<ef-timegroup mode="fixed">`, () => {
59
125
  test("can explicitly set a duration in seconds", async () => {
60
126
  const timegroup = renderTimegroup(
@@ -54,7 +54,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
54
54
  type: String,
55
55
  attribute: "mode",
56
56
  })
57
- mode: "fixed" | "sequence" | "contain" = "contain";
57
+ mode: "fit" | "fixed" | "sequence" | "contain" = "contain";
58
58
 
59
59
  @property({
60
60
  type: Number,
@@ -146,26 +146,49 @@ export class EFTimegroup extends EFTemporal(LitElement) {
146
146
  return undefined;
147
147
  }
148
148
 
149
- get durationMs() {
149
+ get hasOwnDuration() {
150
+ return (
151
+ this.mode === "contain" ||
152
+ this.mode === "sequence" ||
153
+ (this.mode === "fixed" && this.hasExplicitDuration)
154
+ );
155
+ }
156
+
157
+ get durationMs(): number {
150
158
  switch (this.mode) {
159
+ case "fit": {
160
+ if (!this.parentTimegroup) {
161
+ return 0;
162
+ }
163
+ return this.parentTimegroup.durationMs;
164
+ }
151
165
  case "fixed":
152
166
  return super.durationMs;
153
167
  case "sequence": {
154
168
  let duration = 0;
155
- this.childTemporals.forEach((node, index) => {
169
+ this.childTemporals.forEach((child, index) => {
170
+ if (child instanceof EFTimegroup && child.mode === "fit") {
171
+ return;
172
+ }
156
173
  if (index > 0) {
157
174
  duration -= this.overlapMs;
158
175
  }
159
- duration += node.durationMs;
176
+ duration += child.durationMs;
160
177
  });
161
178
  return duration;
162
179
  }
163
180
  case "contain": {
164
181
  let maxDuration = 0;
165
- for (const node of this.childTemporals) {
166
- if (node.intrinsicDurationMs !== undefined) {
167
- maxDuration = Math.max(maxDuration, node.durationMs);
182
+ for (const child of this.childTemporals) {
183
+ // fit timegroups look "up" to their parent timegroup for their duration
184
+ // so we need to skip them to avoid an infinite loop
185
+ if (child instanceof EFTimegroup && child.mode === "fit") {
186
+ continue;
187
+ }
188
+ if (!child.hasOwnDuration) {
189
+ continue;
168
190
  }
191
+ maxDuration = Math.max(maxDuration, child.durationMs);
169
192
  }
170
193
  return maxDuration;
171
194
  }