lexgui 0.1.39 → 0.1.41
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/README.md +3 -1
- package/build/components/audio.js +63 -26
- package/build/components/codeeditor.js +4 -4
- package/build/components/imui.js +1 -1
- package/build/components/nodegraph.js +2 -2
- package/build/components/timeline.js +61 -54
- package/build/components/videoeditor.js +43 -10
- package/build/lexgui.css +43 -0
- package/build/lexgui.js +313 -152
- package/build/lexgui.module.js +316 -155
- package/changelog.md +23 -2
- package/demo.js +16 -9
- package/examples/asset_view.html +10 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# lexgui.js
|
|
2
2
|
|
|
3
|
-
**lexgui.js** is a lightweight JavaScript library that allows you to create web interfaces using only JavaScript, HTML, and CSS. It provides an easy
|
|
3
|
+
**lexgui.js** is a lightweight JavaScript library that allows you to create web interfaces using only JavaScript, HTML, and CSS. It provides an easy API for building dynamic and interactive editor interfaces without the need for tedious frameworks or big libraries. With lexgui.js, you can create custom UI components, handle user interactions, and update the interface dynamically.
|
|
4
|
+
|
|
5
|
+
NPM Package: [npmjs.com/package/lexgui](https://www.npmjs.com/package/lexgui)
|
|
4
6
|
|
|
5
7
|
<table>
|
|
6
8
|
<tr>
|
|
@@ -44,12 +44,29 @@ Panel.prototype.addKnob = function( name, value, min, max, callback, options = {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
const snapEnabled = ( options.snap && options.snap.constructor == Number );
|
|
48
|
+
const ticks = [];
|
|
49
|
+
if( snapEnabled )
|
|
50
|
+
{
|
|
51
|
+
const range = (max - min) / options.snap;
|
|
52
|
+
for( let i = 0; i < ( options.snap + 1 ); ++i )
|
|
53
|
+
{
|
|
54
|
+
ticks.push( min + (i * range) );
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
var container = document.createElement( 'div' );
|
|
48
|
-
container.className = "lexknob
|
|
59
|
+
container.className = "lexknob";
|
|
60
|
+
container.addClass( options.size );
|
|
61
|
+
container.addClass( snapEnabled ? "show-ticks" : null );
|
|
49
62
|
container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
|
|
50
63
|
|
|
51
64
|
let knobCircle = document.createElement( 'div' );
|
|
52
65
|
knobCircle.className = "knobcircle";
|
|
66
|
+
if( snapEnabled )
|
|
67
|
+
{
|
|
68
|
+
knobCircle.style.setProperty( "--knob-snap-mark", ( 270 / options.snap ) + "deg" );
|
|
69
|
+
}
|
|
53
70
|
|
|
54
71
|
let innerKnobCircle = document.createElement( 'div' );
|
|
55
72
|
innerKnobCircle.className = "innerknobcircle";
|
|
@@ -69,6 +86,7 @@ Panel.prototype.addKnob = function( name, value, min, max, callback, options = {
|
|
|
69
86
|
|
|
70
87
|
innerKnobCircle.value = innerKnobCircle.iValue = value;
|
|
71
88
|
|
|
89
|
+
let mustSnap = false;
|
|
72
90
|
let innerSetValue = function( v ) {
|
|
73
91
|
// Convert val between (-135 and 135)
|
|
74
92
|
const angle = LX.remapRange( v, innerKnobCircle.min, innerKnobCircle.max, -135.0, 135.0 );
|
|
@@ -81,29 +99,20 @@ Panel.prototype.addKnob = function( name, value, min, max, callback, options = {
|
|
|
81
99
|
|
|
82
100
|
if( options.disabled )
|
|
83
101
|
{
|
|
84
|
-
|
|
102
|
+
container.addClass( "disabled" );
|
|
85
103
|
}
|
|
86
104
|
|
|
87
|
-
// Add wheel input
|
|
88
|
-
|
|
89
|
-
innerKnobCircle.addEventListener( "wheel", function( e ) {
|
|
90
|
-
e.preventDefault();
|
|
91
|
-
if( this !== document.activeElement )
|
|
92
|
-
return;
|
|
93
|
-
let mult = options.step ?? 1;
|
|
94
|
-
if( e.shiftKey ) mult *= 10;
|
|
95
|
-
else if( e.altKey ) mult *= 0.1;
|
|
96
|
-
let new_value = ( this.value - mult * ( e.deltaY > 0 ? 1 : -1 ) );
|
|
97
|
-
this.value = new_value;// .toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
|
|
98
|
-
Panel._dispatch_event( innerKnobCircle, "change" );
|
|
99
|
-
}, { passive: false });
|
|
100
|
-
|
|
101
105
|
innerKnobCircle.addEventListener( "change", e => {
|
|
102
106
|
|
|
103
107
|
const knob = e.target;
|
|
104
108
|
|
|
105
109
|
const skipCallback = e.detail;
|
|
106
110
|
|
|
111
|
+
if( mustSnap )
|
|
112
|
+
{
|
|
113
|
+
knob.value = ticks.reduce(( prev, curr ) => Math.abs( curr - knob.value ) < Math.abs( prev - knob.value ) ? curr : prev );
|
|
114
|
+
}
|
|
115
|
+
|
|
107
116
|
let val = knob.value = LX.clamp( knob.value, knob.min, knob.max );
|
|
108
117
|
val = options.precision ? LX.round( val, options.precision ) : val;
|
|
109
118
|
|
|
@@ -114,9 +123,14 @@ Panel.prototype.addKnob = function( name, value, min, max, callback, options = {
|
|
|
114
123
|
{
|
|
115
124
|
let btn = element.querySelector( ".lexwidgetname .lexicon" );
|
|
116
125
|
if( btn ) btn.style.display = val != innerKnobCircle.iValue ? "block": "none";
|
|
126
|
+
|
|
127
|
+
if( !( snapEnabled && !mustSnap ) )
|
|
128
|
+
{
|
|
129
|
+
this._trigger( new LX.IEvent( name, val, e ), callback );
|
|
130
|
+
mustSnap = false;
|
|
131
|
+
}
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
if( !skipCallback ) this._trigger( new LX.IEvent( name, val, e ), callback );
|
|
120
134
|
}, { passive: false });
|
|
121
135
|
|
|
122
136
|
// Add drag input
|
|
@@ -124,41 +138,64 @@ Panel.prototype.addKnob = function( name, value, min, max, callback, options = {
|
|
|
124
138
|
innerKnobCircle.addEventListener( "mousedown", inner_mousedown );
|
|
125
139
|
|
|
126
140
|
var that = this;
|
|
127
|
-
|
|
141
|
+
|
|
128
142
|
function inner_mousedown( e ) {
|
|
129
|
-
|
|
143
|
+
|
|
144
|
+
if( document.activeElement == innerKnobCircle || options.disabled )
|
|
145
|
+
{
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
130
149
|
var doc = that.root.ownerDocument;
|
|
131
150
|
doc.addEventListener("mousemove",inner_mousemove);
|
|
132
151
|
doc.addEventListener("mouseup",inner_mouseup);
|
|
133
|
-
lastY = e.pageY;
|
|
134
|
-
document.body.classList.add('nocursor');
|
|
135
152
|
document.body.classList.add('noevents');
|
|
153
|
+
|
|
154
|
+
if( !document.pointerLockElement )
|
|
155
|
+
{
|
|
156
|
+
container.requestPointerLock();
|
|
157
|
+
}
|
|
158
|
+
|
|
136
159
|
e.stopImmediatePropagation();
|
|
137
160
|
e.stopPropagation();
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
function inner_mousemove( e ) {
|
|
141
|
-
|
|
142
|
-
|
|
164
|
+
|
|
165
|
+
let dt = -e.movementY;
|
|
166
|
+
|
|
167
|
+
if ( dt != 0 )
|
|
168
|
+
{
|
|
143
169
|
let mult = options.step ?? 1;
|
|
144
170
|
if(e.shiftKey) mult *= 10;
|
|
145
171
|
else if(e.altKey) mult *= 0.1;
|
|
146
172
|
let new_value = (innerKnobCircle.value - mult * dt);
|
|
147
|
-
innerKnobCircle.value = new_value
|
|
173
|
+
innerKnobCircle.value = new_value;
|
|
148
174
|
Panel._dispatch_event( innerKnobCircle, 'change' );
|
|
149
175
|
}
|
|
150
176
|
|
|
151
|
-
lastY = e.pageY;
|
|
152
177
|
e.stopPropagation();
|
|
153
178
|
e.preventDefault();
|
|
154
179
|
}
|
|
155
180
|
|
|
156
181
|
function inner_mouseup( e ) {
|
|
182
|
+
|
|
157
183
|
var doc = that.root.ownerDocument;
|
|
158
184
|
doc.removeEventListener( 'mousemove', inner_mousemove );
|
|
159
185
|
doc.removeEventListener( 'mouseup', inner_mouseup );
|
|
160
|
-
document.body.classList.remove( 'nocursor' );
|
|
161
186
|
document.body.classList.remove( 'noevents' );
|
|
187
|
+
|
|
188
|
+
// Snap if necessary
|
|
189
|
+
if( snapEnabled )
|
|
190
|
+
{
|
|
191
|
+
mustSnap = true;
|
|
192
|
+
Panel._dispatch_event( innerKnobCircle, 'change' );
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if( document.pointerLockElement )
|
|
196
|
+
{
|
|
197
|
+
document.exitPointerLock();
|
|
198
|
+
}
|
|
162
199
|
}
|
|
163
200
|
|
|
164
201
|
container.appendChild( knobCircle );
|
|
@@ -3624,7 +3624,7 @@ class CodeEditor {
|
|
|
3624
3624
|
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
3625
3625
|
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
3626
3626
|
|
|
3627
|
-
this.hScrollbar.thumb._left = LX.
|
|
3627
|
+
this.hScrollbar.thumb._left = LX.clamp( value, 0, ( scrollBarWidth - scrollThumbWidth ) );
|
|
3628
3628
|
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left + "px";
|
|
3629
3629
|
|
|
3630
3630
|
// Scroll code
|
|
@@ -3646,7 +3646,7 @@ class CodeEditor {
|
|
|
3646
3646
|
const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
|
|
3647
3647
|
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
3648
3648
|
|
|
3649
|
-
this.vScrollbar.thumb._top = LX.
|
|
3649
|
+
this.vScrollbar.thumb._top = LX.clamp( value, 0, ( scrollBarHeight - scrollThumbHeight ) );
|
|
3650
3650
|
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top + "px";
|
|
3651
3651
|
|
|
3652
3652
|
// Scroll code
|
|
@@ -4222,7 +4222,7 @@ class CodeEditor {
|
|
|
4222
4222
|
var r = document.querySelector( ':root' );
|
|
4223
4223
|
var s = getComputedStyle( r );
|
|
4224
4224
|
var pixels = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
4225
|
-
pixels = LX.
|
|
4225
|
+
pixels = LX.clamp( pixels + 1, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
|
|
4226
4226
|
r.style.setProperty( "--code-editor-font-size", pixels + "px" );
|
|
4227
4227
|
this.charWidth = this._measureChar( "a", true );
|
|
4228
4228
|
|
|
@@ -4248,7 +4248,7 @@ class CodeEditor {
|
|
|
4248
4248
|
var r = document.querySelector( ':root' );
|
|
4249
4249
|
var s = getComputedStyle( r );
|
|
4250
4250
|
var pixels = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
4251
|
-
pixels = LX.
|
|
4251
|
+
pixels = LX.clamp( pixels - 1, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
|
|
4252
4252
|
r.style.setProperty( "--code-editor-font-size", pixels + "px" );
|
|
4253
4253
|
this.charWidth = this._measureChar( "a", true );
|
|
4254
4254
|
|
package/build/components/imui.js
CHANGED
|
@@ -1728,7 +1728,7 @@ class GraphEditor {
|
|
|
1728
1728
|
if( delta > 0.0 ) this.currentGraph.scale *= 0.9;
|
|
1729
1729
|
else this.currentGraph.scale *= ( 1.0 / 0.9 );
|
|
1730
1730
|
|
|
1731
|
-
this.currentGraph.scale = LX.
|
|
1731
|
+
this.currentGraph.scale = LX.clamp( this.currentGraph.scale, GraphEditor.MIN_SCALE, GraphEditor.MAX_SCALE );
|
|
1732
1732
|
|
|
1733
1733
|
// Compute zoom center in pattern space using new scale
|
|
1734
1734
|
// and get delta..
|
|
@@ -2218,7 +2218,7 @@ class GraphEditor {
|
|
|
2218
2218
|
startPos = this._getPatternPosition( startPos );
|
|
2219
2219
|
endPos = this._getPatternPosition( endPos );
|
|
2220
2220
|
|
|
2221
|
-
const distanceX = LX.
|
|
2221
|
+
const distanceX = LX.clamp( Math.abs( startPos.x - endPos.x ), 0.0, 256.0 );
|
|
2222
2222
|
const cPDistance = 128.0 * Math.pow( distanceX / 256.0, 0.5 );
|
|
2223
2223
|
|
|
2224
2224
|
let cPoint1 = startPos.add( new LX.vec2( cPDistance, 0 ) );
|
|
@@ -62,7 +62,7 @@ class Timeline {
|
|
|
62
62
|
this.onBeforeCreateTopBar = options.onBeforeCreateTopBar;
|
|
63
63
|
this.onAfterCreateTopBar = options.onAfterCreateTopBar;
|
|
64
64
|
this.onChangePlayMode = options.onChangePlayMode;
|
|
65
|
-
this.
|
|
65
|
+
this.onShowConfiguration = options.onShowConfiguration;
|
|
66
66
|
this.onBeforeDrawContent = options.onBeforeDrawContent;
|
|
67
67
|
|
|
68
68
|
this.playing = false;
|
|
@@ -107,7 +107,8 @@ class Timeline {
|
|
|
107
107
|
let height = options.height ? options.height - this.header_offset : null;
|
|
108
108
|
|
|
109
109
|
let area = new LX.Area( {id: "bottom-timeline-area", width: width || "calc(100% - 7px)", height: height || "100%"});
|
|
110
|
-
area.split({ type: "horizontal", sizes: ["15%", "85%"]});
|
|
110
|
+
area.split({ type: "horizontal", sizes: ["15%", "85%"] });
|
|
111
|
+
area.split_bar.style.zIndex = 1; // for some reason this is needed here
|
|
111
112
|
this.content_area = area;
|
|
112
113
|
let [left, right] = area.sections;
|
|
113
114
|
|
|
@@ -197,23 +198,24 @@ class Timeline {
|
|
|
197
198
|
if(this.onShowOptimizeMenu)
|
|
198
199
|
header.addButton("", '<i class="fa-solid fa-filter"></i>', (value, event) => {this.onShowOptimizeMenu(event)}, {width: "40px"});
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this.configurationDialog
|
|
203
|
-
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
this.configurationDialog = new LX.Dialog("Configuration", d => {
|
|
207
|
-
if ( this.onConfiguration ){
|
|
208
|
-
this.onConfiguration(d);
|
|
209
|
-
}
|
|
210
|
-
}, {
|
|
211
|
-
onclose: (root) => {
|
|
212
|
-
root.remove();
|
|
201
|
+
if(this.onShowConfiguration){
|
|
202
|
+
header.addButton("", '<i class="fa-solid fa-gear"></i>', (value, event) => {
|
|
203
|
+
if(this.configurationDialog){
|
|
204
|
+
this.configurationDialog.close();
|
|
213
205
|
this.configurationDialog = null;
|
|
206
|
+
return;
|
|
214
207
|
}
|
|
215
|
-
|
|
216
|
-
|
|
208
|
+
this.configurationDialog = new LX.Dialog("Configuration", dialog => {
|
|
209
|
+
this.onShowConfiguration(dialog);
|
|
210
|
+
}, {
|
|
211
|
+
onclose: (root) => {
|
|
212
|
+
this.configurationDialog.panel.clear(); // clear signals
|
|
213
|
+
this.configurationDialog = null;
|
|
214
|
+
root.remove();
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
}, {width: "40px"})
|
|
218
|
+
}
|
|
217
219
|
|
|
218
220
|
header.endLine();
|
|
219
221
|
LX.DEFAULT_NAME_WIDTH = "30%";
|
|
@@ -509,19 +511,14 @@ class Timeline {
|
|
|
509
511
|
let max_tracks = Math.ceil( (h - timeline_height + this.currentScrollInPixels) / line_height );
|
|
510
512
|
|
|
511
513
|
ctx.save();
|
|
512
|
-
ctx.fillStyle = Timeline.
|
|
513
|
-
|
|
514
|
+
ctx.fillStyle = "#f0f0f003"//Timeline.TRACK_COLOR_SECONDARY;
|
|
515
|
+
ctx.globalAlpha = 1;
|
|
516
|
+
for(let i = 0; i <= max_tracks; i+=2)
|
|
514
517
|
{
|
|
515
|
-
ctx.fillStyle = i % 2 == 0 ? Timeline.TRACK_COLOR_PRIMARY: Timeline.BACKGROUND_COLOR;
|
|
516
518
|
ctx.fillRect(0, timeline_height + i * line_height - this.currentScrollInPixels, w, line_height );
|
|
517
519
|
}
|
|
518
|
-
|
|
519
|
-
//black bg
|
|
520
|
-
ctx.globalAlpha = 0.7;
|
|
521
|
-
ctx.fillStyle = Timeline.BACKGROUND_COLOR;
|
|
522
|
-
ctx.fillRect( margin, 0, canvas.width - margin, canvas.height);
|
|
523
520
|
ctx.globalAlpha = this.opacity;
|
|
524
|
-
|
|
521
|
+
|
|
525
522
|
//bg lines
|
|
526
523
|
ctx.strokeStyle = "#444";
|
|
527
524
|
ctx.beginPath();
|
|
@@ -529,6 +526,7 @@ class Timeline {
|
|
|
529
526
|
let pos = this.timeToX( 0 );
|
|
530
527
|
if(pos < margin)
|
|
531
528
|
pos = margin;
|
|
529
|
+
ctx.lineWidth = 1;
|
|
532
530
|
ctx.moveTo( pos + 0.5, timeline_height);
|
|
533
531
|
ctx.lineTo( pos + 0.5, canvas.height);
|
|
534
532
|
ctx.moveTo( Math.round( this.timeToX( duration ) ) + 0.5, timeline_height);
|
|
@@ -704,7 +702,7 @@ class Timeline {
|
|
|
704
702
|
* @param {Number} t
|
|
705
703
|
*/
|
|
706
704
|
|
|
707
|
-
setDuration( t, updateHeader = true ) {
|
|
705
|
+
setDuration( t, updateHeader = true, skipCallback = false ) {
|
|
708
706
|
let v = this.validateDuration(t);
|
|
709
707
|
let decimals = t.toString().split('.')[1] ? t.toString().split('.')[1].length : 0;
|
|
710
708
|
updateHeader = (updateHeader || +v.toFixed(decimals) != t);
|
|
@@ -714,7 +712,7 @@ class Timeline {
|
|
|
714
712
|
LX.emit( "@on_set_duration_" + this.name, +this.duration.toFixed(3)); // skipcallback = true
|
|
715
713
|
}
|
|
716
714
|
|
|
717
|
-
if( this.onSetDuration )
|
|
715
|
+
if( this.onSetDuration && !skipCallback )
|
|
718
716
|
this.onSetDuration( this.duration );
|
|
719
717
|
}
|
|
720
718
|
|
|
@@ -733,19 +731,19 @@ class Timeline {
|
|
|
733
731
|
* @param {Number} speed
|
|
734
732
|
*/
|
|
735
733
|
|
|
736
|
-
setSpeed(speed) {
|
|
734
|
+
setSpeed(speed, skipCallback = false) {
|
|
737
735
|
this.speed = speed;
|
|
738
736
|
LX.emit( "@on_set_speed_" + this.name, +this.speed.toFixed(3)); // skipcallback = true
|
|
739
737
|
|
|
740
|
-
if( this.onSetSpeed )
|
|
738
|
+
if( this.onSetSpeed && !skipCallback)
|
|
741
739
|
this.onSetSpeed( this.speed );
|
|
742
740
|
}
|
|
743
741
|
|
|
744
|
-
setTime(time){
|
|
742
|
+
setTime(time, skipCallback = false ){
|
|
745
743
|
this.currentTime = Math.max(0,Math.min(time,this.duration));
|
|
746
744
|
LX.emit( "@on_set_time_" + this.name, +this.currentTime.toFixed(2)); // skipcallback = true
|
|
747
745
|
|
|
748
|
-
if(this.onSetTime)
|
|
746
|
+
if(this.onSetTime && !skipCallback)
|
|
749
747
|
this.onSetTime(this.currentTime);
|
|
750
748
|
}
|
|
751
749
|
|
|
@@ -832,6 +830,9 @@ class Timeline {
|
|
|
832
830
|
this.leftPanel.root.children[1].scrollTop += e.deltaY; // wheel deltaY
|
|
833
831
|
}
|
|
834
832
|
|
|
833
|
+
if ( this.onMouse ){
|
|
834
|
+
this.onMouse(e, time);
|
|
835
|
+
}
|
|
835
836
|
return;
|
|
836
837
|
}
|
|
837
838
|
|
|
@@ -969,8 +970,8 @@ class Timeline {
|
|
|
969
970
|
return true;
|
|
970
971
|
}
|
|
971
972
|
|
|
972
|
-
if( this.onMouse
|
|
973
|
-
|
|
973
|
+
if( this.onMouse )
|
|
974
|
+
this.onMouse( e, time, this );
|
|
974
975
|
|
|
975
976
|
return true;
|
|
976
977
|
}
|
|
@@ -2572,14 +2573,14 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2572
2573
|
|
|
2573
2574
|
const currentSelection = this.selectKeyFrame(t, keyFrameIndex, !multiple); // changes time
|
|
2574
2575
|
|
|
2576
|
+
if( !multiple ) {
|
|
2577
|
+
this.setTime(this.animationClip.tracks[t.clipIdx].times[ keyFrameIndex ]);
|
|
2578
|
+
}
|
|
2575
2579
|
if( this.onSelectKeyFrame && this.onSelectKeyFrame(e, currentSelection)) {
|
|
2576
2580
|
// Event handled
|
|
2577
2581
|
return;
|
|
2578
2582
|
}
|
|
2579
|
-
|
|
2580
|
-
if( !multiple ) {
|
|
2581
|
-
this.setTime(this.animationClip.tracks[t.clipIdx].times[ keyFrameIndex ]);
|
|
2582
|
-
}
|
|
2583
|
+
|
|
2583
2584
|
}
|
|
2584
2585
|
|
|
2585
2586
|
/**
|
|
@@ -2605,7 +2606,6 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2605
2606
|
/**
|
|
2606
2607
|
* @method clearTrack
|
|
2607
2608
|
*/
|
|
2608
|
-
|
|
2609
2609
|
clearTrack(idx, defaultValue) {
|
|
2610
2610
|
|
|
2611
2611
|
let track = this.animationClip.tracks[idx];
|
|
@@ -2615,11 +2615,14 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2615
2615
|
return;
|
|
2616
2616
|
}
|
|
2617
2617
|
|
|
2618
|
+
this.unHoverAll();
|
|
2619
|
+
this.unSelectAllKeyFrames();
|
|
2620
|
+
|
|
2621
|
+
this.saveState(track.clipIdx);
|
|
2618
2622
|
const count = track.times.length;
|
|
2619
2623
|
for(let i = count - 1; i >= 0; i--)
|
|
2620
2624
|
{
|
|
2621
|
-
this
|
|
2622
|
-
this.#delete(track.clipIdx, i );
|
|
2625
|
+
this.#delete(track.clipIdx, i);
|
|
2623
2626
|
}
|
|
2624
2627
|
if(defaultValue != undefined) {
|
|
2625
2628
|
if(typeof(defaultValue) == 'number') {
|
|
@@ -2855,7 +2858,10 @@ class ClipsTimeline extends Timeline {
|
|
|
2855
2858
|
onMouseMove( e, time ) {
|
|
2856
2859
|
// function not called if shift is pressed (boxselection)
|
|
2857
2860
|
|
|
2858
|
-
if(this.
|
|
2861
|
+
if ( this.grabbingTimeBar || this.grabbingScroll ){
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
else if(this.grabbing && e.buttons != 2) {
|
|
2859
2865
|
this.unHoverAll();
|
|
2860
2866
|
|
|
2861
2867
|
let delta = time - this.grabTime;
|
|
@@ -2879,7 +2885,8 @@ class ClipsTimeline extends Timeline {
|
|
|
2879
2885
|
duration = Math.min( track.clips[this.lastClipsSelected[0][1] + 1].start - clip.start - 0.0001, duration );
|
|
2880
2886
|
}
|
|
2881
2887
|
clip.duration = duration;
|
|
2882
|
-
clip.fadeout = Math.max(Math.min((clip.fadeout
|
|
2888
|
+
clip.fadeout = Math.max(Math.min((clip.fadeout ?? (clip.start+clip.duration)) + delta, clip.start+clip.duration), clip.start);
|
|
2889
|
+
clip.fadein = Math.max(Math.min((clip.fadein ?? (clip.start+clip.duration)), (clip.fadeout ?? (clip.start+clip.duration))), clip.start);
|
|
2883
2890
|
if(this.duration < clip.start + clip.duration){
|
|
2884
2891
|
this.setDuration(clip.start + clip.duration);
|
|
2885
2892
|
}
|
|
@@ -3258,16 +3265,13 @@ class ClipsTimeline extends Timeline {
|
|
|
3258
3265
|
/**
|
|
3259
3266
|
* @method optimizeTrack
|
|
3260
3267
|
*/
|
|
3261
|
-
|
|
3262
3268
|
optimizeTrack(trackIdx) {
|
|
3263
3269
|
}
|
|
3264
3270
|
|
|
3265
3271
|
/**
|
|
3266
3272
|
* @method optimizeTracks
|
|
3267
3273
|
*/
|
|
3268
|
-
|
|
3269
3274
|
optimizeTracks() {
|
|
3270
|
-
this.addClip()
|
|
3271
3275
|
}
|
|
3272
3276
|
|
|
3273
3277
|
/**
|
|
@@ -5154,14 +5158,14 @@ class CurvesTimeline extends Timeline {
|
|
|
5154
5158
|
|
|
5155
5159
|
const currentSelection = this.selectKeyFrame(t, keyFrameIndex, !multiple, multiple); // changes time on the first keyframe selected
|
|
5156
5160
|
|
|
5161
|
+
if (!multiple){
|
|
5162
|
+
this.setTime(this.animationClip.tracks[t.clipIdx].times[ keyFrameIndex ]);
|
|
5163
|
+
}
|
|
5164
|
+
|
|
5157
5165
|
if( this.onSelectKeyFrame && this.onSelectKeyFrame(e, currentSelection)) {
|
|
5158
5166
|
// Event handled
|
|
5159
5167
|
return;
|
|
5160
5168
|
}
|
|
5161
|
-
|
|
5162
|
-
if (!multiple){
|
|
5163
|
-
this.setTime(this.animationClip.tracks[t.clipIdx].times[ keyFrameIndex ]);
|
|
5164
|
-
}
|
|
5165
5169
|
}
|
|
5166
5170
|
|
|
5167
5171
|
/**
|
|
@@ -5195,12 +5199,15 @@ class CurvesTimeline extends Timeline {
|
|
|
5195
5199
|
{
|
|
5196
5200
|
return;
|
|
5197
5201
|
}
|
|
5198
|
-
|
|
5202
|
+
|
|
5203
|
+
this.unHoverAll();
|
|
5204
|
+
this.unSelectAllKeyFrames();
|
|
5205
|
+
|
|
5206
|
+
this.saveState(track.clipIdx);
|
|
5199
5207
|
const count = track.times.length;
|
|
5200
5208
|
for(let i = count - 1; i >= 0; i--)
|
|
5201
5209
|
{
|
|
5202
|
-
this
|
|
5203
|
-
this.#delete(track, i );
|
|
5210
|
+
this.#delete(track.clipIdx, i );
|
|
5204
5211
|
}
|
|
5205
5212
|
if(defaultValue != undefined) {
|
|
5206
5213
|
if(typeof(defaultValue) == 'number') {
|
|
@@ -5292,4 +5299,4 @@ LX.UTILS.concatTypedArray = (arrays, ArrayType) => {
|
|
|
5292
5299
|
return result;
|
|
5293
5300
|
}
|
|
5294
5301
|
|
|
5295
|
-
export { Timeline, KeyFramesTimeline, ClipsTimeline, CurvesTimeline };
|
|
5302
|
+
export { Timeline, KeyFramesTimeline, ClipsTimeline, CurvesTimeline };
|
|
@@ -359,16 +359,19 @@ class VideoEditor {
|
|
|
359
359
|
this.startTimeString = "0:0";
|
|
360
360
|
this.endTimeString = "0:0";
|
|
361
361
|
|
|
362
|
-
|
|
362
|
+
this.mainArea = area;
|
|
363
|
+
|
|
364
|
+
let [videoArea, controlsArea] = area.split({ type: 'vertical', sizes: ["85%", null], minimizable: false, resize: false });
|
|
363
365
|
controlsArea.root.classList.add('lexconstrolsarea');
|
|
364
366
|
|
|
365
367
|
// Create video element and load it
|
|
366
368
|
let video = this.video = options.video ?? document.createElement( 'video' );
|
|
369
|
+
this.video.loop = true;
|
|
370
|
+
|
|
367
371
|
if(options.src) {
|
|
368
372
|
this.video.src = options.src;
|
|
373
|
+
this._loadVideo(options);
|
|
369
374
|
}
|
|
370
|
-
this.video.loop = true;
|
|
371
|
-
this._loadVideo(options);
|
|
372
375
|
if(options.videoArea) {
|
|
373
376
|
options.videoArea.root.classList.add("lexvideoeditor");
|
|
374
377
|
videoArea.attach(options.videoArea);
|
|
@@ -458,6 +461,24 @@ class VideoEditor {
|
|
|
458
461
|
this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
|
|
459
462
|
})
|
|
460
463
|
|
|
464
|
+
this.onKeyUp = (event) => {
|
|
465
|
+
if(this.controls && event.key == " ") {
|
|
466
|
+
event.preventDefault();
|
|
467
|
+
event.stopPropagation();
|
|
468
|
+
|
|
469
|
+
if(!this.playing) {
|
|
470
|
+
this.video.play();
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
this.video.pause();
|
|
474
|
+
}
|
|
475
|
+
this.playing = !this.playing;
|
|
476
|
+
this.controlsPanelLeft.refresh();
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
window.addEventListener( "keyup", this.onKeyUp);
|
|
481
|
+
|
|
461
482
|
videoArea.onresize = (v) => {
|
|
462
483
|
bottomArea.setSize([v.width, 40]);
|
|
463
484
|
}
|
|
@@ -483,7 +504,7 @@ class VideoEditor {
|
|
|
483
504
|
this.timebar.onMouseMove(event);
|
|
484
505
|
}
|
|
485
506
|
});
|
|
486
|
-
|
|
507
|
+
|
|
487
508
|
}
|
|
488
509
|
|
|
489
510
|
async _loadVideo( options = {} ) {
|
|
@@ -491,13 +512,17 @@ class VideoEditor {
|
|
|
491
512
|
await new Promise(r => setTimeout(r, 1000));
|
|
492
513
|
this.video.currentTime = 10000000 * Math.random();
|
|
493
514
|
}
|
|
494
|
-
|
|
495
|
-
this.
|
|
515
|
+
|
|
516
|
+
this.timebar.startX = this.timebar.position.x;
|
|
517
|
+
this.timebar.endX = this.timebar.width;
|
|
518
|
+
|
|
519
|
+
this.video.currentTime = 0.01; // BUG: some videos will not play unless this line is present
|
|
496
520
|
this.endTime = this.video.duration;
|
|
497
|
-
|
|
521
|
+
|
|
498
522
|
this._setEndValue(this.timebar.endX);
|
|
499
523
|
this._setStartValue(this.timebar.startX);
|
|
500
|
-
this.
|
|
524
|
+
this.timebar.currentX = this._timeToX(this.video.currentTime);
|
|
525
|
+
this._setCurrentValue(this.timebar.currentX, false);
|
|
501
526
|
this.timebar.update(this.timebar.currentX);
|
|
502
527
|
|
|
503
528
|
if ( !this.requestId ){ // only have one update on flight
|
|
@@ -508,6 +533,8 @@ class VideoEditor {
|
|
|
508
533
|
this.hideControls();
|
|
509
534
|
}
|
|
510
535
|
|
|
536
|
+
window.addEventListener( "keyup", this.onKeyUp);
|
|
537
|
+
|
|
511
538
|
if(this.onVideoLoaded) {
|
|
512
539
|
this.onVideoLoaded(this.video);
|
|
513
540
|
}
|
|
@@ -627,9 +654,15 @@ class VideoEditor {
|
|
|
627
654
|
}
|
|
628
655
|
}
|
|
629
656
|
|
|
630
|
-
|
|
657
|
+
unbind ( ) {
|
|
631
658
|
this.stopUpdates();
|
|
632
|
-
|
|
659
|
+
|
|
660
|
+
this.video.pause();
|
|
661
|
+
this.playing = false;
|
|
662
|
+
this.controlsPanelLeft.refresh();
|
|
663
|
+
this.video.src = "";
|
|
664
|
+
|
|
665
|
+
window.removeEventListener("keyup", this.onKeyUp);
|
|
633
666
|
}
|
|
634
667
|
}
|
|
635
668
|
|
package/build/lexgui.css
CHANGED
|
@@ -2562,6 +2562,45 @@ meter::-webkit-meter-even-less-good-value {
|
|
|
2562
2562
|
border-radius: 50%;
|
|
2563
2563
|
position: relative;
|
|
2564
2564
|
box-shadow: 0px 3px 9px 2px #121212a9;
|
|
2565
|
+
--knob-snap-mark: 45deg;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
/* Arc Indicator */
|
|
2569
|
+
.lexknob .knobcircle::before {
|
|
2570
|
+
content: "";
|
|
2571
|
+
position: absolute;
|
|
2572
|
+
width: 125%;
|
|
2573
|
+
height: 125%;
|
|
2574
|
+
top: -15.5%;
|
|
2575
|
+
left: -15.5%;
|
|
2576
|
+
border-radius: 50%;
|
|
2577
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
2578
|
+
border-bottom-color: transparent;
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
/* Tick Marks */
|
|
2582
|
+
.lexknob.show-ticks .knobcircle::after {
|
|
2583
|
+
content: "";
|
|
2584
|
+
position: absolute;
|
|
2585
|
+
width: 130%;
|
|
2586
|
+
height: 130%;
|
|
2587
|
+
top: -15.5%;
|
|
2588
|
+
left: -15.5%;
|
|
2589
|
+
border-radius: 50%;
|
|
2590
|
+
pointer-events: none;
|
|
2591
|
+
mask-image: radial-gradient(circle, transparent 60%, black 60%), conic-gradient(
|
|
2592
|
+
transparent -135deg,
|
|
2593
|
+
black -135deg 275deg,
|
|
2594
|
+
transparent 275deg
|
|
2595
|
+
);
|
|
2596
|
+
-webkit-mask-composite: intersect;
|
|
2597
|
+
mask-composite: intersect;
|
|
2598
|
+
background: repeating-conic-gradient(
|
|
2599
|
+
transparent 0deg,
|
|
2600
|
+
rgba(255, 255, 255, 0.4) 1deg 3deg,
|
|
2601
|
+
transparent 4deg calc( var(--knob-snap-mark) - 1deg )
|
|
2602
|
+
);
|
|
2603
|
+
transform: rotate(-135deg);
|
|
2565
2604
|
}
|
|
2566
2605
|
|
|
2567
2606
|
.lexknob.sm .knobcircle {
|
|
@@ -2606,6 +2645,10 @@ meter::-webkit-meter-even-less-good-value {
|
|
|
2606
2645
|
margin-top: 6px;
|
|
2607
2646
|
}
|
|
2608
2647
|
|
|
2648
|
+
.lexknob.disabled .knobmarker {
|
|
2649
|
+
background-color: var(--global-text-terciary);
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2609
2652
|
.lexknob.sm .knobmarker {
|
|
2610
2653
|
width: 5px;
|
|
2611
2654
|
height: 5px;
|