@editframe/elements 0.15.0-beta.17 → 0.15.0-beta.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.
Files changed (38) hide show
  1. package/dist/EF_FRAMEGEN.js +1 -1
  2. package/dist/elements/EFMedia.d.ts +7 -3
  3. package/dist/elements/EFMedia.js +45 -90
  4. package/dist/elements/EFTemporal.browsertest.d.ts +4 -3
  5. package/dist/elements/EFTemporal.d.ts +14 -11
  6. package/dist/elements/EFTemporal.js +63 -87
  7. package/dist/elements/EFTimegroup.d.ts +1 -3
  8. package/dist/elements/EFTimegroup.js +15 -103
  9. package/dist/elements/EFVideo.js +3 -1
  10. package/dist/elements/EFWaveform.d.ts +1 -0
  11. package/dist/elements/EFWaveform.js +6 -2
  12. package/dist/elements/durationConverter.d.ts +8 -8
  13. package/dist/elements/durationConverter.js +2 -2
  14. package/dist/elements/updateAnimations.d.ts +9 -0
  15. package/dist/elements/updateAnimations.js +62 -0
  16. package/dist/gui/EFFilmstrip.js +7 -16
  17. package/dist/gui/EFFitScale.d.ts +25 -0
  18. package/dist/gui/EFFitScale.js +123 -0
  19. package/dist/gui/EFWorkbench.d.ts +1 -5
  20. package/dist/gui/EFWorkbench.js +6 -55
  21. package/dist/gui/TWMixin.css.js +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +2 -0
  24. package/dist/style.css +3 -3
  25. package/package.json +2 -2
  26. package/src/elements/EFMedia.browsertest.ts +10 -10
  27. package/src/elements/EFMedia.ts +56 -118
  28. package/src/elements/EFTemporal.browsertest.ts +64 -31
  29. package/src/elements/EFTemporal.ts +99 -119
  30. package/src/elements/EFTimegroup.ts +15 -133
  31. package/src/elements/EFVideo.ts +3 -1
  32. package/src/elements/EFWaveform.ts +5 -2
  33. package/src/elements/durationConverter.ts +9 -4
  34. package/src/elements/updateAnimations.ts +88 -0
  35. package/src/gui/EFFilmstrip.ts +7 -16
  36. package/src/gui/EFFitScale.ts +133 -0
  37. package/src/gui/EFWorkbench.ts +7 -64
  38. package/types.json +1 -1
@@ -87,31 +87,22 @@ class FilmstripItem extends TWMixin(LitElement) {
87
87
  @property({ type: Number })
88
88
  pixelsPerMs = 0.04;
89
89
 
90
+ // Gutter styles represent the entire source media.
91
+ // If there is no trim, then the gutter and trim portion are the same.
90
92
  get gutterStyles() {
91
- if (this.element.sourceInMs || this.element.sourceOutMs) {
92
- return {
93
- position: "relative",
94
- left: `${this.pixelsPerMs * (this.element.startTimeWithinParentMs - this.element.trimStartMs - this.element.sourceInMs)}px`,
95
- width: `${this.pixelsPerMs * (this.element.durationMs + this.element.trimStartMs + this.element.trimEndMs + this.element.sourceOutMs + this.element.sourceInMs)}px`,
96
- };
97
- }
98
93
  return {
99
94
  position: "relative",
100
- left: `${this.pixelsPerMs * (this.element.startTimeWithinParentMs - this.element.trimStartMs)}px`,
101
- width: `${this.pixelsPerMs * (this.element.durationMs + this.element.trimStartMs + this.element.trimEndMs)}px`,
95
+ left: `${this.pixelsPerMs * (this.element.startTimeWithinParentMs - this.element.sourceStartMs)}px`,
96
+ width: `${this.pixelsPerMs * (this.element.intrinsicDurationMs ?? this.element.durationMs)}px`,
102
97
  };
103
98
  }
104
99
 
100
+ // Trim portion is the section of source that will be placed in the timeline
101
+ // If there is no trim, then the gutter and trim portion are the same.
105
102
  get trimPortionStyles() {
106
- if (this.element.sourceInMs || this.element.sourceOutMs) {
107
- return {
108
- width: `${this.pixelsPerMs * this.element.durationMs}px`,
109
- left: `${this.pixelsPerMs * (this.element.trimStartMs + this.element.sourceInMs)}px`,
110
- };
111
- }
112
103
  return {
113
104
  width: `${this.pixelsPerMs * this.element.durationMs}px`,
114
- left: `${this.pixelsPerMs * this.element.trimStartMs}px`,
105
+ left: `${this.pixelsPerMs * this.element.sourceStartMs}px`,
115
106
  };
116
107
  }
117
108
 
@@ -0,0 +1,133 @@
1
+ import { LitElement } from "lit";
2
+ import { customElement, state } from "lit/decorators.js";
3
+ import { createRef } from "lit/directives/ref.js";
4
+
5
+ @customElement("ef-fit-scale")
6
+ export class EFFitScale extends LitElement {
7
+ containerRef = createRef<HTMLDivElement>();
8
+ contentRef = createRef<HTMLSlotElement>();
9
+
10
+ createRenderRoot() {
11
+ Object.assign(this.style, {
12
+ display: "grid",
13
+ width: "100%",
14
+ height: "100%",
15
+ gridTemplateColumns: "100%",
16
+ gridTemplateRows: "100%",
17
+ overflow: "hidden",
18
+ boxSizing: "border-box",
19
+ contain: "strict",
20
+ position: "relative",
21
+ });
22
+ this.id = `${this.uniqueId}`;
23
+ return this;
24
+ }
25
+
26
+ uniqueId = Math.random().toString(36).substring(2, 15);
27
+
28
+ @state()
29
+ private scale = 1;
30
+
31
+ private animationFrameId?: number;
32
+
33
+ get contentChild() {
34
+ const firstElement = this.children[0];
35
+ if (!firstElement) return null;
36
+
37
+ let current: Element = firstElement;
38
+ while (current) {
39
+ if (current instanceof HTMLSlotElement) {
40
+ const assigned = current.assignedElements()[0];
41
+ if (!assigned) break;
42
+ current = assigned;
43
+ continue;
44
+ }
45
+
46
+ const display = window.getComputedStyle(current).display;
47
+ if (display !== "contents" && display !== "none") {
48
+ return current as HTMLElement;
49
+ }
50
+ const firstChild = current.children[0];
51
+ if (!firstChild) break;
52
+ current = firstChild;
53
+ }
54
+ return firstElement as HTMLElement; // Fallback to first element if no non-contents found
55
+ }
56
+
57
+ get scaleInfo() {
58
+ if (!this.contentChild) {
59
+ return {
60
+ scale: 1,
61
+ containerWidth: 0,
62
+ containerHeight: 0,
63
+ contentWidth: 0,
64
+ contentHeight: 0,
65
+ };
66
+ }
67
+
68
+ const containerWidth = this.clientWidth;
69
+ const containerHeight = this.clientHeight;
70
+ const contentWidth = this.contentChild.clientWidth;
71
+ const contentHeight = this.contentChild.clientHeight;
72
+
73
+ const containerRatio = containerWidth / containerHeight;
74
+ const contentRatio = contentWidth / contentHeight;
75
+
76
+ const scale =
77
+ containerRatio > contentRatio
78
+ ? containerHeight / contentHeight
79
+ : containerWidth / contentWidth;
80
+
81
+ return {
82
+ scale,
83
+ containerWidth,
84
+ containerHeight,
85
+ contentWidth,
86
+ contentHeight,
87
+ };
88
+ }
89
+
90
+ setScale = () => {
91
+ if (this.isConnected) {
92
+ const { scale } = this.scaleInfo;
93
+ if (this.contentChild) {
94
+ const containerRect = this.getBoundingClientRect();
95
+ const contentRect = this.contentChild.getBoundingClientRect();
96
+
97
+ const unscaledWidth = contentRect.width / this.scale;
98
+ const unscaledHeight = contentRect.height / this.scale;
99
+ const scaledWidth = unscaledWidth * scale;
100
+ const scaledHeight = unscaledHeight * scale;
101
+ const translateX = (containerRect.width - scaledWidth) / 2;
102
+ const translateY = (containerRect.height - scaledHeight) / 2;
103
+
104
+ // Use toFixed to avoid floating point precision issues
105
+ // this will update every frame with sub-pixel changes if we don't pin it down
106
+ Object.assign(this.contentChild.style, {
107
+ transform: `translate(${translateX.toFixed(4)}px, ${translateY.toFixed(4)}px) scale(${scale.toFixed(4)})`,
108
+ transformOrigin: "top left",
109
+ });
110
+ this.scale = scale;
111
+ }
112
+ this.animationFrameId = requestAnimationFrame(this.setScale);
113
+ }
114
+ };
115
+
116
+ connectedCallback(): void {
117
+ super.connectedCallback();
118
+ this.animationFrameId = requestAnimationFrame(this.setScale);
119
+ }
120
+
121
+ disconnectedCallback(): void {
122
+ super.disconnectedCallback();
123
+ if (this.animationFrameId) {
124
+ cancelAnimationFrame(this.animationFrameId);
125
+ }
126
+ }
127
+ }
128
+
129
+ declare global {
130
+ interface HTMLElementTagNameMap {
131
+ "ef-fit-scale": EFFitScale;
132
+ }
133
+ }
@@ -1,5 +1,5 @@
1
1
  import { LitElement, type PropertyValueMap, css, html } from "lit";
2
- import { customElement, eventOptions, state } from "lit/decorators.js";
2
+ import { customElement, eventOptions } from "lit/decorators.js";
3
3
  import { createRef, ref } from "lit/directives/ref.js";
4
4
 
5
5
  import { ContextMixin } from "./ContextMixin.js";
@@ -17,69 +17,19 @@ export class EFWorkbench extends ContextMixin(TWMixin(LitElement)) {
17
17
  `,
18
18
  ];
19
19
 
20
- stageRef = createRef<HTMLDivElement>();
21
- canvasRef = createRef<HTMLSlotElement>();
20
+ focusOverlay = createRef<HTMLDivElement>();
22
21
 
23
22
  @eventOptions({ passive: false, capture: true })
24
23
  handleStageWheel(event: WheelEvent) {
25
24
  event.preventDefault();
26
25
  }
27
26
 
28
- focusOverlay = createRef<HTMLDivElement>();
29
-
30
- @state()
31
- private stageScale = 1;
32
-
33
- setStageScale = () => {
34
- if (this.isConnected && !this.rendering) {
35
- const canvasElement = this.canvasRef.value;
36
- const stageElement = this.stageRef.value;
37
- const canvasChild = canvasElement?.assignedElements()[0];
38
- if (stageElement && canvasElement && canvasChild) {
39
- // Determine the appropriate scale factor to make the canvas fit into
40
- canvasElement.style.width = `${canvasChild.clientWidth}px`;
41
- canvasElement.style.height = `${canvasChild.clientHeight}px`;
42
- const stageWidth = stageElement.clientWidth;
43
- const stageHeight = stageElement.clientHeight;
44
- const canvasWidth = canvasElement.clientWidth;
45
- const canvasHeight = canvasElement.clientHeight;
46
- const stageRatio = stageWidth / stageHeight;
47
- const canvasRatio = canvasWidth / canvasHeight;
48
-
49
- if (stageRatio > canvasRatio) {
50
- const scale = stageHeight / canvasHeight;
51
- if (this.stageScale !== scale) {
52
- canvasElement.style.transform = `scale(${scale})`;
53
- canvasElement.style.transformOrigin = "top center";
54
- }
55
- this.stageScale = scale;
56
- } else {
57
- const scale = stageWidth / canvasWidth;
58
- if (this.stageScale !== scale) {
59
- canvasElement.style.transform = `scale(${scale})`;
60
- canvasElement.style.transformOrigin = "center";
61
- }
62
- this.stageScale = scale;
63
- }
64
- }
65
- }
66
- if (this.isConnected) {
67
- requestAnimationFrame(this.setStageScale);
68
- }
69
- };
70
-
71
27
  connectedCallback(): void {
72
- // Set the body and html to 100% width and height to avoid scaling issues
73
- // this is a hack to avoid scaling issues when the stage is scaled and during output rendering
74
- // What I've discovered recently is that having the workbench be so smart as to determine
75
- // how it should be displayed at render time causes problems. Much of that has been extracted
76
- // but this is a quick fix to avoid scaling issues.
77
28
  document.body.style.width = "100%";
78
29
  document.body.style.height = "100%";
79
30
  document.documentElement.style.width = "100%";
80
31
  document.documentElement.style.height = "100%";
81
32
  super.connectedCallback();
82
- requestAnimationFrame(this.setStageScale);
83
33
  }
84
34
 
85
35
  disconnectedCallback(): void {
@@ -123,11 +73,7 @@ export class EFWorkbench extends ContextMixin(TWMixin(LitElement)) {
123
73
  render() {
124
74
  if (this.rendering) {
125
75
  return html`
126
- <slot
127
- ${ref(this.canvasRef)}
128
- class="fixed inset-0 h-full w-full"
129
- name="canvas"
130
- ></slot>
76
+ <slot class="fixed inset-0 h-full w-full" name="canvas"></slot>
131
77
  `;
132
78
  }
133
79
  return html`
@@ -136,15 +82,12 @@ export class EFWorkbench extends ContextMixin(TWMixin(LitElement)) {
136
82
  style="grid-template-rows: 1fr 300px; grid-template-columns: 100%;"
137
83
  >
138
84
  <div
139
- ${ref(this.stageRef)}
140
- class="relative grid h-full w-full justify-center overflow-hidden"
85
+ class="relative h-full w-full overflow-hidden"
141
86
  @wheel=${this.handleStageWheel}
142
87
  >
143
- <slot
144
- ${ref(this.canvasRef)}
145
- name="canvas"
146
- class="inline-block"
147
- ></slot>
88
+ <ef-fit-scale class="h-full grid place-content-center">
89
+ <slot name="canvas" class="contents"></slot>
90
+ </ef-fit-scale>
148
91
  <div
149
92
  class="border border-blue-500 bg-blue-200 bg-opacity-20 absolute"
150
93
  ${ref(this.focusOverlay)}