lexgui 0.5.6 → 0.5.8
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/components/audio.js +2 -2
- package/build/components/timeline.js +475 -420
- package/build/components/videoeditor.js +97 -148
- package/build/lexgui.css +25 -1
- package/build/lexgui.js +323 -196
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +323 -196
- package/build/lexgui.module.min.js +1 -1
- package/build/utilities.css +62 -8
- package/changelog.md +31 -1
- package/demo.js +17 -10
- package/package.json +1 -1
|
@@ -6,26 +6,6 @@ if(!LX) {
|
|
|
6
6
|
|
|
7
7
|
LX.components.push( 'Timeline' );
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* @class Session
|
|
11
|
-
* @description Store info about timeline session
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
class Session {
|
|
15
|
-
|
|
16
|
-
constructor() {
|
|
17
|
-
|
|
18
|
-
this.start_time = -0.01;
|
|
19
|
-
this.left_margin = 0;
|
|
20
|
-
// this.current_time = 0;
|
|
21
|
-
// this.last_time = 0;
|
|
22
|
-
// this.seconds_to_pixels = 50;
|
|
23
|
-
this.scroll_y = 0;
|
|
24
|
-
// this.offset_y = 0;
|
|
25
|
-
// this.selection = null;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
9
|
/**
|
|
30
10
|
* @class Timeline
|
|
31
11
|
* @description Agnostic timeline, do not impose any timeline content. Renders to a canvas
|
|
@@ -40,8 +20,10 @@ class Timeline {
|
|
|
40
20
|
constructor( name, options = {} ) {
|
|
41
21
|
|
|
42
22
|
this.name = name ?? '';
|
|
43
|
-
this.currentTime = 0;
|
|
44
23
|
this.opacity = options.opacity || 1;
|
|
24
|
+
this.currentTime = 0;
|
|
25
|
+
this.visualTimeRange = [0,0]; // [start time, end time] - visible range of time. 0 <= time <= duration
|
|
26
|
+
this.visualOriginTime = 0; // time visible at pixel 0. -infinity < time < infinity
|
|
45
27
|
this.topMargin = 40;
|
|
46
28
|
this.clickDiscardTimeout = 200; // ms
|
|
47
29
|
this.lastMouse = [];
|
|
@@ -59,30 +41,31 @@ class Timeline {
|
|
|
59
41
|
this.movingKeys = false;
|
|
60
42
|
this.timeBeforeMove = 0;
|
|
61
43
|
|
|
62
|
-
this.
|
|
63
|
-
this.
|
|
64
|
-
this.
|
|
44
|
+
this.onCreateBeforeTopBar = options.onCreateBeforeTopBar;
|
|
45
|
+
this.onCreateAfterTopBar = options.onCreateAfterTopBar;
|
|
46
|
+
this.onCreateControlsButtons = options.onCreateControlsButtons;
|
|
47
|
+
this.onCreateSettingsButtons = options.onCreateSettingsButtons;
|
|
48
|
+
this.onChangeLoopMode = options.onChangeLoopMode;
|
|
65
49
|
this.onShowConfiguration = options.onShowConfiguration;
|
|
66
50
|
this.onBeforeDrawContent = options.onBeforeDrawContent;
|
|
67
51
|
|
|
68
52
|
this.playing = false;
|
|
69
53
|
this.loop = options.loop ?? true;
|
|
70
54
|
|
|
71
|
-
this.session = new Session();
|
|
72
|
-
|
|
73
55
|
this.canvas = options.canvas ?? document.createElement('canvas');
|
|
56
|
+
this.canvas.style.width = "100%";
|
|
57
|
+
this.canvas.style.height = "100%";
|
|
58
|
+
|
|
74
59
|
|
|
75
60
|
this.duration = 1;
|
|
76
61
|
this.speed = 1;
|
|
77
|
-
this.position = [ 0, 0 ];
|
|
78
62
|
this.size = [ options.width ?? 400, options.height ?? 100 ];
|
|
79
63
|
|
|
80
64
|
this.currentScroll = 0; //in percentage
|
|
81
65
|
this.currentScrollInPixels = 0; //in pixels
|
|
82
|
-
this.scrollableHeight = this.size[1]; //true height of the timeline content
|
|
83
66
|
|
|
84
|
-
this.
|
|
85
|
-
this.
|
|
67
|
+
this.pixelsPerSecond = Math.max( 0.00001, this.size[0]/1 );
|
|
68
|
+
this.secondsPerPixel = 1 / this.pixelsPerSecond;
|
|
86
69
|
this.selectedItems = options.selectedItems ?? [];
|
|
87
70
|
this.animationClip = options.animationClip ?? null;
|
|
88
71
|
this.trackHeight = options.trackHeight ?? 25;
|
|
@@ -99,28 +82,34 @@ class Timeline {
|
|
|
99
82
|
|
|
100
83
|
this.optimizeThreshold = 0.01;
|
|
101
84
|
|
|
102
|
-
this.root = new LX.Area({className : 'lextimeline'});
|
|
103
|
-
|
|
104
85
|
this.header_offset = 48;
|
|
86
|
+
|
|
87
|
+
// main area -- root
|
|
88
|
+
this.mainArea = new LX.Area({className : 'lextimeline'});
|
|
89
|
+
this.mainArea.split({ type: "vertical", sizes: [this.header_offset, "auto"], resize: false});
|
|
90
|
+
|
|
91
|
+
// header
|
|
92
|
+
this.header = new LX.Panel( { id: 'lextimelineheader' } );
|
|
93
|
+
this.mainArea.sections[0].attach( this.header );
|
|
94
|
+
this.updateHeader();
|
|
105
95
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
area.splitBar.style.zIndex = 1; // for some reason this is needed here
|
|
112
|
-
this.content_area = area;
|
|
113
|
-
let [ left, right ] = area.sections;
|
|
96
|
+
// content area
|
|
97
|
+
const contentArea = this.mainArea.sections[1];
|
|
98
|
+
contentArea.root.id = "bottom-timeline-area";
|
|
99
|
+
contentArea.split({ type: "horizontal", sizes: ["15%", "85%"] });
|
|
100
|
+
let [ left, right ] = contentArea.sections;
|
|
114
101
|
|
|
115
|
-
right.
|
|
102
|
+
right.attach( this.canvas );
|
|
116
103
|
this.canvasArea = right;
|
|
117
104
|
this.canvasArea.root.classList.add("lextimelinearea");
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
this.
|
|
105
|
+
|
|
106
|
+
this.leftPanel = left.addPanel( { className: 'lextimelinepanel', width: "100%", height: "100%" } );
|
|
107
|
+
this.trackTreesPanel = null;
|
|
108
|
+
this.trackTreesWidget = null;
|
|
109
|
+
this.updateLeftPanel();
|
|
121
110
|
|
|
122
111
|
if(!options.canvas && this.name != '') {
|
|
123
|
-
this.
|
|
112
|
+
this.mainArea.root.id = this.name;
|
|
124
113
|
this.canvas.id = this.name + '-canvas';
|
|
125
114
|
}
|
|
126
115
|
|
|
@@ -136,10 +125,10 @@ class Timeline {
|
|
|
136
125
|
// Process keys events
|
|
137
126
|
this.canvasArea.root.addEventListener("keydown", this.processKeys.bind(this));
|
|
138
127
|
|
|
139
|
-
|
|
128
|
+
this.canvasArea.onresize = bounding => {
|
|
140
129
|
if(!(bounding.width && bounding.height))
|
|
141
130
|
return;
|
|
142
|
-
this.resizeCanvas(
|
|
131
|
+
this.resizeCanvas();
|
|
143
132
|
}
|
|
144
133
|
this.resize(this.size);
|
|
145
134
|
|
|
@@ -157,18 +146,9 @@ class Timeline {
|
|
|
157
146
|
*/
|
|
158
147
|
|
|
159
148
|
updateHeader() {
|
|
149
|
+
this.header.clear();
|
|
160
150
|
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
this.header.clear();
|
|
164
|
-
}
|
|
165
|
-
else
|
|
166
|
-
{
|
|
167
|
-
this.header = new LX.Panel( { id: 'lextimelineheader', height: this.header_offset + "px" } );
|
|
168
|
-
this.root.root.appendChild( this.header.root );
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let header = this.header;
|
|
151
|
+
const header = this.header;
|
|
172
152
|
header.sameLine();
|
|
173
153
|
|
|
174
154
|
if( this.name )
|
|
@@ -180,40 +160,53 @@ class Timeline {
|
|
|
180
160
|
|
|
181
161
|
header.queue( buttonContainer );
|
|
182
162
|
|
|
183
|
-
header.addButton("playBtn", '
|
|
163
|
+
const playbtn = header.addButton("playBtn", '', (value, event) => {
|
|
184
164
|
this.changeState();
|
|
185
|
-
}, { buttonClass: "accept", title: "Play", hideName: true });
|
|
165
|
+
}, { buttonClass: "accept", title: "Play", hideName: true, icon: "fa-solid fa-play", swap: "fa-solid fa-pause" });
|
|
166
|
+
playbtn.root.setState(this.playing, true);
|
|
186
167
|
|
|
187
|
-
header.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
168
|
+
header.addBlank("0.05em", "auto");
|
|
169
|
+
|
|
170
|
+
header.addButton("stopBtn", '', (value, event) => {
|
|
171
|
+
this.setState(false, true); // skip callback of set state
|
|
172
|
+
if ( this.onStateStop ){
|
|
173
|
+
this.onStateStop();
|
|
192
174
|
}
|
|
193
|
-
}, {
|
|
175
|
+
}, { buttonClass: "accept", title: "Stop", hideName: true, icon: "fa-solid fa-stop" });
|
|
176
|
+
|
|
177
|
+
header.addBlank("0.05em", "auto");
|
|
178
|
+
|
|
179
|
+
header.addButton("loopBtn", '', ( value, event ) => {
|
|
180
|
+
this.setLoopMode(!this.loop);
|
|
181
|
+
}, { selectable: true, selected: this.loop, title: 'Loop', hideName: true, icon: "fa-solid fa-rotate" });
|
|
194
182
|
|
|
195
|
-
if( this.
|
|
196
|
-
|
|
197
|
-
this.onBeforeCreateTopBar( header );
|
|
183
|
+
if( this.onCreateControlsButtons ){
|
|
184
|
+
this.onCreateControlsButtons( header );
|
|
198
185
|
}
|
|
199
|
-
|
|
186
|
+
|
|
200
187
|
header.clearQueue( buttonContainer );
|
|
201
|
-
|
|
202
188
|
header.addContent( "header-buttons", buttonContainer );
|
|
203
189
|
|
|
190
|
+
// time number inputs - duration, speed, current time, etc
|
|
191
|
+
|
|
192
|
+
if( this.onCreateBeforeTopBar )
|
|
193
|
+
{
|
|
194
|
+
this.onCreateBeforeTopBar( header );
|
|
195
|
+
}
|
|
196
|
+
|
|
204
197
|
header.addNumber("Current Time", this.currentTime, (value, event) => {
|
|
205
198
|
this.setTime(value)
|
|
206
199
|
}, {
|
|
207
|
-
units: "s",
|
|
200
|
+
// units: "s", // commented until lexgui is refactored. There is a performance hit while using units
|
|
208
201
|
signal: "@on_set_time_" + this.name,
|
|
209
202
|
step: 0.01, min: 0, precision: 3,
|
|
210
203
|
skipSlider: true
|
|
211
204
|
});
|
|
212
205
|
|
|
213
206
|
header.addNumber("Duration", + this.duration.toFixed(3), (value, event) => {
|
|
214
|
-
this.setDuration(value, false)
|
|
207
|
+
this.setDuration(value, false, false);
|
|
215
208
|
}, {
|
|
216
|
-
units: "s",
|
|
209
|
+
// units: "s", // commented until lexgui is refactored. There is a performance hit while using units
|
|
217
210
|
step: 0.01, min: 0,
|
|
218
211
|
signal: "@on_set_duration_" + this.name
|
|
219
212
|
});
|
|
@@ -225,19 +218,30 @@ class Timeline {
|
|
|
225
218
|
signal: "@on_set_speed_" + this.name
|
|
226
219
|
});
|
|
227
220
|
|
|
228
|
-
if( this.
|
|
221
|
+
if( this.onCreateAfterTopBar )
|
|
229
222
|
{
|
|
230
|
-
this.
|
|
223
|
+
this.onCreateAfterTopBar( header );
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// settings buttons - optimize, settings, etc
|
|
227
|
+
|
|
228
|
+
const buttonContainerEnd = LX.makeContainer( ["auto", "100%"], "flex flex-row" );
|
|
229
|
+
header.queue( buttonContainerEnd );
|
|
230
|
+
|
|
231
|
+
if( this.onCreateSettingsButtons ){
|
|
232
|
+
this.onCreateSettingsButtons( header );
|
|
233
|
+
header.addBlank("0.05em", "auto");
|
|
231
234
|
}
|
|
232
235
|
|
|
233
236
|
if( this.onShowOptimizeMenu )
|
|
234
237
|
{
|
|
235
|
-
header.addButton(null,
|
|
238
|
+
header.addButton(null, "", (value, event) => {this.onShowOptimizeMenu(event)}, { title: "Optimize", icon:"fa-solid fa-filter" });
|
|
236
239
|
}
|
|
240
|
+
header.addBlank("0.05em", "auto");
|
|
237
241
|
|
|
238
242
|
if( this.onShowConfiguration )
|
|
239
243
|
{
|
|
240
|
-
header.addButton(null,
|
|
244
|
+
header.addButton(null, "", (value, event) => {
|
|
241
245
|
if(this.configurationDialog){
|
|
242
246
|
this.configurationDialog.close();
|
|
243
247
|
this.configurationDialog = null;
|
|
@@ -252,9 +256,12 @@ class Timeline {
|
|
|
252
256
|
root.remove();
|
|
253
257
|
}
|
|
254
258
|
})
|
|
255
|
-
}, { title: "Settings" })
|
|
259
|
+
}, { title: "Settings", icon: "fa-solid fa-gear" })
|
|
256
260
|
}
|
|
257
261
|
|
|
262
|
+
header.clearQueue( buttonContainerEnd );
|
|
263
|
+
header.addContent( "header-buttons-end", buttonContainerEnd );
|
|
264
|
+
|
|
258
265
|
header.endLine( "justify-around" );
|
|
259
266
|
}
|
|
260
267
|
|
|
@@ -262,22 +269,14 @@ class Timeline {
|
|
|
262
269
|
* @method updateLeftPanel
|
|
263
270
|
*
|
|
264
271
|
*/
|
|
265
|
-
updateLeftPanel(
|
|
272
|
+
updateLeftPanel( ) {
|
|
266
273
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
{
|
|
270
|
-
scrollTop = this.leftPanel.root.children[ 1 ].scrollTop;
|
|
271
|
-
this.leftPanel.clear();
|
|
272
|
-
}
|
|
273
|
-
else
|
|
274
|
-
{
|
|
275
|
-
this.leftPanel = area.addPanel( { className: 'lextimelinepanel', width: "100%", height: "100%" } );
|
|
276
|
-
}
|
|
274
|
+
const scrollTop = this.trackTreesPanel ? this.trackTreesPanel.root.scrollTop : 0;
|
|
275
|
+
this.leftPanel.clear();
|
|
277
276
|
|
|
278
|
-
|
|
277
|
+
const panel = this.leftPanel;
|
|
278
|
+
|
|
279
279
|
panel.sameLine( 2 );
|
|
280
|
-
|
|
281
280
|
let titleWidget = panel.addTitle( "Tracks" );
|
|
282
281
|
let title = titleWidget.root;
|
|
283
282
|
|
|
@@ -287,7 +286,6 @@ class Timeline {
|
|
|
287
286
|
this.addNewTrack();
|
|
288
287
|
}, { hideName: true, title: "Add Track" });
|
|
289
288
|
}
|
|
290
|
-
|
|
291
289
|
panel.endLine();
|
|
292
290
|
|
|
293
291
|
const styles = window.getComputedStyle( title );
|
|
@@ -295,10 +293,9 @@ class Timeline {
|
|
|
295
293
|
|
|
296
294
|
let p = new LX.Panel({height: "calc(100% - " + titleHeight + "px)"});
|
|
297
295
|
|
|
296
|
+
let treeTracks = [];
|
|
298
297
|
if( this.animationClip && this.selectedItems.length )
|
|
299
298
|
{
|
|
300
|
-
let items = { 'id': '', 'children': [] };
|
|
301
|
-
|
|
302
299
|
const tracksPerItem = this.animationClip.tracksPerItem;
|
|
303
300
|
for( let i = 0; i < this.selectedItems.length; i++ )
|
|
304
301
|
{
|
|
@@ -347,66 +344,71 @@ class Timeline {
|
|
|
347
344
|
// panel.addTitle(track.name + (track.type? '(' + track.type + ')' : ''));
|
|
348
345
|
}
|
|
349
346
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
347
|
+
treeTracks.push( t );
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
}
|
|
351
|
+
this.trackTreesWidget = p.addTree(null, treeTracks, {filter: false, rename: false, draggable: false, onevent: (e) => {
|
|
352
|
+
switch(e.type) {
|
|
353
|
+
case LX.TreeEvent.NODE_SELECTED:
|
|
354
|
+
if (e.node.parent){
|
|
355
|
+
const tracksInItem = this.animationClip.tracksPerItem[e.node.parent.id];
|
|
356
|
+
const type = e.node.id;
|
|
357
|
+
for(let i = 0; i < tracksInItem.length; i++) {
|
|
358
|
+
if(tracksInItem[i].type == type){
|
|
359
|
+
this.selectTrack(tracksInItem[i].clipIdx);
|
|
360
|
+
break;
|
|
364
361
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
break;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
break;
|
|
380
|
-
case LX.TreeEvent.NODE_CARETCHANGED:
|
|
381
|
-
const tracksInItem = this.animationClip.tracksPerItem[e.node.id];
|
|
382
|
-
for( let i = 0; i < tracksInItem; ++i )
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
case LX.TreeEvent.NODE_VISIBILITY:
|
|
366
|
+
if ( e.node.parent )
|
|
367
|
+
{
|
|
368
|
+
const tracksInItem = this.animationClip.tracksPerItem[ e.node.parent.id ];
|
|
369
|
+
const type = e.node.id;
|
|
370
|
+
for(let i = 0; i < tracksInItem.length; i++) {
|
|
371
|
+
if(tracksInItem[i].type == type)
|
|
383
372
|
{
|
|
384
|
-
this.
|
|
373
|
+
this.changeTrackVisibility( tracksInItem[ i ].clipIdx, e.value );
|
|
374
|
+
break;
|
|
385
375
|
}
|
|
386
|
-
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
case LX.TreeEvent.NODE_CARETCHANGED:
|
|
380
|
+
const tracksInItem = this.animationClip.tracksPerItem[e.node.id];
|
|
381
|
+
for( let i = 0; i < tracksInItem; ++i )
|
|
382
|
+
{
|
|
383
|
+
this.changeTrackDisplay( tracksInItem[ i ].clipIdx, e.node.closed );
|
|
387
384
|
}
|
|
388
|
-
|
|
389
|
-
|
|
385
|
+
break;
|
|
390
386
|
}
|
|
391
|
-
}
|
|
387
|
+
}});
|
|
388
|
+
// setting a name in the addTree function adds an undesired node
|
|
389
|
+
this.trackTreesWidget.name = "tracksTrees";
|
|
390
|
+
p.widgets[this.trackTreesWidget.name] = this.trackTreesWidget;
|
|
392
391
|
|
|
393
|
-
|
|
394
|
-
p.root
|
|
392
|
+
this.trackTreesPanel = p;
|
|
393
|
+
panel.attach( p.root );
|
|
395
394
|
p.root.addEventListener("scroll", e => {
|
|
396
|
-
|
|
395
|
+
if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight){
|
|
396
|
+
this.currentScroll = e.currentTarget.scrollTop / (e.currentTarget.scrollHeight - e.currentTarget.clientHeight);
|
|
397
|
+
this.currentScrollInPixels = e.currentTarget.scrollTop;
|
|
398
|
+
}
|
|
399
|
+
else{
|
|
400
|
+
this.currentScroll = 0;
|
|
401
|
+
this.currentScrollInPixels = 0;
|
|
402
|
+
}
|
|
397
403
|
});
|
|
398
|
-
// for(let i = 0; i < this.animationClip.tracks.length; i++) {
|
|
399
|
-
// let track = this.animationClip.tracks[i];
|
|
400
|
-
// panel.addTitle(track.name + (track.type? '(' + track.type + ')' : ''));
|
|
401
|
-
// }
|
|
402
|
-
this.leftPanel.root.children[ 1 ].scrollTop = scrollTop;
|
|
403
404
|
|
|
404
|
-
|
|
405
|
-
|
|
405
|
+
this.trackTreesPanel.scrollTop = scrollTop;
|
|
406
|
+
|
|
407
|
+
if( this.leftPanel.parent.root.classList.contains("hidden") || !this.mainArea.root.parent ){
|
|
406
408
|
return;
|
|
407
409
|
}
|
|
408
410
|
|
|
409
|
-
this.resizeCanvas(
|
|
411
|
+
this.resizeCanvas();
|
|
410
412
|
}
|
|
411
413
|
|
|
412
414
|
/**
|
|
@@ -497,7 +499,6 @@ class Timeline {
|
|
|
497
499
|
this.duration = this.animationClip.duration;
|
|
498
500
|
this.speed = this.animationClip.speed ?? this.speed;
|
|
499
501
|
|
|
500
|
-
//this.updateHeader();
|
|
501
502
|
this.updateLeftPanel();
|
|
502
503
|
|
|
503
504
|
return this.animationClip;
|
|
@@ -514,28 +515,30 @@ class Timeline {
|
|
|
514
515
|
|
|
515
516
|
// background of timeinfo
|
|
516
517
|
ctx.fillStyle = Timeline.BACKGROUND_COLOR;
|
|
517
|
-
ctx.fillRect(
|
|
518
|
-
ctx.strokeStyle =
|
|
518
|
+
ctx.fillRect( 0, 0, this.canvas.width, h );
|
|
519
|
+
ctx.strokeStyle = Timeline.FONT_COLOR_PRIMARY;
|
|
519
520
|
|
|
520
521
|
// set tick and sub tick times
|
|
521
522
|
let tickTime = 4;
|
|
522
|
-
if ( this.
|
|
523
|
-
else if ( this.
|
|
524
|
-
else if ( this.
|
|
523
|
+
if ( this.pixelsPerSecond > 900 ) { tickTime = 1; }
|
|
524
|
+
else if ( this.pixelsPerSecond > 100 ) { tickTime = 2; }
|
|
525
|
+
else if ( this.pixelsPerSecond > 50 ) { tickTime = 3; }
|
|
525
526
|
|
|
526
527
|
let subtickTime = this.timeSeparators[tickTime - 1];
|
|
527
528
|
tickTime = this.timeSeparators[tickTime];
|
|
528
529
|
|
|
530
|
+
const startTime = this.visualTimeRange[0];
|
|
531
|
+
const endTime = this.visualTimeRange[1];
|
|
529
532
|
// Transform times into pixel coords
|
|
530
|
-
let tickX = this.timeToX(
|
|
533
|
+
let tickX = this.timeToX( startTime + tickTime ) - this.timeToX( startTime );
|
|
531
534
|
let subtickX = subtickTime * tickX / tickTime;
|
|
532
535
|
|
|
533
|
-
let startx = this.timeToX( Math.floor(
|
|
534
|
-
let endx = this.timeToX(
|
|
536
|
+
let startx = this.timeToX( Math.floor( startTime / tickTime) * tickTime ); // floor because might need to draw previous subticks
|
|
537
|
+
let endx = this.timeToX( endTime ); // draw up to endTime
|
|
535
538
|
|
|
536
539
|
// Begin drawing
|
|
537
540
|
ctx.beginPath();
|
|
538
|
-
ctx.fillStyle = Timeline.
|
|
541
|
+
ctx.fillStyle = Timeline.FONT_COLOR_PRIMARY;
|
|
539
542
|
ctx.globalAlpha = this.opacity;
|
|
540
543
|
|
|
541
544
|
for( let x = startx; x <= endx; x += tickX )
|
|
@@ -569,34 +572,35 @@ class Timeline {
|
|
|
569
572
|
ctx.globalAlpha = this.opacity;
|
|
570
573
|
|
|
571
574
|
// Content
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
+
const topMargin = this.topMargin;
|
|
576
|
+
const treeOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop;
|
|
577
|
+
const line_height = this.trackHeight;
|
|
575
578
|
|
|
576
579
|
//fill track lines
|
|
577
580
|
w = w || canvas.width;
|
|
578
|
-
let max_tracks = Math.ceil( (h -
|
|
581
|
+
let max_tracks = Math.ceil( (h - topMargin) / line_height ) + 1;
|
|
579
582
|
|
|
580
583
|
ctx.save();
|
|
581
584
|
ctx.fillStyle = Timeline.TRACK_COLOR_SECONDARY;
|
|
582
|
-
|
|
583
|
-
|
|
585
|
+
|
|
586
|
+
const rectsOffset = this.currentScrollInPixels % line_height;
|
|
587
|
+
const blackOrWhite = 1 - Math.floor(this.currentScrollInPixels / line_height ) % 2;
|
|
588
|
+
for(let i = blackOrWhite; i <= max_tracks; i+=2)
|
|
584
589
|
{
|
|
585
|
-
ctx.fillRect(0,
|
|
590
|
+
ctx.fillRect(0, treeOffset - rectsOffset + i * line_height, w, line_height );
|
|
586
591
|
}
|
|
587
|
-
ctx.globalAlpha = this.opacity;
|
|
588
592
|
|
|
589
593
|
//bg lines
|
|
590
594
|
ctx.strokeStyle = Timeline.TRACK_COLOR_TERCIARY;
|
|
591
595
|
ctx.beginPath();
|
|
592
596
|
|
|
593
597
|
let pos = this.timeToX( 0 );
|
|
594
|
-
if(pos <
|
|
595
|
-
pos =
|
|
598
|
+
if(pos < 0)
|
|
599
|
+
pos = 0;
|
|
596
600
|
ctx.lineWidth = 1;
|
|
597
|
-
ctx.moveTo( pos + 0.5,
|
|
601
|
+
ctx.moveTo( pos + 0.5, topMargin);
|
|
598
602
|
ctx.lineTo( pos + 0.5, canvas.height);
|
|
599
|
-
ctx.moveTo( Math.round( this.timeToX( duration ) ) + 0.5,
|
|
603
|
+
ctx.moveTo( Math.round( this.timeToX( duration ) ) + 0.5, topMargin);
|
|
600
604
|
ctx.lineTo( Math.round( this.timeToX( duration ) ) + 0.5, canvas.height);
|
|
601
605
|
ctx.stroke();
|
|
602
606
|
|
|
@@ -605,36 +609,33 @@ class Timeline {
|
|
|
605
609
|
|
|
606
610
|
/**
|
|
607
611
|
* @method draw
|
|
608
|
-
* @param {*} rect (optional)
|
|
609
612
|
*/
|
|
610
613
|
|
|
611
|
-
draw(
|
|
614
|
+
draw( ) {
|
|
612
615
|
|
|
613
616
|
let ctx = this.canvas.getContext("2d");
|
|
614
617
|
ctx.textBaseline = "bottom";
|
|
615
618
|
ctx.font = "11px " + Timeline.FONT;//"11px Calibri";
|
|
616
|
-
if(!rect)
|
|
617
|
-
rect = [0, 0, ctx.canvas.width, ctx.canvas.height ];
|
|
618
619
|
|
|
619
620
|
// this.canvas = ctx.canvas;
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
let w = rect[2];
|
|
623
|
-
let h = rect[3];
|
|
624
|
-
// this.updateHeader();
|
|
625
|
-
this.currentScrollInPixels = this.scrollableHeight <= h ? 0 : (this.currentScroll * (this.scrollableHeight - h));
|
|
621
|
+
const w = ctx.canvas.width;
|
|
622
|
+
const h = ctx.canvas.height;
|
|
626
623
|
|
|
627
|
-
|
|
628
|
-
this.
|
|
629
|
-
if(this.startTime < 0)
|
|
630
|
-
this.startTime = 0;
|
|
631
|
-
// this.endTime = Math.ceil( this.startTime + (w - this.session.left_margin) * this.pixelsToSeconds );
|
|
632
|
-
this.endTime = this.session.start_time + (w - this.session.left_margin) * this.pixelsToSeconds;
|
|
633
|
-
if(this.endTime > this.duration)
|
|
634
|
-
this.endTime = this.duration;
|
|
635
|
-
if(this.startTime > this.endTime)
|
|
636
|
-
this.endTime = this.startTime;
|
|
624
|
+
const scrollableHeight = this.trackTreesWidget.root.scrollHeight;
|
|
625
|
+
const treeOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop;
|
|
637
626
|
|
|
627
|
+
if ( this.trackTreesPanel.root.scrollHeight > 0 ){
|
|
628
|
+
const ul = this.trackTreesWidget.innerTree.domEl.children[0];
|
|
629
|
+
this.trackHeight = ul.children.length < 1 ? 25 : (ul.offsetHeight / ul.children.length);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
//zoom
|
|
633
|
+
let startTime = this.visualOriginTime; //seconds
|
|
634
|
+
startTime = Math.min( this.duration, Math.max( 0, startTime ) );
|
|
635
|
+
let endTime = this.visualOriginTime + w * this.secondsPerPixel; //seconds
|
|
636
|
+
endTime = Math.max( startTime, Math.min( this.duration, endTime ) );
|
|
637
|
+
this.visualTimeRange[0] = startTime;
|
|
638
|
+
this.visualTimeRange[1] = endTime;
|
|
638
639
|
|
|
639
640
|
this.tracksDrawn.length = 0;
|
|
640
641
|
|
|
@@ -650,40 +651,38 @@ class Timeline {
|
|
|
650
651
|
}
|
|
651
652
|
|
|
652
653
|
if(this.animationClip) {
|
|
653
|
-
|
|
654
|
-
ctx.translate( this.position[0], this.position[1] + this.topMargin ); //20 is the top margin area
|
|
655
|
-
|
|
654
|
+
ctx.translate( 0, treeOffset );
|
|
656
655
|
this.drawContent( ctx, this.timeStart, this.timeEnd, this );
|
|
657
|
-
|
|
658
|
-
ctx.translate( -this.position[0], -(this.position[1] + this.topMargin) ); //20 is the top margin area
|
|
656
|
+
ctx.translate( 0, -treeOffset );
|
|
659
657
|
}
|
|
660
658
|
|
|
661
659
|
//scrollbar
|
|
662
|
-
if( h <
|
|
660
|
+
if( (h-this.topMargin) < scrollableHeight ){
|
|
663
661
|
ctx.fillStyle = "#222";
|
|
664
|
-
ctx.fillRect( w -
|
|
662
|
+
ctx.fillRect( w - 10, 0, 10, h );
|
|
665
663
|
|
|
666
|
-
ctx.fillStyle = this.grabbingScroll ? Timeline.
|
|
664
|
+
ctx.fillStyle = this.grabbingScroll ? Timeline.FONT_COLOR_PRIMARY : Timeline.FONT_COLOR_QUATERNARY;
|
|
667
665
|
|
|
668
|
-
let scrollBarHeight = Math.max( 10, (h-this.topMargin)* (h-this.topMargin)/ this.
|
|
666
|
+
let scrollBarHeight = Math.max( 10, (h-this.topMargin)* (h-this.topMargin)/ this.trackTreesPanel.root.scrollHeight);
|
|
669
667
|
let scrollLoc = this.currentScroll * ( h - this.topMargin - scrollBarHeight ) + this.topMargin;
|
|
670
668
|
ctx.roundRect( w - 10, scrollLoc, 10, scrollBarHeight, 5, true );
|
|
671
669
|
}
|
|
670
|
+
|
|
672
671
|
this.drawTimeInfo(w);
|
|
673
672
|
|
|
674
673
|
// Current time marker vertical line
|
|
675
674
|
let posx = Math.round( this.timeToX( this.currentTime ) );
|
|
676
675
|
let posy = this.topMargin * 0.4;
|
|
677
|
-
if(posx >=
|
|
676
|
+
if(posx >= 0)
|
|
678
677
|
{
|
|
679
|
-
ctx.strokeStyle = ctx.fillStyle =
|
|
678
|
+
ctx.strokeStyle = ctx.fillStyle = Timeline.TIME_MARKER_COLOR;
|
|
680
679
|
ctx.globalAlpha = this.opacity;
|
|
681
680
|
ctx.beginPath();
|
|
682
681
|
ctx.moveTo(posx, posy * 0.6); ctx.lineTo(posx, this.canvas.height);//line
|
|
683
682
|
ctx.stroke();
|
|
684
683
|
ctx.closePath();
|
|
685
684
|
ctx.shadowBlur = 8;
|
|
686
|
-
ctx.shadowColor =
|
|
685
|
+
ctx.shadowColor = Timeline.TIME_MARKER_COLOR;
|
|
687
686
|
ctx.shadowOffsetX = 1;
|
|
688
687
|
ctx.shadowOffsetY = 1;
|
|
689
688
|
|
|
@@ -698,12 +697,12 @@ class Timeline {
|
|
|
698
697
|
ctx.font = "11px " + Timeline.FONT;//"11px Calibri";
|
|
699
698
|
ctx.textAlign = "center";
|
|
700
699
|
//ctx.textBaseline = "middle";
|
|
701
|
-
ctx.fillStyle = Timeline.
|
|
700
|
+
ctx.fillStyle = Timeline.TIME_MARKER_COLOR_TEXT;
|
|
702
701
|
ctx.fillText( (Math.floor(this.currentTime*10)*0.1).toFixed(1), posx, this.topMargin * 0.6 );
|
|
703
702
|
|
|
704
703
|
// Selections
|
|
705
|
-
ctx.strokeStyle = ctx.fillStyle =
|
|
706
|
-
ctx.translate(
|
|
704
|
+
ctx.strokeStyle = ctx.fillStyle = Timeline.FONT_COLOR_PRIMARY;
|
|
705
|
+
ctx.translate( 0, this.topMargin );
|
|
707
706
|
if(this.boxSelection) {
|
|
708
707
|
ctx.globalAlpha = 0.15 * this.opacity;
|
|
709
708
|
ctx.fillStyle = Timeline.BOX_SELECTION_COLOR;
|
|
@@ -712,7 +711,7 @@ class Timeline {
|
|
|
712
711
|
ctx.stroke();
|
|
713
712
|
ctx.globalAlpha = this.opacity;
|
|
714
713
|
}
|
|
715
|
-
ctx.translate(
|
|
714
|
+
ctx.translate( 0, -this.topMargin );
|
|
716
715
|
|
|
717
716
|
}
|
|
718
717
|
|
|
@@ -730,8 +729,8 @@ class Timeline {
|
|
|
730
729
|
let markersPos = [];
|
|
731
730
|
for (let i = 0; i < markers.length; ++i) {
|
|
732
731
|
let marker = markers[i];
|
|
733
|
-
if (marker.time < this.
|
|
734
|
-
marker.time > this.
|
|
732
|
+
if (marker.time < this.visualTimeRange[0] - this.secondsPerPixel * 100 ||
|
|
733
|
+
marker.time > this.visualTimeRange[1])
|
|
735
734
|
continue;
|
|
736
735
|
var x = this.timeToX(marker.time);
|
|
737
736
|
markersPos.push(x);
|
|
@@ -769,14 +768,12 @@ class Timeline {
|
|
|
769
768
|
* @param {Number} t
|
|
770
769
|
*/
|
|
771
770
|
|
|
772
|
-
setDuration( t,
|
|
771
|
+
setDuration( t, skipCallback = false, updateHeader = true ) {
|
|
773
772
|
let v = this.validateDuration(t);
|
|
774
|
-
let decimals = t.toString().split('.')[1] ? t.toString().split('.')[1].length : 0;
|
|
775
|
-
updateHeader = (updateHeader || +v.toFixed(decimals) != t);
|
|
776
773
|
this.duration = this.animationClip.duration = v;
|
|
777
774
|
|
|
778
775
|
if(updateHeader) {
|
|
779
|
-
LX.emit( "@on_set_duration_" + this.name, +this.duration.toFixed(
|
|
776
|
+
LX.emit( "@on_set_duration_" + this.name, +this.duration.toFixed(2)); // skipcallback = true
|
|
780
777
|
}
|
|
781
778
|
|
|
782
779
|
if( this.onSetDuration && !skipCallback )
|
|
@@ -816,30 +813,26 @@ class Timeline {
|
|
|
816
813
|
|
|
817
814
|
// Converts distance in pixels to time
|
|
818
815
|
xToTime( x ) {
|
|
819
|
-
return
|
|
816
|
+
return x * this.secondsPerPixel + this.visualOriginTime;
|
|
820
817
|
}
|
|
821
818
|
|
|
822
819
|
// Converts time to disance in pixels
|
|
823
820
|
timeToX( t ) {
|
|
824
|
-
return
|
|
821
|
+
return (t - this.visualOriginTime) * this.pixelsPerSecond;
|
|
825
822
|
}
|
|
826
823
|
|
|
827
824
|
/**
|
|
828
825
|
* @method setScale
|
|
829
|
-
* @param {*}
|
|
826
|
+
* @param {*} pixelsPerSecond >0. totalVisiblePixels / totalVisibleSeconds.
|
|
830
827
|
*/
|
|
831
828
|
|
|
832
|
-
setScale(
|
|
833
|
-
|
|
834
|
-
if(!this.session)
|
|
835
|
-
return;
|
|
836
|
-
|
|
829
|
+
setScale( pixelsPerSecond ) {
|
|
837
830
|
const xCurrentTime = this.timeToX(this.currentTime);
|
|
838
|
-
this.
|
|
839
|
-
this.
|
|
831
|
+
this.pixelsPerSecond = pixelsPerSecond;
|
|
832
|
+
this.pixelsPerSecond = Math.max( 0.00001, this.pixelsPerSecond );
|
|
840
833
|
|
|
841
|
-
this.
|
|
842
|
-
this.
|
|
834
|
+
this.secondsPerPixel = 1 / this.pixelsPerSecond;
|
|
835
|
+
this.visualOriginTime += this.currentTime - this.xToTime(xCurrentTime);
|
|
843
836
|
}
|
|
844
837
|
|
|
845
838
|
/**
|
|
@@ -862,12 +855,12 @@ class Timeline {
|
|
|
862
855
|
let y = e.offsetY;
|
|
863
856
|
e.deltax = x - this.lastMouse[0];
|
|
864
857
|
e.deltay = y - this.lastMouse[1];
|
|
865
|
-
let localX = e.offsetX
|
|
866
|
-
let localY = e.offsetY
|
|
858
|
+
let localX = e.offsetX;
|
|
859
|
+
let localY = e.offsetY;
|
|
867
860
|
|
|
868
861
|
let timeX = this.timeToX( this.currentTime );
|
|
869
|
-
let isHoveringTimeBar = localY < this.topMargin &&
|
|
870
|
-
|
|
862
|
+
let isHoveringTimeBar = localY < this.topMargin &&
|
|
863
|
+
localX > (timeX - 6) && localX < (timeX + 6);
|
|
871
864
|
|
|
872
865
|
if( isHoveringTimeBar ) {
|
|
873
866
|
this.canvas.style.cursor = "col-resize";
|
|
@@ -885,16 +878,16 @@ class Timeline {
|
|
|
885
878
|
if( e.type == "wheel" ) {
|
|
886
879
|
if(e.shiftKey)
|
|
887
880
|
{
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
881
|
+
if ( e.wheelDelta ){
|
|
882
|
+
let mouseTime = this.xToTime(localX);
|
|
883
|
+
this.setScale( this.pixelsPerSecond * (e.wheelDelta < 0 ? 0.95 : 1.05) );
|
|
884
|
+
this.visualOriginTime = mouseTime - localX * this.secondsPerPixel;
|
|
885
|
+
}
|
|
886
|
+
|
|
894
887
|
}
|
|
895
|
-
else if( h < this.
|
|
888
|
+
else if( (h-this.topMargin) < this.trackTreesWidget.root.scrollHeight)
|
|
896
889
|
{
|
|
897
|
-
this.
|
|
890
|
+
this.trackTreesPanel.root.scrollTop += e.deltaY; // wheel deltaY
|
|
898
891
|
}
|
|
899
892
|
|
|
900
893
|
if ( this.onMouse ){
|
|
@@ -905,8 +898,8 @@ class Timeline {
|
|
|
905
898
|
|
|
906
899
|
var time = this.xToTime(x, true);
|
|
907
900
|
|
|
908
|
-
var is_inside = x >=
|
|
909
|
-
y >=
|
|
901
|
+
var is_inside = x >= 0 && x <= this.size[0] &&
|
|
902
|
+
y >= 0 && y <= this.size[1];
|
|
910
903
|
|
|
911
904
|
var track = null;
|
|
912
905
|
for(var i = this.tracksDrawn.length - 1; i >= 0; --i)
|
|
@@ -971,7 +964,7 @@ class Timeline {
|
|
|
971
964
|
this.grabbingTimeBar = true;
|
|
972
965
|
this.setTime(time);
|
|
973
966
|
}
|
|
974
|
-
else if( h < this.
|
|
967
|
+
else if( (h-this.topMargin) < this.trackTreesWidget.root.scrollHeight && x > w - 10 ) { // grabbing scroll bar
|
|
975
968
|
this.grabbing = true;
|
|
976
969
|
this.grabbingScroll = true;
|
|
977
970
|
}
|
|
@@ -999,22 +992,22 @@ class Timeline {
|
|
|
999
992
|
}
|
|
1000
993
|
else if(this.grabbingScroll)
|
|
1001
994
|
{
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
995
|
+
// will automatically call scroll event
|
|
996
|
+
if ( y < this.topMargin ){
|
|
997
|
+
this.trackTreesPanel.root.scrollTop = 0;
|
|
998
|
+
}
|
|
999
|
+
else{
|
|
1000
|
+
this.trackTreesPanel.root.scrollTop += this.trackTreesPanel.root.scrollHeight * e.deltay / (h-this.topMargin);
|
|
1001
|
+
}
|
|
1009
1002
|
}
|
|
1010
1003
|
else
|
|
1011
1004
|
{
|
|
1012
1005
|
// Move timeline in X (independent of current time)
|
|
1013
1006
|
var old = this.xToTime( this.lastMouse[0] );
|
|
1014
1007
|
var now = this.xToTime( e.offsetX );
|
|
1015
|
-
this.
|
|
1008
|
+
this.visualOriginTime += (old - now);
|
|
1016
1009
|
|
|
1017
|
-
this.
|
|
1010
|
+
this.trackTreesPanel.root.scrollTop -= e.deltay; // will automatically call scroll event
|
|
1018
1011
|
|
|
1019
1012
|
}
|
|
1020
1013
|
}
|
|
@@ -1077,15 +1070,43 @@ class Timeline {
|
|
|
1077
1070
|
|
|
1078
1071
|
/**
|
|
1079
1072
|
* @method changeState
|
|
1073
|
+
* @param {bool} skipCallback defaults false
|
|
1080
1074
|
* @description change play/pause state
|
|
1081
|
-
* ...
|
|
1082
1075
|
**/
|
|
1083
|
-
changeState() {
|
|
1084
|
-
this.
|
|
1085
|
-
|
|
1076
|
+
changeState(skipCallback = false) {
|
|
1077
|
+
this.setState(!this.playing, skipCallback);
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* @method setState
|
|
1081
|
+
* @param {bool} state
|
|
1082
|
+
* @param {bool} skipCallback defaults false
|
|
1083
|
+
* @description change play/pause state
|
|
1084
|
+
**/
|
|
1085
|
+
setState(state, skipCallback = false) {
|
|
1086
|
+
this.playing = state;
|
|
1087
|
+
|
|
1088
|
+
this.header.widgets.playBtn.root.setState(this.playing, true);
|
|
1089
|
+
|
|
1090
|
+
if(this.onStateChange && !skipCallback) {
|
|
1091
|
+
this.onStateChange(this.playing);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1086
1094
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1095
|
+
/**
|
|
1096
|
+
* @method setLoopMode
|
|
1097
|
+
* @param {bool} loopState
|
|
1098
|
+
* @param {bool} skipCallback defaults false
|
|
1099
|
+
* @description change loop mode of the timeline
|
|
1100
|
+
*/
|
|
1101
|
+
setLoopMode(loopState, skipCallback = false){
|
|
1102
|
+
this.loop = loopState;
|
|
1103
|
+
if ( this.loop ){
|
|
1104
|
+
this.header.widgets.loopBtn.root.children[0].classList.add("selected");
|
|
1105
|
+
}else{
|
|
1106
|
+
this.header.widgets.loopBtn.root.children[0].classList.remove("selected")
|
|
1107
|
+
}
|
|
1108
|
+
if( this.onChangeLoopMode && !skipCallback ){
|
|
1109
|
+
this.onChangeLoopMode( this.loop );
|
|
1089
1110
|
}
|
|
1090
1111
|
}
|
|
1091
1112
|
|
|
@@ -1096,38 +1117,33 @@ class Timeline {
|
|
|
1096
1117
|
|
|
1097
1118
|
drawTrackWithBoxes( ctx, y, trackHeight, title, track ) {
|
|
1098
1119
|
|
|
1120
|
+
const treeOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop;
|
|
1121
|
+
this.tracksDrawn.push([track, y + treeOffset, trackHeight]);
|
|
1122
|
+
|
|
1123
|
+
// Fill track background if it's selected
|
|
1124
|
+
ctx.globalAlpha = 0.2 * this.opacity;
|
|
1125
|
+
ctx.fillStyle = Timeline.TRACK_SELECTED_LIGHT;
|
|
1126
|
+
if(track.isSelected) {
|
|
1127
|
+
ctx.fillRect(0, y, ctx.canvas.width, trackHeight );
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const clips = track.clips;
|
|
1131
|
+
if(!clips) {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1099
1135
|
const offset = (trackHeight - trackHeight * 0.6) * 0.5;
|
|
1100
|
-
this.tracksDrawn.push([track, y + this.topMargin, trackHeight]);
|
|
1101
1136
|
|
|
1102
1137
|
trackHeight *= 0.6;
|
|
1103
|
-
this.canvas = this.canvas || ctx.canvas;
|
|
1104
1138
|
|
|
1105
1139
|
let selectedClipArea = null;
|
|
1106
1140
|
|
|
1107
|
-
if(track.enabled === false) {
|
|
1108
|
-
ctx.globalAlpha = 0.4 * this.opacity;
|
|
1109
|
-
}
|
|
1110
|
-
else {
|
|
1111
|
-
ctx.globalAlpha = 0.2 * this.opacity;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
1141
|
ctx.font = Math.floor( trackHeight * 0.8) + "px" + Timeline.FONT;
|
|
1115
1142
|
ctx.textAlign = "left";
|
|
1116
1143
|
ctx.textBaseline = "middle";
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
// Fill track background if it's selected
|
|
1120
|
-
if(track.isSelected) {
|
|
1121
|
-
ctx.fillRect(0, y + offset - 2, ctx.canvas.width, trackHeight + 4 );
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
let clips = track.clips;
|
|
1125
|
-
let trackAlpha = this.opacity;
|
|
1126
|
-
|
|
1127
|
-
if(!clips) {
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1144
|
+
const trackAlpha = this.opacity;
|
|
1130
1145
|
|
|
1146
|
+
|
|
1131
1147
|
for(var j = 0; j < clips.length; ++j)
|
|
1132
1148
|
{
|
|
1133
1149
|
selectedClipArea = null;
|
|
@@ -1142,20 +1158,20 @@ class Timeline {
|
|
|
1142
1158
|
|
|
1143
1159
|
// Overwrite clip color state depending on its state
|
|
1144
1160
|
ctx.globalAlpha = trackAlpha;
|
|
1145
|
-
ctx.fillStyle = clip.clipColor || (track.hovered[j] ? Timeline.
|
|
1161
|
+
ctx.fillStyle = clip.clipColor || (track.hovered[j] ? Timeline.KEYFRAME_COLOR_HOVERED : (Timeline.KEYFRAME_COLOR));
|
|
1146
1162
|
if(track.selected[j] && !clip.clipColor) {
|
|
1147
1163
|
ctx.fillStyle = Timeline.TRACK_SELECTED;
|
|
1148
1164
|
}
|
|
1149
1165
|
if(!this.active || track.active == false) {
|
|
1150
|
-
ctx.fillStyle = Timeline.
|
|
1166
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_INACTIVE;
|
|
1151
1167
|
}
|
|
1152
1168
|
|
|
1153
1169
|
// Draw clip background
|
|
1154
1170
|
ctx.roundRect( x, y + offset, w, trackHeight , 5, true);
|
|
1155
1171
|
|
|
1156
1172
|
// Compute timeline position of fade-in and fade-out clip times
|
|
1157
|
-
let fadeinX = this.
|
|
1158
|
-
let fadeoutX = this.
|
|
1173
|
+
let fadeinX = this.pixelsPerSecond * ((clip.fadein || 0) - clip.start);
|
|
1174
|
+
let fadeoutX = this.pixelsPerSecond * (clip.start + clip.duration - (clip.fadeout || (clip.start + clip.duration)));
|
|
1159
1175
|
|
|
1160
1176
|
if(this.active && track.active) {
|
|
1161
1177
|
// Transform fade-in and fade-out fill color to RGBA
|
|
@@ -1177,7 +1193,7 @@ class Timeline {
|
|
|
1177
1193
|
}
|
|
1178
1194
|
}
|
|
1179
1195
|
|
|
1180
|
-
ctx.fillStyle = clip.color || Timeline.
|
|
1196
|
+
ctx.fillStyle = clip.color || Timeline.FONT_COLOR_PRIMARY;
|
|
1181
1197
|
//ctx.font = "12px" + Timeline.FONT;
|
|
1182
1198
|
|
|
1183
1199
|
// Overwrite style and draw clip selection area if it's selected
|
|
@@ -1201,19 +1217,19 @@ class Timeline {
|
|
|
1201
1217
|
}
|
|
1202
1218
|
|
|
1203
1219
|
// Overwrite style with small font size if it's zoomed out
|
|
1204
|
-
if( this.
|
|
1205
|
-
ctx.font = this.
|
|
1220
|
+
if( this.pixelsPerSecond < 200) {
|
|
1221
|
+
ctx.font = this.pixelsPerSecond*0.06 +"px" + Timeline.FONT;
|
|
1206
1222
|
}
|
|
1207
1223
|
|
|
1208
1224
|
const text = clip.id.replaceAll("_", " ").replaceAll("-", " ");
|
|
1209
1225
|
const textInfo = ctx.measureText( text );
|
|
1210
1226
|
|
|
1211
1227
|
// Draw clip name if it's readable
|
|
1212
|
-
if(this.
|
|
1228
|
+
if(this.pixelsPerSecond > 100) {
|
|
1213
1229
|
ctx.fillText( text, x + (w - textInfo.width)*0.5, y + offset + trackHeight * 0.5);
|
|
1214
1230
|
}
|
|
1215
1231
|
|
|
1216
|
-
ctx.fillStyle = track.hovered[j] ? "white" : Timeline.
|
|
1232
|
+
ctx.fillStyle = track.hovered[j] ? "white" : Timeline.FONT_COLOR_PRIMARY;
|
|
1217
1233
|
// Draw resize bounding
|
|
1218
1234
|
ctx.roundRect(x + w - 8 , y + offset , 8, trackHeight, {tl: 4, bl: 4, tr:4, br:4}, true);
|
|
1219
1235
|
}
|
|
@@ -1224,7 +1240,7 @@ class Timeline {
|
|
|
1224
1240
|
/**
|
|
1225
1241
|
* @method selectTrack
|
|
1226
1242
|
* @param {int} trackIdx
|
|
1227
|
-
* // NOTE: to select a track from outside of the timeline, a this.
|
|
1243
|
+
* // NOTE: to select a track from outside of the timeline, a this.trackTreesWidget.innerTree.select(item) needs to be called.
|
|
1228
1244
|
*/
|
|
1229
1245
|
selectTrack( trackIdx ) {
|
|
1230
1246
|
|
|
@@ -1293,23 +1309,17 @@ class Timeline {
|
|
|
1293
1309
|
* @method resize
|
|
1294
1310
|
* @param {*} size
|
|
1295
1311
|
*/
|
|
1296
|
-
resize( size = [this.
|
|
1312
|
+
resize( size = [this.mainArea.parent.root.clientWidth, this.mainArea.parent.root.clientHeight]) {
|
|
1297
1313
|
|
|
1298
|
-
// this.root.root.style.width = size[0] + "px";
|
|
1299
|
-
// this.root.root.style.height = size[1] + "px";
|
|
1300
|
-
|
|
1301
1314
|
this.size = size;
|
|
1302
1315
|
//this.content_area.setSize([size[0], size[1] - this.header_offset]);
|
|
1303
|
-
this.
|
|
1316
|
+
this.mainArea.sections[1].root.style.height = "calc(100% - "+ this.header_offset + "px)";
|
|
1304
1317
|
|
|
1305
1318
|
let w = size[0] - this.leftPanel.root.clientWidth - 8;
|
|
1306
|
-
this.resizeCanvas(
|
|
1319
|
+
this.resizeCanvas();
|
|
1307
1320
|
}
|
|
1308
1321
|
|
|
1309
|
-
resizeCanvas(
|
|
1310
|
-
if( size[0] <= 0 && size[1] <=0 )
|
|
1311
|
-
return;
|
|
1312
|
-
size[1] -= this.header_offset;
|
|
1322
|
+
resizeCanvas( ) {
|
|
1313
1323
|
this.canvas.width = this.canvasArea.root.clientWidth;
|
|
1314
1324
|
this.canvas.height = this.canvasArea.root.clientHeight;
|
|
1315
1325
|
}
|
|
@@ -1319,7 +1329,7 @@ class Timeline {
|
|
|
1319
1329
|
* Hide timeline area
|
|
1320
1330
|
*/
|
|
1321
1331
|
hide() {
|
|
1322
|
-
this.
|
|
1332
|
+
this.mainArea.hide();
|
|
1323
1333
|
}
|
|
1324
1334
|
|
|
1325
1335
|
/**
|
|
@@ -1328,9 +1338,9 @@ class Timeline {
|
|
|
1328
1338
|
*/
|
|
1329
1339
|
show() {
|
|
1330
1340
|
|
|
1331
|
-
this.
|
|
1332
|
-
this.updateLeftPanel();
|
|
1341
|
+
this.mainArea.show();
|
|
1333
1342
|
this.resize();
|
|
1343
|
+
this.updateLeftPanel();
|
|
1334
1344
|
}
|
|
1335
1345
|
|
|
1336
1346
|
/**
|
|
@@ -1343,8 +1353,15 @@ class Timeline {
|
|
|
1343
1353
|
Timeline.TRACK_COLOR_TERCIARY = LX.getThemeColor("global-color-terciary");
|
|
1344
1354
|
Timeline.TRACK_COLOR_QUATERNARY = LX.getThemeColor("global-color-quaternary");
|
|
1345
1355
|
Timeline.FONT = LX.getThemeColor("global-font");
|
|
1346
|
-
Timeline.
|
|
1347
|
-
|
|
1356
|
+
Timeline.FONT_COLOR_PRIMARY = LX.getThemeColor("global-text-primary");
|
|
1357
|
+
Timeline.FONT_COLOR_QUATERNARY = LX.getThemeColor("global-text-quaternary");
|
|
1358
|
+
|
|
1359
|
+
Timeline.KEYFRAME_COLOR = LX.getThemeColor("lxTimeline-keyframe");
|
|
1360
|
+
Timeline.KEYFRAME_COLOR_SELECTED = Timeline.KEYFRAME_COLOR_HOVERED = LX.getThemeColor("lxTimeline-keyframe-selected");
|
|
1361
|
+
Timeline.KEYFRAME_COLOR_LOCK = LX.getThemeColor("lxTimeline-keyframe-locked");
|
|
1362
|
+
Timeline.KEYFRAME_COLOR_EDITED = LX.getThemeColor("lxTimeline-keyframe-edited");
|
|
1363
|
+
Timeline.KEYFRAME_COLOR_INACTIVE =LX.getThemeColor("lxTimeline-keyframe-inactive");
|
|
1364
|
+
}
|
|
1348
1365
|
};
|
|
1349
1366
|
|
|
1350
1367
|
Timeline.BACKGROUND_COLOR = LX.getThemeColor("global-blur-background");
|
|
@@ -1352,16 +1369,25 @@ Timeline.TRACK_COLOR_PRIMARY = LX.getThemeColor("global-color-primary");
|
|
|
1352
1369
|
Timeline.TRACK_COLOR_SECONDARY = LX.getThemeColor("global-color-secondary");
|
|
1353
1370
|
Timeline.TRACK_COLOR_TERCIARY = LX.getThemeColor("global-color-terciary");
|
|
1354
1371
|
Timeline.TRACK_COLOR_QUATERNARY = LX.getThemeColor("global-color-quaternary");
|
|
1355
|
-
Timeline.TRACK_SELECTED = LX.getThemeColor("global-
|
|
1356
|
-
Timeline.TRACK_SELECTED_LIGHT = LX.getThemeColor("global-
|
|
1372
|
+
Timeline.TRACK_SELECTED = LX.getThemeColor("global-color-accent");
|
|
1373
|
+
Timeline.TRACK_SELECTED_LIGHT = LX.getThemeColor("global-color-accent-light");
|
|
1357
1374
|
Timeline.FONT = LX.getThemeColor("global-font");
|
|
1358
|
-
Timeline.
|
|
1359
|
-
Timeline.
|
|
1360
|
-
Timeline.
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1375
|
+
Timeline.FONT_COLOR_PRIMARY = LX.getThemeColor("global-text-primary");
|
|
1376
|
+
Timeline.FONT_COLOR_QUATERNARY = LX.getThemeColor("global-text-quaternary");
|
|
1377
|
+
Timeline.TIME_MARKER_COLOR = LX.getThemeColor("global-color-accent");
|
|
1378
|
+
Timeline.TIME_MARKER_COLOR_TEXT = "#ffffff";
|
|
1379
|
+
|
|
1380
|
+
LX.setThemeColor("lxTimeline-keyframe", "light-dark(#2d69da,#2d69da)");
|
|
1381
|
+
LX.setThemeColor("lxTimeline-keyframe-selected", "light-dark(#f5c700,#fafa14)");
|
|
1382
|
+
LX.setThemeColor("lxTimeline-keyframe-hovered", "light-dark(#f5c700,#fafa14)");
|
|
1383
|
+
LX.setThemeColor("lxTimeline-keyframe-locked", "light-dark(#c62e2e,#ff7d7d)");
|
|
1384
|
+
LX.setThemeColor("lxTimeline-keyframe-edited", "light-dark(#00d000,#00d000)");
|
|
1385
|
+
LX.setThemeColor("lxTimeline-keyframe-inactive", "light-dark(#706b6b,#706b6b)");
|
|
1386
|
+
Timeline.KEYFRAME_COLOR = LX.getThemeColor("lxTimeline-keyframe");
|
|
1387
|
+
Timeline.KEYFRAME_COLOR_SELECTED = Timeline.KEYFRAME_COLOR_HOVERED = LX.getThemeColor("lxTimeline-keyframe-selected");
|
|
1388
|
+
Timeline.KEYFRAME_COLOR_LOCK = LX.getThemeColor("lxTimeline-keyframe-locked");
|
|
1389
|
+
Timeline.KEYFRAME_COLOR_EDITED = LX.getThemeColor("lxTimeline-keyframe-edited");
|
|
1390
|
+
Timeline.KEYFRAME_COLOR_INACTIVE =LX.getThemeColor("lxTimeline-keyframe-inactive");
|
|
1365
1391
|
Timeline.BOX_SELECTION_COLOR = "#AAA";
|
|
1366
1392
|
LX.Timeline = Timeline;
|
|
1367
1393
|
|
|
@@ -1394,7 +1420,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1394
1420
|
e.multipleSelection = true;
|
|
1395
1421
|
// Manual multiple selection
|
|
1396
1422
|
if(!discard && track) {
|
|
1397
|
-
const keyFrameIdx = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
1423
|
+
const keyFrameIdx = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
1398
1424
|
if ( keyFrameIdx > -1 ){
|
|
1399
1425
|
track.selected[keyFrameIdx] ?
|
|
1400
1426
|
this.unSelectKeyFrame(track, keyFrameIdx) :
|
|
@@ -1403,13 +1429,13 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1403
1429
|
}
|
|
1404
1430
|
// Box selection
|
|
1405
1431
|
else if(this.boxSelection) {
|
|
1406
|
-
let tracks = this.getTracksInRange(this.boxSelectionStart[1], this.boxSelectionEnd[1], this.
|
|
1432
|
+
let tracks = this.getTracksInRange(this.boxSelectionStart[1], this.boxSelectionEnd[1], this.secondsPerPixel * 5);
|
|
1407
1433
|
|
|
1408
1434
|
for(let t of tracks) {
|
|
1409
1435
|
let keyFrameIndices = this.getKeyFramesInRange(t,
|
|
1410
1436
|
this.xToTime( this.boxSelectionStart[0] ),
|
|
1411
1437
|
this.xToTime( this.boxSelectionEnd[0] ),
|
|
1412
|
-
this.
|
|
1438
|
+
this.secondsPerPixel * 5);
|
|
1413
1439
|
|
|
1414
1440
|
if(keyFrameIndices) {
|
|
1415
1441
|
for(let index = keyFrameIndices[0]; index <= keyFrameIndices[1]; ++index){
|
|
@@ -1428,7 +1454,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1428
1454
|
this.unSelectAllKeyFrames();
|
|
1429
1455
|
}
|
|
1430
1456
|
if (track){
|
|
1431
|
-
const keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
1457
|
+
const keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
1432
1458
|
if( keyFrameIndex > -1 ) {
|
|
1433
1459
|
this.processCurrentKeyFrame( e, keyFrameIndex, track, null, e.multipleSelection ); // Settings this as multiple so time is not being set
|
|
1434
1460
|
}
|
|
@@ -1555,7 +1581,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1555
1581
|
else if(track) {
|
|
1556
1582
|
|
|
1557
1583
|
this.unHoverAll();
|
|
1558
|
-
let keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
1584
|
+
let keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
1559
1585
|
if(keyFrameIndex > -1 ) {
|
|
1560
1586
|
|
|
1561
1587
|
const name = this.animationClip.tracksDictionary[track.fullname];
|
|
@@ -1638,43 +1664,44 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1638
1664
|
|
|
1639
1665
|
}
|
|
1640
1666
|
|
|
1641
|
-
drawContent( ctx
|
|
1667
|
+
drawContent( ctx ) {
|
|
1642
1668
|
|
|
1643
1669
|
if(!this.animationClip || !this.animationClip.tracksPerItem)
|
|
1644
1670
|
return;
|
|
1645
1671
|
|
|
1646
1672
|
ctx.save();
|
|
1647
|
-
this.scrollableHeight = this.topMargin;
|
|
1648
1673
|
|
|
1649
|
-
|
|
1674
|
+
const trackHeight = this.trackHeight;
|
|
1650
1675
|
const tracksPerItem = this.animationClip.tracksPerItem;
|
|
1676
|
+
const scrollY = - this.currentScrollInPixels;
|
|
1677
|
+
const treeOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop;
|
|
1678
|
+
|
|
1679
|
+
let offset = scrollY;
|
|
1680
|
+
ctx.translate(0, offset);
|
|
1681
|
+
|
|
1651
1682
|
for(let t = 0; t < this.selectedItems.length; t++) {
|
|
1652
|
-
let tracks = tracksPerItem[this.selectedItems[t]]
|
|
1683
|
+
let tracks = tracksPerItem[this.selectedItems[t]];
|
|
1653
1684
|
if(!tracks) continue;
|
|
1654
1685
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1686
|
+
offset += trackHeight;
|
|
1687
|
+
ctx.translate(0, trackHeight);
|
|
1688
|
+
|
|
1689
|
+
if ( this.trackTreesWidget.innerTree.data[t].closed ){
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1658
1692
|
|
|
1659
|
-
let offsetI = 0;
|
|
1660
1693
|
for(let i = 0; i < tracks.length; i++) {
|
|
1661
1694
|
let track = tracks[i];
|
|
1662
1695
|
if(track.hide) {
|
|
1663
1696
|
continue;
|
|
1664
1697
|
}
|
|
1665
1698
|
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
this.tracksDrawn.push([track, track_y + this.topMargin, height]);
|
|
1672
|
-
|
|
1673
|
-
ctx.restore();
|
|
1674
|
-
|
|
1675
|
-
offsetI++;
|
|
1699
|
+
this.drawTrackWithKeyframes(ctx, trackHeight, track);
|
|
1700
|
+
this.tracksDrawn.push([track, offset + treeOffset, trackHeight]);
|
|
1701
|
+
|
|
1702
|
+
offset += trackHeight;
|
|
1703
|
+
ctx.translate(0, trackHeight);
|
|
1676
1704
|
}
|
|
1677
|
-
offset += offsetI * height + height;
|
|
1678
1705
|
}
|
|
1679
1706
|
|
|
1680
1707
|
ctx.restore();
|
|
@@ -1699,19 +1726,22 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1699
1726
|
ctx.fillRect(0, 0, ctx.canvas.width, trackHeight );
|
|
1700
1727
|
}
|
|
1701
1728
|
|
|
1702
|
-
ctx.fillStyle = Timeline.
|
|
1729
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR;
|
|
1703
1730
|
ctx.globalAlpha = this.opacity;
|
|
1704
1731
|
|
|
1705
|
-
|
|
1732
|
+
const keyframes = track.times;
|
|
1706
1733
|
|
|
1707
1734
|
if(!keyframes) {
|
|
1708
1735
|
return;
|
|
1709
1736
|
}
|
|
1710
1737
|
|
|
1738
|
+
const startTime = this.visualTimeRange[0];
|
|
1739
|
+
const endTime = this.visualTimeRange[1];
|
|
1740
|
+
|
|
1711
1741
|
for(let j = 0; j < keyframes.length; ++j)
|
|
1712
1742
|
{
|
|
1713
1743
|
let time = keyframes[j];
|
|
1714
|
-
if( time <
|
|
1744
|
+
if( time < startTime || time > endTime ) {
|
|
1715
1745
|
continue;
|
|
1716
1746
|
}
|
|
1717
1747
|
|
|
@@ -1719,23 +1749,23 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1719
1749
|
let size = trackHeight * 0.3;
|
|
1720
1750
|
|
|
1721
1751
|
if(!this.active || track.active == false) {
|
|
1722
|
-
ctx.fillStyle = Timeline.
|
|
1752
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_INACTIVE;
|
|
1723
1753
|
}
|
|
1724
1754
|
else if(track.locked) {
|
|
1725
|
-
ctx.fillStyle = Timeline.
|
|
1755
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_LOCK;
|
|
1726
1756
|
}
|
|
1727
1757
|
else if(track.hovered[j]) {
|
|
1728
1758
|
size = trackHeight * 0.45;
|
|
1729
|
-
ctx.fillStyle = Timeline.
|
|
1759
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_HOVERED;
|
|
1730
1760
|
}
|
|
1731
1761
|
else if(track.selected[j]) {
|
|
1732
|
-
ctx.fillStyle = Timeline.
|
|
1762
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_SELECTED;
|
|
1733
1763
|
}
|
|
1734
1764
|
else if(track.edited[j]) {
|
|
1735
|
-
ctx.fillStyle = Timeline.
|
|
1765
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_EDITED;
|
|
1736
1766
|
}
|
|
1737
1767
|
else {
|
|
1738
|
-
ctx.fillStyle = Timeline.
|
|
1768
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR;
|
|
1739
1769
|
}
|
|
1740
1770
|
|
|
1741
1771
|
ctx.save();
|
|
@@ -2446,7 +2476,13 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2446
2476
|
|
|
2447
2477
|
this.unSelectAllKeyFrames();
|
|
2448
2478
|
this.unHoverAll();
|
|
2449
|
-
|
|
2479
|
+
|
|
2480
|
+
this.selectedItems = [];
|
|
2481
|
+
for( let i = 0; i < itemsName.length; ++i ){
|
|
2482
|
+
if ( this.animationClip.tracksPerItem[itemsName[i]] ){
|
|
2483
|
+
this.selectedItems.push(itemsName[i]);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2450
2486
|
this.updateLeftPanel();
|
|
2451
2487
|
}
|
|
2452
2488
|
|
|
@@ -2663,7 +2699,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2663
2699
|
this.unSelectAllKeyFrames();
|
|
2664
2700
|
}
|
|
2665
2701
|
|
|
2666
|
-
keyFrameIndex = keyFrameIndex ?? this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
2702
|
+
keyFrameIndex = keyFrameIndex ?? this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
2667
2703
|
if(keyFrameIndex < 0)
|
|
2668
2704
|
return;
|
|
2669
2705
|
|
|
@@ -2759,23 +2795,17 @@ class ClipsTimeline extends Timeline {
|
|
|
2759
2795
|
this.lastTrackClipsMove = 0; // vertical movement of clips, onMouseMove onMousedown
|
|
2760
2796
|
}
|
|
2761
2797
|
|
|
2762
|
-
updateLeftPanel(
|
|
2798
|
+
updateLeftPanel() {
|
|
2763
2799
|
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
scrollTop = this.leftPanel.root.children[1].scrollTop;
|
|
2767
|
-
this.leftPanel.clear();
|
|
2768
|
-
}
|
|
2769
|
-
else {
|
|
2770
|
-
this.leftPanel = area.addPanel({className: 'lextimelinepanel', width: "100%", height: "100%"});
|
|
2771
|
-
}
|
|
2800
|
+
const scrollTop = this.trackTreesPanel ? this.trackTreesPanel.root.scrollTop : 0;
|
|
2801
|
+
this.leftPanel.clear();
|
|
2772
2802
|
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
panel.sameLine(2);
|
|
2803
|
+
const panel = this.leftPanel;
|
|
2776
2804
|
|
|
2805
|
+
panel.sameLine(2);
|
|
2777
2806
|
let titleWidget = panel.addTitle("Tracks");
|
|
2778
2807
|
let title = titleWidget.root;
|
|
2808
|
+
|
|
2779
2809
|
if(!this.disableNewTracks)
|
|
2780
2810
|
{
|
|
2781
2811
|
panel.addButton("addTrackBtn", '<i class = "fa-solid fa-plus"></i>', (value, event) => {
|
|
@@ -2783,8 +2813,10 @@ class ClipsTimeline extends Timeline {
|
|
|
2783
2813
|
}, { hideName: true, title: "Add Track" });
|
|
2784
2814
|
}
|
|
2785
2815
|
panel.endLine();
|
|
2816
|
+
|
|
2786
2817
|
const styles = window.getComputedStyle(title);
|
|
2787
2818
|
const titleHeight = title.clientHeight + parseFloat(styles['marginTop']) + parseFloat(styles['marginBottom']);
|
|
2819
|
+
|
|
2788
2820
|
let p = new LX.Panel({height: "calc(100% - " + titleHeight + "px)"});
|
|
2789
2821
|
|
|
2790
2822
|
let treeTracks = [];
|
|
@@ -2798,12 +2830,11 @@ class ClipsTimeline extends Timeline {
|
|
|
2798
2830
|
'skipVisibility': this.skipVisibility,
|
|
2799
2831
|
'visible': track.active,
|
|
2800
2832
|
// 'selected' : track.isSelected
|
|
2801
|
-
} );
|
|
2802
|
-
|
|
2833
|
+
} );
|
|
2803
2834
|
}
|
|
2804
2835
|
|
|
2805
2836
|
}
|
|
2806
|
-
this.
|
|
2837
|
+
this.trackTreesWidget = p.addTree(null, treeTracks, {filter: false, rename: false, draggable: false, onevent: (e) => {
|
|
2807
2838
|
switch(e.type) {
|
|
2808
2839
|
case LX.TreeEvent.NODE_SELECTED:
|
|
2809
2840
|
this.selectTrack( parseInt( e.node.id.split("Track_")[1] ) );
|
|
@@ -2816,17 +2847,30 @@ class ClipsTimeline extends Timeline {
|
|
|
2816
2847
|
break;
|
|
2817
2848
|
}
|
|
2818
2849
|
}});
|
|
2850
|
+
// setting a name in the addTree function adds an undesired node
|
|
2851
|
+
this.trackTreesWidget.name = "tracksTrees";
|
|
2852
|
+
p.widgets[this.trackTreesWidget.name] = this.trackTreesWidget;
|
|
2853
|
+
|
|
2854
|
+
this.trackTreesPanel = p;
|
|
2819
2855
|
panel.attach(p.root)
|
|
2820
|
-
p.root.style.overflowY = "scroll";
|
|
2821
2856
|
p.root.addEventListener("scroll", (e) => {
|
|
2822
|
-
|
|
2823
|
-
|
|
2857
|
+
if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight){
|
|
2858
|
+
this.currentScroll = e.currentTarget.scrollTop / (e.currentTarget.scrollHeight - e.currentTarget.clientHeight);
|
|
2859
|
+
this.currentScrollInPixels = e.currentTarget.scrollTop;
|
|
2860
|
+
}
|
|
2861
|
+
else{
|
|
2862
|
+
this.currentScroll = 0;
|
|
2863
|
+
this.currentScrollInPixels = 0;
|
|
2864
|
+
}
|
|
2865
|
+
});
|
|
2824
2866
|
|
|
2825
|
-
this.
|
|
2867
|
+
this.trackTreesPanel.root.scrollTop = scrollTop;
|
|
2826
2868
|
|
|
2827
|
-
if(this.leftPanel.parent.root.classList.contains("hidden") || !this.
|
|
2869
|
+
if(this.leftPanel.parent.root.classList.contains("hidden") || !this.mainArea.root.parent){
|
|
2828
2870
|
return;
|
|
2829
|
-
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
this.resizeCanvas();
|
|
2830
2874
|
}
|
|
2831
2875
|
|
|
2832
2876
|
unSelectAllTracks() {
|
|
@@ -2856,7 +2900,7 @@ class ClipsTimeline extends Timeline {
|
|
|
2856
2900
|
// Manual Multiple selection
|
|
2857
2901
|
if(!discard) {
|
|
2858
2902
|
if ( track ){
|
|
2859
|
-
let clipIndex = this.getCurrentClip( track, this.xToTime( localX ), this.
|
|
2903
|
+
let clipIndex = this.getCurrentClip( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
2860
2904
|
if ( clipIndex > -1 ){
|
|
2861
2905
|
track.selected[clipIndex] ?
|
|
2862
2906
|
this.unselectClip( track, clipIndex, null ) :
|
|
@@ -2867,13 +2911,13 @@ class ClipsTimeline extends Timeline {
|
|
|
2867
2911
|
// Box selection
|
|
2868
2912
|
else if (this.boxSelection){
|
|
2869
2913
|
|
|
2870
|
-
let tracks = this.getTracksInRange(this.boxSelectionStart[1], this.boxSelectionEnd[1], this.
|
|
2914
|
+
let tracks = this.getTracksInRange(this.boxSelectionStart[1], this.boxSelectionEnd[1], this.secondsPerPixel * 5);
|
|
2871
2915
|
|
|
2872
2916
|
for(let t of tracks) {
|
|
2873
2917
|
let clipsIndices = this.getClipsInRange(t,
|
|
2874
2918
|
this.xToTime( this.boxSelectionStart[0] ),
|
|
2875
2919
|
this.xToTime( this.boxSelectionEnd[0] ),
|
|
2876
|
-
this.
|
|
2920
|
+
this.secondsPerPixel * 5);
|
|
2877
2921
|
|
|
2878
2922
|
if(clipsIndices) {
|
|
2879
2923
|
for(let index of clipsIndices)
|
|
@@ -2917,7 +2961,7 @@ class ClipsTimeline extends Timeline {
|
|
|
2917
2961
|
this.canvas.style.cursor = "grab";
|
|
2918
2962
|
let curTrackIdx = -1;
|
|
2919
2963
|
|
|
2920
|
-
this.lastTrackClipsMove = Math.floor( (e.localY - this.topMargin + this.
|
|
2964
|
+
this.lastTrackClipsMove = Math.floor( (e.localY - this.topMargin + this.trackTreesPanel.root.scrollTop) / this.trackHeight );
|
|
2921
2965
|
|
|
2922
2966
|
for(let i = 0; i < selectedClips.length; i++)
|
|
2923
2967
|
{
|
|
@@ -3001,7 +3045,8 @@ class ClipsTimeline extends Timeline {
|
|
|
3001
3045
|
|
|
3002
3046
|
this.movingKeys = true;
|
|
3003
3047
|
|
|
3004
|
-
|
|
3048
|
+
const treeOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop;
|
|
3049
|
+
let newTrackClipsMove = Math.floor( (e.localY - treeOffset) / this.trackHeight );
|
|
3005
3050
|
|
|
3006
3051
|
// move clips vertically
|
|
3007
3052
|
if ( e.altKey ){
|
|
@@ -3306,23 +3351,19 @@ class ClipsTimeline extends Timeline {
|
|
|
3306
3351
|
|
|
3307
3352
|
}
|
|
3308
3353
|
|
|
3309
|
-
drawContent( ctx
|
|
3354
|
+
drawContent( ctx ) {
|
|
3310
3355
|
|
|
3311
|
-
if(!this.animationClip)
|
|
3356
|
+
if(!this.animationClip || !this.animationClip.tracks)
|
|
3312
3357
|
return;
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
const height = this.trackHeight;
|
|
3318
|
-
|
|
3319
|
-
this.scrollableHeight = (tracks.length)*height + this.topMargin;
|
|
3320
|
-
let scroll_y = - this.currentScrollInPixels;
|
|
3358
|
+
|
|
3359
|
+
const tracks = this.animationClip.tracks;
|
|
3360
|
+
const trackHeight = this.trackHeight;
|
|
3361
|
+
const scrollY = - this.currentScrollInPixels;
|
|
3321
3362
|
|
|
3322
3363
|
ctx.save();
|
|
3323
3364
|
for(let i = 0; i < tracks.length; i++) {
|
|
3324
3365
|
let track = tracks[i];
|
|
3325
|
-
this.drawTrackWithBoxes(ctx,
|
|
3366
|
+
this.drawTrackWithBoxes(ctx, i * trackHeight + scrollY, trackHeight, track.name || "", track);
|
|
3326
3367
|
}
|
|
3327
3368
|
|
|
3328
3369
|
ctx.restore();
|
|
@@ -3841,7 +3882,7 @@ class ClipsTimeline extends Timeline {
|
|
|
3841
3882
|
|
|
3842
3883
|
selectClip( track, clipIndex = null, localX = null, unselect = true, skipCallback = false ) {
|
|
3843
3884
|
|
|
3844
|
-
clipIndex = clipIndex ?? this.getCurrentClip( track, this.xToTime( localX ), this.
|
|
3885
|
+
clipIndex = clipIndex ?? this.getCurrentClip( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
3845
3886
|
|
|
3846
3887
|
if(unselect){
|
|
3847
3888
|
this.unSelectAllClips();
|
|
@@ -3873,7 +3914,7 @@ class ClipsTimeline extends Timeline {
|
|
|
3873
3914
|
}
|
|
3874
3915
|
|
|
3875
3916
|
unselectClip( track, clipIndex, localX = null ){
|
|
3876
|
-
clipIndex = clipIndex ?? this.getCurrentClip( track, this.xToTime( localX ), this.
|
|
3917
|
+
clipIndex = clipIndex ?? this.getCurrentClip( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
3877
3918
|
|
|
3878
3919
|
if(clipIndex == -1)
|
|
3879
3920
|
return -1;
|
|
@@ -3977,7 +4018,7 @@ class CurvesTimeline extends Timeline {
|
|
|
3977
4018
|
e.multipleSelection = true;
|
|
3978
4019
|
// Manual multiple selection
|
|
3979
4020
|
if(!discard && track) {
|
|
3980
|
-
const keyFrameIdx = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
4021
|
+
const keyFrameIdx = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
3981
4022
|
if ( keyFrameIdx > -1 ){
|
|
3982
4023
|
track.selected[keyFrameIdx] ?
|
|
3983
4024
|
this.unSelectKeyFrame(track, keyFrameIdx) :
|
|
@@ -3986,13 +4027,13 @@ class CurvesTimeline extends Timeline {
|
|
|
3986
4027
|
}
|
|
3987
4028
|
// Box selection
|
|
3988
4029
|
else if(this.boxSelection) {
|
|
3989
|
-
let tracks = this.getTracksInRange(this.boxSelectionStart[1], this.boxSelectionEnd[1], this.
|
|
4030
|
+
let tracks = this.getTracksInRange(this.boxSelectionStart[1], this.boxSelectionEnd[1], this.secondsPerPixel * 5);
|
|
3990
4031
|
|
|
3991
4032
|
for(let t of tracks) {
|
|
3992
4033
|
let keyFrameIndices = this.getKeyFramesInRange(t,
|
|
3993
4034
|
this.xToTime( this.boxSelectionStart[0] ),
|
|
3994
4035
|
this.xToTime( this.boxSelectionEnd[0] ),
|
|
3995
|
-
this.
|
|
4036
|
+
this.secondsPerPixel * 5);
|
|
3996
4037
|
|
|
3997
4038
|
if(keyFrameIndices) {
|
|
3998
4039
|
for(let index = keyFrameIndices[0]; index <= keyFrameIndices[1]; ++index){
|
|
@@ -4011,7 +4052,7 @@ class CurvesTimeline extends Timeline {
|
|
|
4011
4052
|
this.unSelectAllKeyFrames();
|
|
4012
4053
|
}
|
|
4013
4054
|
if (track){
|
|
4014
|
-
const keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
4055
|
+
const keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
4015
4056
|
if( keyFrameIndex > -1 ) {
|
|
4016
4057
|
this.processCurrentKeyFrame( e, keyFrameIndex, track, null, e.multipleSelection ); // Settings this as multiple so time is not being set
|
|
4017
4058
|
}
|
|
@@ -4161,7 +4202,7 @@ class CurvesTimeline extends Timeline {
|
|
|
4161
4202
|
else if(track) {
|
|
4162
4203
|
|
|
4163
4204
|
this.unHoverAll();
|
|
4164
|
-
let keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
4205
|
+
let keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
4165
4206
|
if(keyFrameIndex > -1 ) {
|
|
4166
4207
|
|
|
4167
4208
|
const name = this.animationClip.tracksDictionary[track.fullname];
|
|
@@ -4245,42 +4286,44 @@ class CurvesTimeline extends Timeline {
|
|
|
4245
4286
|
|
|
4246
4287
|
}
|
|
4247
4288
|
|
|
4248
|
-
drawContent( ctx
|
|
4289
|
+
drawContent( ctx ) {
|
|
4249
4290
|
|
|
4250
4291
|
if(!this.animationClip || !this.animationClip.tracksPerItem)
|
|
4251
4292
|
return;
|
|
4252
4293
|
|
|
4253
4294
|
ctx.save();
|
|
4254
|
-
this.scrollableHeight = this.topMargin;
|
|
4255
4295
|
|
|
4256
|
-
|
|
4296
|
+
const trackHeight = this.trackHeight;
|
|
4297
|
+
const tracksPerItem = this.animationClip.tracksPerItem;
|
|
4298
|
+
const scrollY = - this.currentScrollInPixels;
|
|
4299
|
+
const treeOffset = this.trackTreesWidget.innerTree.domEl.offsetTop - this.canvas.offsetTop;
|
|
4300
|
+
|
|
4301
|
+
let offset = scrollY;
|
|
4302
|
+
ctx.translate(0, offset);
|
|
4303
|
+
|
|
4257
4304
|
for(let t = 0; t < this.selectedItems.length; t++) {
|
|
4258
|
-
let tracks =
|
|
4305
|
+
let tracks = tracksPerItem[this.selectedItems[t]];
|
|
4259
4306
|
if(!tracks) continue;
|
|
4260
4307
|
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
let scroll_y = - this.currentScrollInPixels;
|
|
4308
|
+
offset += trackHeight;
|
|
4309
|
+
ctx.translate(0, trackHeight);
|
|
4264
4310
|
|
|
4265
|
-
|
|
4311
|
+
if ( this.trackTreesWidget.innerTree.data[t].closed ){
|
|
4312
|
+
continue;
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4266
4315
|
for(let i = 0; i < tracks.length; i++) {
|
|
4267
4316
|
let track = tracks[i];
|
|
4268
4317
|
if(track.hide) {
|
|
4269
4318
|
continue;
|
|
4270
4319
|
}
|
|
4271
4320
|
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
this.tracksDrawn.push([track, track_y + this.topMargin, height]);
|
|
4278
|
-
|
|
4279
|
-
ctx.restore();
|
|
4280
|
-
|
|
4281
|
-
offsetI++;
|
|
4321
|
+
this.drawTrackWithCurves(ctx, trackHeight, track);
|
|
4322
|
+
this.tracksDrawn.push([track, offset + treeOffset, trackHeight]);
|
|
4323
|
+
|
|
4324
|
+
offset += trackHeight;
|
|
4325
|
+
ctx.translate(0, trackHeight);
|
|
4282
4326
|
}
|
|
4283
|
-
offset += offsetI * height + height;
|
|
4284
4327
|
}
|
|
4285
4328
|
ctx.restore();
|
|
4286
4329
|
|
|
@@ -4298,7 +4341,13 @@ class CurvesTimeline extends Timeline {
|
|
|
4298
4341
|
}
|
|
4299
4342
|
|
|
4300
4343
|
ctx.globalAlpha = this.opacity;
|
|
4301
|
-
|
|
4344
|
+
|
|
4345
|
+
const defaultPointSize = 5;
|
|
4346
|
+
const hoverPointSize = 7;
|
|
4347
|
+
const valueRange = this.range; //[min, max]
|
|
4348
|
+
const displayRange = trackHeight - defaultPointSize * 2;
|
|
4349
|
+
const startTime = this.visualTimeRange[0];
|
|
4350
|
+
const endTime = this.visualTimeRange[1];
|
|
4302
4351
|
//draw lines
|
|
4303
4352
|
ctx.strokeStyle = "white";
|
|
4304
4353
|
ctx.beginPath();
|
|
@@ -4307,9 +4356,9 @@ class CurvesTimeline extends Timeline {
|
|
|
4307
4356
|
let time = keyframes[j];
|
|
4308
4357
|
let keyframePosX = this.timeToX( time );
|
|
4309
4358
|
let value = values[j];
|
|
4310
|
-
value = ((value -
|
|
4359
|
+
value = ((value - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
4311
4360
|
|
|
4312
|
-
if( time <
|
|
4361
|
+
if( time < startTime ){
|
|
4313
4362
|
ctx.moveTo( keyframePosX, value );
|
|
4314
4363
|
continue;
|
|
4315
4364
|
}
|
|
@@ -4317,40 +4366,40 @@ class CurvesTimeline extends Timeline {
|
|
|
4317
4366
|
//convert to timeline track range
|
|
4318
4367
|
ctx.lineTo( keyframePosX, value );
|
|
4319
4368
|
|
|
4320
|
-
if ( time >
|
|
4369
|
+
if ( time > endTime ){
|
|
4321
4370
|
break; //end loop, but print line
|
|
4322
4371
|
}
|
|
4323
4372
|
}
|
|
4324
4373
|
ctx.stroke();
|
|
4325
4374
|
|
|
4326
4375
|
//draw points
|
|
4327
|
-
ctx.fillStyle = Timeline.
|
|
4376
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR;
|
|
4328
4377
|
for(let j = 0; j < keyframes.length; ++j)
|
|
4329
4378
|
{
|
|
4330
4379
|
let time = keyframes[j];
|
|
4331
|
-
if( time <
|
|
4380
|
+
if( time < startTime || time > endTime )
|
|
4332
4381
|
continue;
|
|
4333
4382
|
|
|
4334
|
-
let size =
|
|
4383
|
+
let size = defaultPointSize;
|
|
4335
4384
|
let keyframePosX = this.timeToX( time );
|
|
4336
4385
|
|
|
4337
4386
|
if(!this.active || !track.active)
|
|
4338
|
-
ctx.fillStyle = Timeline.
|
|
4387
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_INACTIVE;
|
|
4339
4388
|
else if(track.locked)
|
|
4340
|
-
ctx.fillStyle = Timeline.
|
|
4389
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_LOCK;
|
|
4341
4390
|
else if(track.hovered[j]) {
|
|
4342
|
-
size =
|
|
4343
|
-
ctx.fillStyle = Timeline.
|
|
4391
|
+
size = hoverPointSize;
|
|
4392
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_HOVERED;
|
|
4344
4393
|
}
|
|
4345
4394
|
else if(track.selected[j])
|
|
4346
|
-
ctx.fillStyle = Timeline.
|
|
4395
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_SELECTED;
|
|
4347
4396
|
else if(track.edited[j])
|
|
4348
|
-
ctx.fillStyle = Timeline.
|
|
4397
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR_EDITED;
|
|
4349
4398
|
else
|
|
4350
|
-
ctx.fillStyle = Timeline.
|
|
4399
|
+
ctx.fillStyle = Timeline.KEYFRAME_COLOR
|
|
4351
4400
|
|
|
4352
4401
|
let value = values[j];
|
|
4353
|
-
value = ((value - this.range[0]) / (this.range[1] - this.range[0])) *
|
|
4402
|
+
value = ((value - this.range[0]) / (this.range[1] - this.range[0])) *(-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
4354
4403
|
|
|
4355
4404
|
ctx.beginPath();
|
|
4356
4405
|
ctx.arc( keyframePosX, value, size, 0, Math.PI * 2);
|
|
@@ -5032,7 +5081,13 @@ class CurvesTimeline extends Timeline {
|
|
|
5032
5081
|
|
|
5033
5082
|
this.unSelectAllKeyFrames();
|
|
5034
5083
|
this.unHoverAll();
|
|
5035
|
-
|
|
5084
|
+
|
|
5085
|
+
this.selectedItems = [];
|
|
5086
|
+
for( let i = 0; i < itemsName.length; ++i ){
|
|
5087
|
+
if ( this.animationClip.tracksPerItem[itemsName[i]] ){
|
|
5088
|
+
this.selectedItems.push(itemsName[i]);
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5036
5091
|
this.updateLeftPanel();
|
|
5037
5092
|
}
|
|
5038
5093
|
|
|
@@ -5250,7 +5305,7 @@ class CurvesTimeline extends Timeline {
|
|
|
5250
5305
|
this.unSelectAllKeyFrames();
|
|
5251
5306
|
}
|
|
5252
5307
|
|
|
5253
|
-
keyFrameIndex = keyFrameIndex ?? this.getCurrentKeyFrame( track, this.xToTime( localX ), this.
|
|
5308
|
+
keyFrameIndex = keyFrameIndex ?? this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * 5 );
|
|
5254
5309
|
if (keyFrameIndex < 0)
|
|
5255
5310
|
return;
|
|
5256
5311
|
|