lexgui 0.7.9 → 0.7.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/build/extensions/codeeditor.js +4 -0
- package/build/extensions/timeline.js +72 -19
- package/build/extensions/videoeditor.js +262 -172
- package/build/lexgui.css +33 -5
- package/build/lexgui.js +141 -87
- package/build/lexgui.min.css +2 -2
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +165 -111
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +28 -1
- package/examples/area-tabs.html +1 -1
- package/examples/asset-view.html +27 -1
- package/examples/timeline.html +23 -13
- package/examples/video-editor.html +152 -3
- package/examples/video-editor2.html +5 -5
- package/package.json +1 -1
|
@@ -23,6 +23,7 @@ class TimeBar {
|
|
|
23
23
|
constructor( area, type, options = {} ) {
|
|
24
24
|
|
|
25
25
|
this.type = type;
|
|
26
|
+
this.duration = options.duration || 1.0;
|
|
26
27
|
|
|
27
28
|
// Create canvas
|
|
28
29
|
this.canvas = document.createElement( 'canvas' );
|
|
@@ -31,7 +32,7 @@ class TimeBar {
|
|
|
31
32
|
area.attach( this.canvas );
|
|
32
33
|
|
|
33
34
|
this.ctx = this.canvas.getContext("2d");
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
this.markerWidth = options.markerWidth ?? 8;
|
|
36
37
|
this.markerHeight = options.markerHeight ?? (this.canvas.height * 0.5);
|
|
37
38
|
this.offset = options.offset || (this.markerWidth*0.5 + 5);
|
|
@@ -52,17 +53,75 @@ class TimeBar {
|
|
|
52
53
|
// Retrieve again the color using LX.getThemeColor, which checks the applied theme
|
|
53
54
|
this.updateTheme();
|
|
54
55
|
} )
|
|
56
|
+
|
|
57
|
+
this.canvas.onmousedown = (e) => this.onMouseDown(e);
|
|
58
|
+
this.canvas.onmousemove = (e) => this.onMouseMove(e);
|
|
59
|
+
this.canvas.onmouseup = (e) => this.onMouseUp(e);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
updateTheme(){
|
|
62
|
+
updateTheme( ) {
|
|
58
63
|
TimeBar.BACKGROUND_COLOR = LX.getThemeColor("global-color-secondary");
|
|
59
64
|
TimeBar.COLOR = LX.getThemeColor("global-color-quaternary");
|
|
60
65
|
TimeBar.ACTIVE_COLOR = "#668ee4";
|
|
61
66
|
}
|
|
67
|
+
|
|
68
|
+
setDuration( duration ) {
|
|
69
|
+
this.duration = duration;
|
|
70
|
+
}
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
xToTime( x ) {
|
|
73
|
+
return ((x - this.offset) / (this.lineWidth)) * this.duration;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
timeToX( time ) {
|
|
77
|
+
return (time / this.duration) * (this.lineWidth) + this.offset;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
setCurrentTime( time ) {
|
|
81
|
+
this.currentX = this.timeToX( time );
|
|
82
|
+
this.onSetCurrentValue( this.currentX );
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setStartTime( time ) {
|
|
86
|
+
this.startX = this.timeToX( time );
|
|
87
|
+
this.onSetStartValue( this.startX )
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setEndTime( time ) {
|
|
91
|
+
this.endX = this.timeToX( time );
|
|
92
|
+
this.onSetEndValue( this.endX );
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
onSetCurrentValue( x ) {
|
|
96
|
+
this.update( x );
|
|
97
|
+
|
|
98
|
+
const t = this.xToTime( x );
|
|
99
|
+
if( this.onChangeCurrent ) {
|
|
100
|
+
this.onChangeCurrent( t );
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onSetStartValue( x ) {
|
|
105
|
+
this.update( x );
|
|
106
|
+
|
|
107
|
+
const t = this.xToTime( x );
|
|
108
|
+
if( this.onChangeStart ) {
|
|
109
|
+
this.onChangeStart( t );
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onSetEndValue( x ) {
|
|
114
|
+
this.update( x );
|
|
65
115
|
|
|
116
|
+
const t = this.xToTime( x );
|
|
117
|
+
if( this.onChangeEnd ) {
|
|
118
|
+
this.onChangeEnd( t );
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_draw( ) {
|
|
123
|
+
const ctx = this.ctx;
|
|
124
|
+
|
|
66
125
|
ctx.save();
|
|
67
126
|
ctx.fillStyle = TimeBar.BACKGROUND_COLOR;
|
|
68
127
|
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
@@ -82,6 +141,10 @@ class TimeBar {
|
|
|
82
141
|
this._drawTrimMarker('start', this.startX, { color: null, fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9'});
|
|
83
142
|
this._drawTrimMarker('end', this.endX, { color: null, fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9'});
|
|
84
143
|
this._drawTimeMarker('current', this.currentX, { color: '#e5e5e5', fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9', width: this.markerWidth });
|
|
144
|
+
|
|
145
|
+
if( this.onDraw ) {
|
|
146
|
+
this.onDraw();
|
|
147
|
+
}
|
|
85
148
|
}
|
|
86
149
|
|
|
87
150
|
_drawTrimMarker(name, x, options) {
|
|
@@ -157,20 +220,20 @@ class TimeBar {
|
|
|
157
220
|
ctx.shadowBlur = 0;
|
|
158
221
|
}
|
|
159
222
|
|
|
160
|
-
update
|
|
223
|
+
update( x ) {
|
|
161
224
|
this.currentX = Math.min(Math.max(this.startX, x), this.endX);
|
|
162
225
|
this._draw();
|
|
163
|
-
|
|
164
|
-
if(this.onDraw) {
|
|
165
|
-
this.onDraw();
|
|
166
|
-
}
|
|
167
226
|
}
|
|
168
227
|
|
|
169
|
-
onMouseDown
|
|
228
|
+
onMouseDown( e ) {
|
|
229
|
+
|
|
230
|
+
if( this.onMouse ) {
|
|
231
|
+
this.onMouse( e );
|
|
232
|
+
}
|
|
170
233
|
|
|
171
234
|
e.preventDefault();
|
|
172
235
|
|
|
173
|
-
if(!this.canvas || e.target != this.canvas) {
|
|
236
|
+
if( !this.canvas || e.target != this.canvas || e.cancelBubble ) {
|
|
174
237
|
return;
|
|
175
238
|
}
|
|
176
239
|
const canvas = this.canvas;
|
|
@@ -183,43 +246,46 @@ class TimeBar {
|
|
|
183
246
|
const threshold = this.markerWidth;
|
|
184
247
|
|
|
185
248
|
// grab trim markers only from the bottom
|
|
186
|
-
if(Math.abs(this.startX - x) < threshold && this.position.y < y) {
|
|
249
|
+
if( Math.abs(this.startX - x) < threshold && this.position.y < y ) {
|
|
187
250
|
this.dragging = 'start';
|
|
188
251
|
canvas.style.cursor = "grabbing";
|
|
189
252
|
}
|
|
190
|
-
else if(Math.abs(this.endX - x) < threshold && this.position.y < y) {
|
|
253
|
+
else if( Math.abs(this.endX - x) < threshold && this.position.y < y ) {
|
|
191
254
|
this.dragging = 'end';
|
|
192
255
|
canvas.style.cursor = "grabbing";
|
|
193
256
|
}
|
|
194
257
|
else {
|
|
195
258
|
this.dragging = 'current';
|
|
196
259
|
canvas.style.cursor = "grabbing";
|
|
197
|
-
|
|
198
|
-
if(x < this.startX) {
|
|
260
|
+
|
|
261
|
+
if( x < this.startX ) {
|
|
199
262
|
this.currentX = this.startX;
|
|
200
263
|
}
|
|
201
|
-
else if(x > this.endX) {
|
|
264
|
+
else if( x > this.endX ) {
|
|
202
265
|
this.currentX = this.endX;
|
|
203
266
|
}
|
|
204
267
|
else {
|
|
205
268
|
this.currentX = x;
|
|
206
269
|
}
|
|
207
270
|
|
|
208
|
-
|
|
209
|
-
this.onChangeCurrent(this.currentX);
|
|
210
|
-
}
|
|
271
|
+
this.onSetCurrentValue( this.currentX );
|
|
211
272
|
}
|
|
212
273
|
|
|
213
274
|
this._draw();
|
|
214
275
|
}
|
|
215
276
|
|
|
216
|
-
onMouseUp
|
|
277
|
+
onMouseUp( e ) {
|
|
278
|
+
|
|
279
|
+
if( this.onMouse ) {
|
|
280
|
+
this.onMouse( e );
|
|
281
|
+
}
|
|
282
|
+
|
|
217
283
|
e.preventDefault();
|
|
218
284
|
|
|
219
285
|
this.dragging = false;
|
|
220
286
|
this.hovering = false;
|
|
221
287
|
|
|
222
|
-
if(!this.canvas) {
|
|
288
|
+
if( !this.canvas || e.cancelBubble ) {
|
|
223
289
|
return;
|
|
224
290
|
}
|
|
225
291
|
|
|
@@ -227,8 +293,13 @@ class TimeBar {
|
|
|
227
293
|
canvas.style.cursor = "default";
|
|
228
294
|
}
|
|
229
295
|
|
|
230
|
-
onMouseMove
|
|
231
|
-
|
|
296
|
+
onMouseMove( e ) {
|
|
297
|
+
|
|
298
|
+
if( this.onMouse ) {
|
|
299
|
+
this.onMouse( e );
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if( !this.canvas || e.cancelBubble ) {
|
|
232
303
|
return;
|
|
233
304
|
}
|
|
234
305
|
|
|
@@ -239,43 +310,38 @@ class TimeBar {
|
|
|
239
310
|
const x = e.target == canvas ? e.offsetX : e.clientX - canvas.offsetLeft;
|
|
240
311
|
const y = e.target == canvas ? e.offsetY : e.clientY - canvas.offsetTop;
|
|
241
312
|
|
|
242
|
-
if(this.dragging) {
|
|
243
|
-
switch(this.dragging) {
|
|
313
|
+
if( this.dragging ) {
|
|
314
|
+
switch( this.dragging ) {
|
|
244
315
|
case 'start':
|
|
245
|
-
this.startX = Math.max(this.position.x, Math.min(this.endX, x));
|
|
316
|
+
this.startX = Math.max(this.position.x, Math.min(this.endX, x));
|
|
246
317
|
this.currentX = this.startX;
|
|
247
|
-
|
|
248
|
-
this.onChangeStart(this.startX);
|
|
249
|
-
}
|
|
318
|
+
this.onSetStartValue(this.startX);
|
|
250
319
|
break;
|
|
251
320
|
case 'end':
|
|
252
|
-
this.endX = Math.max(this.startX, Math.min(this.position.x + this.lineWidth, x));
|
|
321
|
+
this.endX = Math.max( this.startX, Math.min(this.position.x + this.lineWidth, x) );
|
|
253
322
|
this.currentX = this.endX;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
323
|
+
|
|
324
|
+
this.onSetEndValue( this.endX );
|
|
257
325
|
break;
|
|
258
326
|
default:
|
|
259
|
-
this.currentX = Math.max(this.startX, Math.min(this.endX, x));
|
|
327
|
+
this.currentX = Math.max( this.startX, Math.min(this.endX, x) );
|
|
260
328
|
break;
|
|
261
329
|
}
|
|
262
330
|
|
|
263
|
-
|
|
264
|
-
this.onChangeCurrent(this.currentX);
|
|
265
|
-
}
|
|
331
|
+
this.onSetCurrentValue( this.currentX );
|
|
266
332
|
}
|
|
267
333
|
else {
|
|
268
334
|
const threshold = this.markerWidth * 0.5;
|
|
269
335
|
|
|
270
|
-
if(Math.abs(this.startX - x) < threshold ) {
|
|
336
|
+
if( Math.abs(this.startX - x) < threshold ) {
|
|
271
337
|
this.hovering = 'start';
|
|
272
338
|
canvas.style.cursor = "grab";
|
|
273
339
|
}
|
|
274
|
-
else if(Math.abs(this.endX - x) < threshold) {
|
|
340
|
+
else if( Math.abs(this.endX - x) < threshold ) {
|
|
275
341
|
this.hovering = 'end';
|
|
276
342
|
canvas.style.cursor = "grab";
|
|
277
343
|
}
|
|
278
|
-
else if(Math.abs(this.currentX - x) < threshold) {
|
|
344
|
+
else if( Math.abs(this.currentX - x) < threshold ) {
|
|
279
345
|
this.hovering = 'current';
|
|
280
346
|
canvas.style.cursor = "grab";
|
|
281
347
|
}
|
|
@@ -287,16 +353,16 @@ class TimeBar {
|
|
|
287
353
|
this._draw();
|
|
288
354
|
}
|
|
289
355
|
|
|
290
|
-
resize
|
|
356
|
+
resize( size ) {
|
|
291
357
|
this.canvas.width = size[0];
|
|
292
358
|
this.canvas.height = size[1];
|
|
293
359
|
|
|
294
360
|
let newWidth = size[0] - this.offset * 2;
|
|
295
|
-
newWidth = newWidth < 0.00001 ? 0.00001 : newWidth; // actual width of the line = canvas.width - offsetleft - offsetRight
|
|
361
|
+
newWidth = newWidth < 0.00001 ? 0.00001 : newWidth; // actual width of the line = canvas.width - offsetleft - offsetRight
|
|
296
362
|
const startRatio = (this.startX - this.offset) / this.lineWidth;
|
|
297
363
|
const currentRatio = (this.currentX - this.offset) / this.lineWidth;
|
|
298
364
|
const endRatio = (this.endX - this.offset) / this.lineWidth;
|
|
299
|
-
|
|
365
|
+
|
|
300
366
|
this.lineWidth = newWidth;
|
|
301
367
|
this.startX = Math.min( Math.max(newWidth * startRatio, 0), newWidth ) + this.offset;
|
|
302
368
|
this.currentX = Math.min(Math.max(newWidth * currentRatio, 0), newWidth) + this.offset;
|
|
@@ -305,6 +371,7 @@ class TimeBar {
|
|
|
305
371
|
this._draw();
|
|
306
372
|
}
|
|
307
373
|
}
|
|
374
|
+
|
|
308
375
|
LX.TimeBar = TimeBar;
|
|
309
376
|
|
|
310
377
|
|
|
@@ -322,12 +389,13 @@ class VideoEditor {
|
|
|
322
389
|
this.currentTime = this.startTime = 0;
|
|
323
390
|
this.startTimeString = "0:0";
|
|
324
391
|
this.endTimeString = "0:0";
|
|
325
|
-
|
|
392
|
+
this._updateTime = true;
|
|
393
|
+
this.speed = options.speed || 1;
|
|
326
394
|
this.mainArea = area;
|
|
327
395
|
|
|
328
396
|
let videoArea = null;
|
|
329
397
|
let controlsArea = null;
|
|
330
|
-
if(options.controlsArea) {
|
|
398
|
+
if( options.controlsArea ) {
|
|
331
399
|
videoArea = area;
|
|
332
400
|
controlsArea = options.controlsArea;
|
|
333
401
|
}
|
|
@@ -335,7 +403,7 @@ class VideoEditor {
|
|
|
335
403
|
[videoArea, controlsArea] = area.split({ type: 'vertical', sizes: ["85%", null], minimizable: false, resize: false });
|
|
336
404
|
}
|
|
337
405
|
controlsArea.root.classList.add('lexconstrolsarea');
|
|
338
|
-
|
|
406
|
+
|
|
339
407
|
this.cropArea = document.createElement("div");
|
|
340
408
|
this.cropArea.id = "cropArea";
|
|
341
409
|
this.cropArea.className = "resize-area hidden"
|
|
@@ -343,20 +411,20 @@ class VideoEditor {
|
|
|
343
411
|
this.brCrop = document.createElement("div");
|
|
344
412
|
this.brCrop.className = " resize-handle br"; // bottom right
|
|
345
413
|
this.cropArea.append(this.brCrop);
|
|
346
|
-
|
|
414
|
+
|
|
347
415
|
this.crop = options.crop;
|
|
348
416
|
this.dragOffsetX = 0;
|
|
349
417
|
this.dragOffsetY = 0;
|
|
350
418
|
// Create video element and load it
|
|
351
419
|
let video = this.video = options.video ?? document.createElement( 'video' );
|
|
352
420
|
this.loop = options.loop ?? false;
|
|
353
|
-
|
|
354
|
-
if(options.src) {
|
|
421
|
+
|
|
422
|
+
if( options.src ) {
|
|
355
423
|
this.video.src = options.src;
|
|
356
|
-
this.
|
|
424
|
+
this.loadVideo( options );
|
|
357
425
|
}
|
|
358
|
-
|
|
359
|
-
if(options.videoArea) {
|
|
426
|
+
|
|
427
|
+
if( options.videoArea ) {
|
|
360
428
|
options.videoArea.root.classList.add("lexvideoeditor");
|
|
361
429
|
options.videoArea.attach(this.cropArea);
|
|
362
430
|
videoArea.attach(options.videoArea);
|
|
@@ -408,7 +476,38 @@ class VideoEditor {
|
|
|
408
476
|
this.controlsPanelLeft.refresh();
|
|
409
477
|
}, { width: '40px', icon: (this.playing ? 'Pause@solid' : 'Play@solid'), className: "justify-center"});
|
|
410
478
|
|
|
411
|
-
|
|
479
|
+
if(this.speedDialog) {
|
|
480
|
+
this.speedDialog.close();
|
|
481
|
+
}
|
|
482
|
+
this.speedDialog = null;
|
|
483
|
+
const btn = this.controlsPanelLeft.addButton('', '', (v, e) => {
|
|
484
|
+
|
|
485
|
+
// if(this.speedDialog) {
|
|
486
|
+
// this.speedDialog.close();
|
|
487
|
+
// this.speedDialog = null;
|
|
488
|
+
// return;
|
|
489
|
+
// }
|
|
490
|
+
|
|
491
|
+
const panel = new LX.Panel();
|
|
492
|
+
panel.addNumber("Speed", this.speed, (v) => {
|
|
493
|
+
this.speed = v;
|
|
494
|
+
this.video.playbackRate = v;
|
|
495
|
+
if( this.onChangeSpeed ) {
|
|
496
|
+
this.onChangeSpeed( v );
|
|
497
|
+
}
|
|
498
|
+
}, {min: 0, max: 2.5, step: 0.01, nameWidth: "50px"})
|
|
499
|
+
|
|
500
|
+
new LX.Popover( e.target, [ panel ], { align: "start", side: "top", sideOffset: 12 } );
|
|
501
|
+
|
|
502
|
+
}, { width: '40px', title: 'speed', icon: "Timer@solid", className: "justify-center" } );
|
|
503
|
+
|
|
504
|
+
this.controlsPanelLeft.addButton('', 'Loop', (v) => {
|
|
505
|
+
this.loop = !this.loop;
|
|
506
|
+
|
|
507
|
+
this.controlsPanelLeft.refresh();
|
|
508
|
+
}, { width: '40px', title: 'loop', icon: ( 'Repeat@solid'), className: `justify-center`, buttonClass: `${(this.loop ? 'bg-accent' : '')}`});
|
|
509
|
+
|
|
510
|
+
this.controlsPanelLeft.addLabel(this.startTimeString, {width: "100px"});
|
|
412
511
|
this.controlsPanelLeft.endLine();
|
|
413
512
|
|
|
414
513
|
let availableWidth = leftArea.root.clientWidth - controlsLeft.root.clientWidth;
|
|
@@ -417,7 +516,8 @@ class VideoEditor {
|
|
|
417
516
|
|
|
418
517
|
this.controlsPanelLeft.refresh();
|
|
419
518
|
controlsLeft.root.style.minWidth = 'fit-content';
|
|
420
|
-
|
|
519
|
+
// controlsLeft.root.classList.add();
|
|
520
|
+
controlsLeft.attach(this.controlsPanelLeft);
|
|
421
521
|
|
|
422
522
|
// Create right controls panel (ens time)
|
|
423
523
|
this.controlsPanelRight = new LX.Panel({className: 'lexcontrolspanel'});
|
|
@@ -429,15 +529,15 @@ class VideoEditor {
|
|
|
429
529
|
controlsRight.root.style.minWidth = 'fit-content';
|
|
430
530
|
controlsRight.attach(this.controlsPanelRight);
|
|
431
531
|
|
|
432
|
-
this.timebar.onChangeCurrent = this.
|
|
433
|
-
this.timebar.onChangeStart = this.
|
|
434
|
-
this.timebar.onChangeEnd = this.
|
|
532
|
+
this.timebar.onChangeCurrent = this._setCurrentTime.bind(this);
|
|
533
|
+
this.timebar.onChangeStart = this._setStartTime.bind(this);
|
|
534
|
+
this.timebar.onChangeEnd = this._setEndTime.bind(this);
|
|
435
535
|
|
|
436
536
|
window.addEventListener('resize', (v) => {
|
|
437
537
|
if(this.onResize) {
|
|
438
538
|
this.onResize([videoArea.root.clientWidth, videoArea.root.clientHeight]);
|
|
439
539
|
}
|
|
440
|
-
bottomArea.setSize([
|
|
540
|
+
bottomArea.setSize([this.controlsArea.root.clientWidth, 40]);
|
|
441
541
|
let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth - controlsRight.root.clientWidth;
|
|
442
542
|
this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
|
|
443
543
|
this.dragCropArea( { clientX: -1, clientY: -1 } );
|
|
@@ -446,12 +546,12 @@ class VideoEditor {
|
|
|
446
546
|
})
|
|
447
547
|
|
|
448
548
|
this.onKeyUp = (event) => {
|
|
449
|
-
if(this.controls && event.key == " ") {
|
|
549
|
+
if( this.controls && event.key == " " ) {
|
|
450
550
|
event.preventDefault();
|
|
451
551
|
event.stopPropagation();
|
|
452
552
|
|
|
453
553
|
this.playing = !this.playing;
|
|
454
|
-
if(this.playing) {
|
|
554
|
+
if( this.playing ) {
|
|
455
555
|
if( this.video.currentTime + 0.000001 >= this.endTime) {
|
|
456
556
|
this.video.currentTime = this.startTime;
|
|
457
557
|
}
|
|
@@ -467,15 +567,18 @@ class VideoEditor {
|
|
|
467
567
|
window.addEventListener( "keyup", this.onKeyUp);
|
|
468
568
|
|
|
469
569
|
videoArea.onresize = (v) => {
|
|
470
|
-
bottomArea.
|
|
570
|
+
if( bottomArea.parentArea ) {
|
|
571
|
+
bottomArea.setSize([bottomArea.parentArea.root.clientWidth, 40]);
|
|
572
|
+
}
|
|
471
573
|
|
|
472
574
|
const ratio = this.video.clientHeight / this.video.videoHeight;
|
|
473
575
|
this.cropArea.style.height = this.video.clientHeight + "px";
|
|
474
576
|
this.cropArea.style.width = this.video.videoWidth * ratio + "px";
|
|
475
577
|
}
|
|
476
578
|
|
|
579
|
+
|
|
477
580
|
timeBarArea.onresize = (v) => {
|
|
478
|
-
let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth - controlsRight.root.clientWidth;
|
|
581
|
+
let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth - controlsRight.root.clientWidth - 20;
|
|
479
582
|
this.timebar.resize([availableWidth, v.height]);
|
|
480
583
|
}
|
|
481
584
|
|
|
@@ -483,14 +586,14 @@ class VideoEditor {
|
|
|
483
586
|
|
|
484
587
|
// Add canvas event listeneres
|
|
485
588
|
parent.addEventListener( "mousedown", (event) => {
|
|
486
|
-
if(this.controls) {
|
|
487
|
-
|
|
488
|
-
}
|
|
589
|
+
// if(this.controls) {
|
|
590
|
+
// this.timebar.onMouseDown(event);
|
|
591
|
+
// }
|
|
489
592
|
});
|
|
490
|
-
parent.addEventListener( "mouseup",
|
|
491
|
-
if(this.controls) {
|
|
492
|
-
|
|
493
|
-
}
|
|
593
|
+
parent.addEventListener( "mouseup", (event) => {
|
|
594
|
+
// if(this.controls) {
|
|
595
|
+
// this.timebar.onMouseUp(event);
|
|
596
|
+
// }
|
|
494
597
|
|
|
495
598
|
if( ( this.isDragging || this.isResizing ) && this.onCropArea ) {
|
|
496
599
|
if( this.onCropArea ) {
|
|
@@ -502,23 +605,23 @@ class VideoEditor {
|
|
|
502
605
|
|
|
503
606
|
});
|
|
504
607
|
parent.addEventListener( "mousemove", (event) => {
|
|
505
|
-
if(this.controls) {
|
|
506
|
-
|
|
507
|
-
}
|
|
608
|
+
// if(this.controls) {
|
|
609
|
+
// this.timebar.onMouseMove(event);
|
|
610
|
+
// }
|
|
508
611
|
|
|
509
|
-
if (this.isResizing) {
|
|
510
|
-
this.resizeCropArea(event);
|
|
612
|
+
if ( this.isResizing ) {
|
|
613
|
+
this.resizeCropArea( event );
|
|
511
614
|
}
|
|
512
615
|
|
|
513
|
-
if(this.isDragging) {
|
|
514
|
-
this.dragCropArea(event);
|
|
616
|
+
if( this.isDragging ) {
|
|
617
|
+
this.dragCropArea( event );
|
|
515
618
|
}
|
|
516
619
|
});
|
|
517
620
|
|
|
518
|
-
this.cropArea.addEventListener(
|
|
621
|
+
this.cropArea.addEventListener( "mousedown", (event) => {
|
|
519
622
|
|
|
520
|
-
|
|
521
|
-
if (event.target === this.cropArea) {
|
|
623
|
+
|
|
624
|
+
if ( event.target === this.cropArea ) {
|
|
522
625
|
const rect = this.cropArea.getBoundingClientRect();
|
|
523
626
|
this.isDragging = true;
|
|
524
627
|
|
|
@@ -527,9 +630,9 @@ class VideoEditor {
|
|
|
527
630
|
}
|
|
528
631
|
});
|
|
529
632
|
|
|
530
|
-
document.querySelectorAll(
|
|
633
|
+
document.querySelectorAll(".resize-handle" ).forEach( handle => {
|
|
531
634
|
|
|
532
|
-
handle.addEventListener(
|
|
635
|
+
handle.addEventListener("mousedown", ( e ) => {
|
|
533
636
|
|
|
534
637
|
e.stopPropagation();
|
|
535
638
|
if (handle.classList[1] === 'br') {
|
|
@@ -537,23 +640,24 @@ class VideoEditor {
|
|
|
537
640
|
}
|
|
538
641
|
});
|
|
539
642
|
});
|
|
540
|
-
|
|
643
|
+
|
|
541
644
|
this.onChangeStart = null;
|
|
542
645
|
this.onChangeEnd = null;
|
|
543
646
|
}
|
|
544
647
|
|
|
545
|
-
resizeCropArea(event) {
|
|
648
|
+
resizeCropArea( event ) {
|
|
546
649
|
|
|
547
650
|
const mouseX = event.clientX;
|
|
548
651
|
const mouseY = event.clientY;
|
|
549
|
-
|
|
652
|
+
|
|
550
653
|
const isCropHidden = this.cropArea.classList.contains("hidden");
|
|
551
654
|
const nodes = this.cropArea.parentElement.childNodes;
|
|
552
|
-
|
|
655
|
+
|
|
553
656
|
const rectCrop = this.cropArea.getBoundingClientRect();
|
|
554
657
|
const rectVideo = this.video.getBoundingClientRect();
|
|
555
658
|
let width = Math.max( 0, Math.min( mouseX - rectCrop.left, rectVideo.width ) );
|
|
556
659
|
let height = Math.max( 0, Math.min( mouseY - rectCrop.top, rectVideo.height ) );
|
|
660
|
+
|
|
557
661
|
if ( (rectCrop.left + width) > rectVideo.right ){
|
|
558
662
|
width = Math.min( rectVideo.width, rectVideo.right - rectCrop.left);
|
|
559
663
|
}
|
|
@@ -561,9 +665,9 @@ class VideoEditor {
|
|
|
561
665
|
height = Math.min( rectVideo.height, rectVideo.bottom - rectCrop.top);
|
|
562
666
|
}
|
|
563
667
|
|
|
564
|
-
if ( !isCropHidden ){
|
|
668
|
+
if ( !isCropHidden ){
|
|
565
669
|
for( let i = 0; i < nodes.length; i++ ) {
|
|
566
|
-
if( nodes[i] != this.cropArea ) {
|
|
670
|
+
if( nodes[i] != this.cropArea ) {
|
|
567
671
|
const rectEl = nodes[i].getBoundingClientRect();
|
|
568
672
|
nodes[i].style.webkitMask = `linear-gradient(#000 0 0) ${rectCrop.x - rectEl.left}px ${ rectCrop.y - rectEl.top }px / ${width}px ${height}px, linear-gradient(rgba(0, 0, 0, 0.3) 0 0)`;
|
|
569
673
|
nodes[i].style.webkitMaskRepeat = 'no-repeat';
|
|
@@ -593,13 +697,13 @@ class VideoEditor {
|
|
|
593
697
|
if( y < rectVideo.top ) {
|
|
594
698
|
y = rectVideo.top;
|
|
595
699
|
}
|
|
596
|
-
|
|
700
|
+
|
|
597
701
|
if( y + rectCrop.height > rectVideo.bottom ) {
|
|
598
702
|
y = Math.max( rectVideo.top, rectVideo.bottom - rectCrop.height );
|
|
599
703
|
}
|
|
600
704
|
|
|
601
705
|
if ( !this.cropArea.classList.contains("hidden") ){
|
|
602
|
-
const nodes = this.cropArea.parentElement.childNodes;
|
|
706
|
+
const nodes = this.cropArea.parentElement.childNodes;
|
|
603
707
|
for( let i = 0; i < nodes.length; i++ ) {
|
|
604
708
|
if( nodes[i] != this.cropArea ) {
|
|
605
709
|
const rectEl = nodes[i].getBoundingClientRect();
|
|
@@ -615,55 +719,59 @@ class VideoEditor {
|
|
|
615
719
|
|
|
616
720
|
}
|
|
617
721
|
|
|
618
|
-
async
|
|
722
|
+
async loadVideo( options = {} ) {
|
|
619
723
|
this.videoReady = false;
|
|
620
|
-
while(this.video.duration === Infinity || isNaN(this.video.duration) || !this.timebar) {
|
|
724
|
+
while( this.video.duration === Infinity || isNaN(this.video.duration) || !this.timebar ) {
|
|
621
725
|
await new Promise(r => setTimeout(r, 1000));
|
|
622
726
|
this.video.currentTime = 10000000 * Math.random();
|
|
623
727
|
}
|
|
624
|
-
this.video.currentTime = 0.01; // BUG: some videos will not play unless this line is present
|
|
625
|
-
|
|
728
|
+
this.video.currentTime = 0.01; // BUG: some videos will not play unless this line is present
|
|
729
|
+
|
|
626
730
|
// Duration can change if the video is dynamic (stream). This function is to ensure to load all buffer data
|
|
627
|
-
const forceLoadChunks = () => {
|
|
731
|
+
const forceLoadChunks = () => {
|
|
628
732
|
const state = this.videoReady;
|
|
629
|
-
if(this.video.readyState > 3) {
|
|
733
|
+
if( this.video.readyState > 3 ) {
|
|
630
734
|
this.videoReady = true;
|
|
631
735
|
}
|
|
632
|
-
if(!state) {
|
|
736
|
+
if( !state ) {
|
|
633
737
|
this.video.currentTime = this.video.duration;
|
|
634
738
|
}
|
|
635
739
|
}
|
|
636
740
|
|
|
637
|
-
this.video.addEventListener( "canplaythrough", forceLoadChunks, {passive
|
|
741
|
+
this.video.addEventListener( "canplaythrough", forceLoadChunks, { passive: true } );
|
|
638
742
|
|
|
639
|
-
this.video.ondurationchange = (v) => {
|
|
743
|
+
this.video.ondurationchange = ( v ) => {
|
|
640
744
|
if( this.video.duration != this.endTime ) {
|
|
641
745
|
|
|
642
746
|
this.video.currentTime = this.startTime;
|
|
643
747
|
console.log("duration changed from", this.endTime, " to ", this.video.duration);
|
|
644
748
|
this.endTime = this.video.duration;
|
|
645
|
-
|
|
646
|
-
this.
|
|
749
|
+
this.timebar.setDuration( this.endTime );
|
|
750
|
+
this.timebar.setEndTime( this.endTime );
|
|
647
751
|
}
|
|
648
752
|
this.video.currentTime = this.startTime;
|
|
753
|
+
this.timebar.setCurrentTime( this.video.currentTime );
|
|
754
|
+
|
|
649
755
|
}
|
|
650
|
-
|
|
756
|
+
|
|
651
757
|
this.timebar.startX = this.timebar.position.x;
|
|
652
758
|
this.timebar.endX = this.timebar.position.x + this.timebar.lineWidth;
|
|
653
|
-
|
|
759
|
+
this.startTime = 0;
|
|
654
760
|
this.endTime = this.video.duration;
|
|
655
|
-
|
|
656
|
-
this.
|
|
657
|
-
this.
|
|
658
|
-
this.timebar.
|
|
659
|
-
this.
|
|
660
|
-
this.timebar.
|
|
661
|
-
|
|
761
|
+
this.timebar.setDuration( this.endTime );
|
|
762
|
+
this.timebar.setEndTime( this.video.duration );
|
|
763
|
+
this.timebar.setStartTime( this.startTime );
|
|
764
|
+
this.timebar.setCurrentTime( this.startTime );
|
|
765
|
+
//this.timebar.setStartValue(this.timebar.startX);
|
|
766
|
+
//this.timebar.currentX = this._timeToX(this.video.currentTime);
|
|
767
|
+
//this.timebar.setCurrentValue(this.timebar.currentX);
|
|
768
|
+
//this.timebar.update( this.timebar.currentX );
|
|
769
|
+
|
|
662
770
|
if ( !this.requestId ){ // only have one update on flight
|
|
663
771
|
this._update();
|
|
664
|
-
}
|
|
772
|
+
}
|
|
665
773
|
this.controls = options.controls ?? true;
|
|
666
|
-
|
|
774
|
+
|
|
667
775
|
if ( !this.controls ) {
|
|
668
776
|
this.hideControls();
|
|
669
777
|
}
|
|
@@ -675,26 +783,27 @@ class VideoEditor {
|
|
|
675
783
|
|
|
676
784
|
if( this.crop ) {
|
|
677
785
|
this.showCropArea();
|
|
678
|
-
}
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
679
788
|
this.hideCropArea();
|
|
680
789
|
}
|
|
681
790
|
|
|
682
791
|
window.addEventListener( "keyup", this.onKeyUp);
|
|
683
792
|
|
|
684
793
|
if( this.onVideoLoaded ) {
|
|
685
|
-
this.onVideoLoaded(this.video);
|
|
794
|
+
this.onVideoLoaded( this.video );
|
|
686
795
|
}
|
|
687
796
|
}
|
|
688
797
|
|
|
689
798
|
_update () {
|
|
690
799
|
|
|
691
|
-
if(this.onDraw) {
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
if(this.playing) {
|
|
695
|
-
if( this.video.currentTime + 0.000001 >= this.endTime) {
|
|
800
|
+
// if( this.onDraw ) {
|
|
801
|
+
// this.onDraw();
|
|
802
|
+
// }
|
|
803
|
+
if( this.playing ) {
|
|
804
|
+
if( this.video.currentTime + 0.000001 >= this.endTime ) {
|
|
696
805
|
this.video.pause();
|
|
697
|
-
if(!this.loop) {
|
|
806
|
+
if( !this.loop ) {
|
|
698
807
|
this.playing = false;
|
|
699
808
|
this.controlsPanelLeft.refresh();
|
|
700
809
|
}
|
|
@@ -703,29 +812,15 @@ class VideoEditor {
|
|
|
703
812
|
this.video.play();
|
|
704
813
|
}
|
|
705
814
|
}
|
|
706
|
-
|
|
707
|
-
this.
|
|
708
|
-
this.
|
|
815
|
+
this._updateTime = false;
|
|
816
|
+
this.timebar.setCurrentTime( this.video.currentTime );
|
|
817
|
+
this._updateTime = true;
|
|
709
818
|
}
|
|
710
819
|
|
|
711
|
-
this.requestId = requestAnimationFrame(this._update.bind(this));
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
_xToTime (x) {
|
|
715
|
-
return ((x - this.timebar.offset) / (this.timebar.lineWidth)) * this.video.duration;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
_timeToX (time) {
|
|
719
|
-
return (time / this.video.duration) * (this.timebar.lineWidth) + this.timebar.offset;
|
|
820
|
+
this.requestId = requestAnimationFrame( this._update.bind(this) );
|
|
720
821
|
}
|
|
721
822
|
|
|
722
|
-
|
|
723
|
-
const t = this._xToTime(x);
|
|
724
|
-
|
|
725
|
-
if(updateTime) {
|
|
726
|
-
this.video.currentTime = t;
|
|
727
|
-
}
|
|
728
|
-
//console.log( "Computed: " + t)
|
|
823
|
+
timeToString( t ) {
|
|
729
824
|
let mzminutes = Math.floor(t / 60);
|
|
730
825
|
let mzseconds = Math.floor(t - (mzminutes * 60));
|
|
731
826
|
let mzmiliseconds = Math.floor((t - mzseconds)*100);
|
|
@@ -733,56 +828,51 @@ class VideoEditor {
|
|
|
733
828
|
mzmiliseconds = mzmiliseconds < 10 ? ('0' + mzmiliseconds) : mzmiliseconds;
|
|
734
829
|
mzseconds = mzseconds < 10 ? ('0' + mzseconds) : mzseconds;
|
|
735
830
|
mzminutes = mzminutes < 10 ? ('0' + mzminutes) : mzminutes;
|
|
736
|
-
|
|
831
|
+
return mzminutes + ':' + mzseconds + '.' + mzmiliseconds;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
_setCurrentTime( t ) {
|
|
835
|
+
|
|
836
|
+
if( this.video.currentTime != t && this._updateTime ) {
|
|
837
|
+
this.video.currentTime = t;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
this.currentTimeString = this.timeToString( t );
|
|
737
841
|
this.controlsCurrentPanel.refresh();
|
|
738
842
|
|
|
739
|
-
if(this.onSetTime) {
|
|
843
|
+
if( this.onSetTime ) {
|
|
740
844
|
this.onSetTime(t);
|
|
741
845
|
}
|
|
846
|
+
if( this.onChangeCurrent ) {
|
|
847
|
+
this.onChangeCurrent( t );
|
|
848
|
+
}
|
|
742
849
|
}
|
|
743
850
|
|
|
744
|
-
|
|
745
|
-
const t = this._xToTime(x);
|
|
851
|
+
_setStartTime( t ) {
|
|
746
852
|
this.startTime = this.video.currentTime = t;
|
|
747
853
|
|
|
748
|
-
|
|
749
|
-
let mzseconds = Math.floor(t - (mzminutes * 60));
|
|
750
|
-
let mzmiliseconds = Math.floor((t - mzseconds)*100);
|
|
751
|
-
|
|
752
|
-
mzmiliseconds = mzmiliseconds < 10 ? ('0' + mzmiliseconds) : mzmiliseconds;
|
|
753
|
-
mzseconds = mzseconds < 10 ? ('0' + mzseconds) : mzseconds;
|
|
754
|
-
mzminutes = mzminutes < 10 ? ('0' + mzminutes) : mzminutes;
|
|
755
|
-
this.startTimeString = mzminutes+':'+mzseconds+'.'+mzmiliseconds;
|
|
854
|
+
this.startTimeString = this.timeToString( t );
|
|
756
855
|
this.controlsPanelLeft.refresh();
|
|
757
|
-
|
|
758
|
-
|
|
856
|
+
|
|
857
|
+
if( this.onSetTime ) {
|
|
858
|
+
this.onSetTime( t );
|
|
759
859
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
this.onChangeStart(t);
|
|
860
|
+
if( this.onChangeStart ) {
|
|
861
|
+
this.onChangeStart ( t );
|
|
763
862
|
}
|
|
764
863
|
}
|
|
765
864
|
|
|
766
|
-
|
|
767
|
-
const t = this._xToTime(x);
|
|
865
|
+
_setEndTime( t ) {
|
|
768
866
|
this.endTime = this.video.currentTime = t;
|
|
769
867
|
|
|
770
|
-
|
|
771
|
-
let mzseconds = Math.floor(t - (mzminutes * 60));
|
|
772
|
-
let mzmiliseconds = Math.floor((t - mzseconds)*100);
|
|
773
|
-
|
|
774
|
-
mzmiliseconds = mzmiliseconds < 10 ? ('0' + mzmiliseconds) : mzmiliseconds;
|
|
775
|
-
mzseconds = mzseconds < 10 ? ('0' + mzseconds) : mzseconds;
|
|
776
|
-
mzminutes = mzminutes < 10 ? ('0' + mzminutes) : mzminutes;
|
|
777
|
-
|
|
778
|
-
this.endTimeString = mzminutes+':'+mzseconds+'.'+mzmiliseconds;
|
|
868
|
+
this.endTimeString = this.timeToString( t)
|
|
779
869
|
this.controlsPanelRight.refresh();
|
|
780
|
-
|
|
870
|
+
|
|
871
|
+
if( this.onSetTime ) {
|
|
781
872
|
this.onSetTime(t);
|
|
782
873
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
this.onChangeEnd(t);
|
|
874
|
+
if( this.onChangeEnd ) {
|
|
875
|
+
this.onChangeEnd( t );
|
|
786
876
|
}
|
|
787
877
|
}
|
|
788
878
|
|
|
@@ -821,7 +911,7 @@ class VideoEditor {
|
|
|
821
911
|
|
|
822
912
|
const nodes = this.cropArea.parentElement.childNodes;
|
|
823
913
|
for( let i = 0; i < nodes.length; i++ ) {
|
|
824
|
-
if( nodes[i] != this.cropArea ) {
|
|
914
|
+
if( nodes[i] != this.cropArea ) {
|
|
825
915
|
nodes[i].style.webkitMask = "";
|
|
826
916
|
nodes[i].style.webkitMaskRepeat = 'no-repeat';
|
|
827
917
|
}
|
|
@@ -848,7 +938,7 @@ class VideoEditor {
|
|
|
848
938
|
|
|
849
939
|
unbind ( ) {
|
|
850
940
|
this.stopUpdates();
|
|
851
|
-
|
|
941
|
+
|
|
852
942
|
this.video.pause();
|
|
853
943
|
this.playing = false;
|
|
854
944
|
this.controlsPanelLeft.refresh();
|