lexgui 0.7.9 → 0.7.11
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 +130 -103
- package/build/extensions/videoeditor.js +262 -172
- package/build/lexgui.css +33 -5
- package/build/lexgui.js +421 -346
- package/build/lexgui.min.css +2 -2
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +421 -346
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +48 -1
- package/examples/area-tabs.html +1 -1
- package/examples/asset-view.html +27 -1
- package/examples/editor.html +2 -2
- 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
|
@@ -1299,6 +1299,10 @@ class CodeEditor
|
|
|
1299
1299
|
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1300
1300
|
}, 50 );
|
|
1301
1301
|
|
|
1302
|
+
if( options.callback )
|
|
1303
|
+
{
|
|
1304
|
+
options.callback.call( this, this );
|
|
1305
|
+
}
|
|
1302
1306
|
});
|
|
1303
1307
|
|
|
1304
1308
|
window.editor = this;
|
|
@@ -6,6 +6,9 @@ if(!LX) {
|
|
|
6
6
|
|
|
7
7
|
LX.extensions.push( 'Timeline' );
|
|
8
8
|
|
|
9
|
+
LX.registerIcon("TimelineLock", '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path fill="none" d="M7 11V7a4 4 0 0 1 9 0v4 M5,11h13 a2 2 0 0 1 2 2 v7 a2 2 0 0 1 -2 2 h-13 a2 2 0 0 1 -2 -2 v-7 a2 2 0 0 1 2 -2 M12 16 v2"/></svg>' );
|
|
10
|
+
LX.registerIcon("TimelineLockOpen", '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path fill="none" d="M14 11V7a4 4 0 0 1 9 0v2 M3,11h13 a2 2 0 0 1 2 2 v7 a2 2 0 0 1 -2 2 h-13 a2 2 0 0 1 -2 -2 v-7 a2 2 0 0 1 2 -2 M8 17 h3"/></svg>' );
|
|
11
|
+
|
|
9
12
|
/**
|
|
10
13
|
* @class Timeline
|
|
11
14
|
* @description Agnostic timeline, do not impose any timeline content. Renders to a canvas
|
|
@@ -64,7 +67,8 @@ class Timeline {
|
|
|
64
67
|
this.pixelsPerSecond = 300;
|
|
65
68
|
this.secondsPerPixel = 1 / this.pixelsPerSecond;
|
|
66
69
|
this.animationClip = this.instantiateAnimationClip();
|
|
67
|
-
|
|
70
|
+
|
|
71
|
+
this.trackHeight = 32;
|
|
68
72
|
this.timeSeparators = [0.01, 0.1, 0.5, 1, 5];
|
|
69
73
|
|
|
70
74
|
this.boxSelection = false;
|
|
@@ -301,7 +305,7 @@ class Timeline {
|
|
|
301
305
|
|
|
302
306
|
const panel = this.leftPanel;
|
|
303
307
|
|
|
304
|
-
panel.sameLine(
|
|
308
|
+
panel.sameLine();
|
|
305
309
|
let titleComponent = panel.addTitle( "Tracks", { style: { background: "none"}, className: "fg-secondary text-lg px-4"} );
|
|
306
310
|
let title = titleComponent.root;
|
|
307
311
|
|
|
@@ -340,10 +344,20 @@ class Timeline {
|
|
|
340
344
|
this.setTrackState( e.node.trackData.trackIdx, e.value );
|
|
341
345
|
}
|
|
342
346
|
break;
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if ( this.onTrackTreeEvent ){
|
|
350
|
+
this.onTrackTreeEvent(e);
|
|
345
351
|
}
|
|
346
352
|
}});
|
|
353
|
+
|
|
354
|
+
const that = this;
|
|
355
|
+
this.trackTreesComponent.innerTree._refresh = this.trackTreesComponent.innerTree.refresh;
|
|
356
|
+
this.trackTreesComponent.innerTree.refresh = function( newData, selectedId ){
|
|
357
|
+
this._refresh( newData, selectedId );
|
|
358
|
+
that.setTrackHeight( that.trackHeight );
|
|
359
|
+
}
|
|
360
|
+
|
|
347
361
|
// setting a name in the addTree function adds an undesired node
|
|
348
362
|
this.trackTreesComponent.name = "tracksTrees";
|
|
349
363
|
p.components[this.trackTreesComponent.name] = this.trackTreesComponent;
|
|
@@ -362,12 +376,33 @@ class Timeline {
|
|
|
362
376
|
});
|
|
363
377
|
|
|
364
378
|
this.trackTreesPanel.root.scrollTop = this.currentScrollInPixels;
|
|
379
|
+
this.setTrackHeight( this.trackHeight );
|
|
365
380
|
|
|
366
381
|
if( this.leftPanel.parent.root.classList.contains("hidden") || !this.root.parentElement ){
|
|
367
382
|
return;
|
|
368
383
|
}
|
|
369
384
|
|
|
370
385
|
this.resizeCanvas();
|
|
386
|
+
|
|
387
|
+
this.setScroll( this.currentScroll ); // avoid scroll bugs
|
|
388
|
+
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
setTrackHeight( trackHeight ){
|
|
392
|
+
// ul list has a "gap" of 0.25rem. Compute pixel count of 0.25 rem
|
|
393
|
+
const gapSize = parseFloat(getComputedStyle(document.documentElement).fontSize) * 0.25;
|
|
394
|
+
|
|
395
|
+
this.trackHeight = trackHeight = Math.max(gapSize, trackHeight);
|
|
396
|
+
|
|
397
|
+
if ( !this.trackTreesComponent ){
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
trackHeight -= gapSize;
|
|
402
|
+
const tracks = this.trackTreesComponent.root.querySelector("ul").children;
|
|
403
|
+
for( let i = 0; i < tracks.length; ++i ){
|
|
404
|
+
tracks[i].style.height = trackHeight + "px";
|
|
405
|
+
}
|
|
371
406
|
}
|
|
372
407
|
|
|
373
408
|
/**
|
|
@@ -583,11 +618,6 @@ class Timeline {
|
|
|
583
618
|
const scrollableHeight = this.trackTreesComponent.root.scrollHeight;
|
|
584
619
|
// tree has gaps of 0.25rem (4px) inbetween entries but not in the beginning nor ending. Move half gap upwards.
|
|
585
620
|
const treeOffset = this.lastTrackTreesComponentOffset = this.trackTreesComponent.innerTree.domEl.offsetTop - this.canvas.offsetTop -2;
|
|
586
|
-
|
|
587
|
-
if ( this.trackTreesPanel.root.scrollHeight > 0 ){
|
|
588
|
-
const ul = this.trackTreesComponent.innerTree.domEl.children[0];
|
|
589
|
-
this.trackHeight = ul.children.length < 1 ? 25 : ((ul.offsetHeight+4) / ul.children.length);
|
|
590
|
-
}
|
|
591
621
|
|
|
592
622
|
//zoom
|
|
593
623
|
let startTime = this.visualOriginTime; //seconds
|
|
@@ -1306,25 +1336,12 @@ class Timeline {
|
|
|
1306
1336
|
const track = this.selectedItems[ i ];
|
|
1307
1337
|
treeTracks.push({'trackData': track, 'id': track.id, 'skipVisibility': this.skipVisibility, visible: track.active, 'children':[], actions : this.skipLock ? null : [{
|
|
1308
1338
|
'name':'Lock edition',
|
|
1309
|
-
'icon': (track.locked ? '
|
|
1310
|
-
'swap': (track.locked ? '
|
|
1311
|
-
'callback': (node,
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
if(value) {
|
|
1315
|
-
el.title = 'Lock edition';
|
|
1316
|
-
el.classList.remove('Lock');
|
|
1317
|
-
el.classList.add('LockOpen');
|
|
1318
|
-
}
|
|
1319
|
-
else {
|
|
1320
|
-
el.title = 'Unlock edition';
|
|
1321
|
-
el.classList.remove('LockOpen');
|
|
1322
|
-
el.classList.add('Lock');
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
node.trackData.locked = !value;
|
|
1339
|
+
'icon': (track.locked ? 'TimelineLock' : 'TimelineLockOpen'),
|
|
1340
|
+
'swap': (track.locked ? 'TimelineLockOpen' : 'TimelineLock'),
|
|
1341
|
+
'callback': (node, swapValue, event) => {
|
|
1342
|
+
node.trackData.locked = !node.trackData.locked;
|
|
1326
1343
|
if(this.onLockTrack){
|
|
1327
|
-
this.onLockTrack(
|
|
1344
|
+
this.onLockTrack(node.trackData, node);
|
|
1328
1345
|
}
|
|
1329
1346
|
}
|
|
1330
1347
|
}]});
|
|
@@ -1418,6 +1435,9 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1418
1435
|
this.defaultCurves = true; // whn a track with dim == 1 has no curves attribute, defaultCurves will be used instead. If true, track is rendered using curves
|
|
1419
1436
|
this.defaultCurvesRange = [0,1]; // whn a track with dim == 1 has no curves attribute, defaultCurves will be used instead. If true, track is rendered using curves
|
|
1420
1437
|
|
|
1438
|
+
this.keyframeSize = this.trackHeight * 0.5; // height of keyframe
|
|
1439
|
+
this.keyframeSizeHovered = this.trackHeight * 0.5 + 5;
|
|
1440
|
+
|
|
1421
1441
|
if(this.animationClip) {
|
|
1422
1442
|
this.setAnimationClip(this.animationClip);
|
|
1423
1443
|
}
|
|
@@ -1437,25 +1457,12 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1437
1457
|
const track = itemTracks[j];
|
|
1438
1458
|
nodes.push({'trackData': track, 'id': track.id, 'skipVisibility': this.skipVisibility, visible: track.active, 'children':[], actions : this.skipLock ? null : [{
|
|
1439
1459
|
'name':'Lock edition',
|
|
1440
|
-
'icon': (track.locked ? '
|
|
1441
|
-
'swap': (track.locked ? '
|
|
1442
|
-
'callback': (node,
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
if(value) {
|
|
1446
|
-
el.title = 'Lock edition';
|
|
1447
|
-
el.classList.remove('Lock');
|
|
1448
|
-
el.classList.add('LockOpen');
|
|
1449
|
-
}
|
|
1450
|
-
else {
|
|
1451
|
-
el.title = 'Unlock edition';
|
|
1452
|
-
el.classList.remove('LockOpen');
|
|
1453
|
-
el.classList.add('Lock');
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
node.trackData.locked = !value;
|
|
1460
|
+
'icon': (track.locked ? 'TimelineLock' : 'TimelineLockOpen'),
|
|
1461
|
+
'swap': (track.locked ? 'TimelineLockOpen' : 'TimelineLock'),
|
|
1462
|
+
'callback': (node, swapValue, event) => {
|
|
1463
|
+
node.trackData.locked = !node.trackData.locked;
|
|
1457
1464
|
if(this.onLockTrack){
|
|
1458
|
-
this.onLockTrack(
|
|
1465
|
+
this.onLockTrack(node.trackData, node);
|
|
1459
1466
|
}
|
|
1460
1467
|
}
|
|
1461
1468
|
}]});
|
|
@@ -1646,7 +1653,6 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1646
1653
|
}
|
|
1647
1654
|
}
|
|
1648
1655
|
}
|
|
1649
|
-
|
|
1650
1656
|
|
|
1651
1657
|
this.updateLeftPanel();
|
|
1652
1658
|
|
|
@@ -1751,6 +1757,16 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1751
1757
|
return null;
|
|
1752
1758
|
}
|
|
1753
1759
|
|
|
1760
|
+
/**
|
|
1761
|
+
*
|
|
1762
|
+
* @param {number} size pixels, height of keyframe
|
|
1763
|
+
* @param {number} sizeHovered optional, size in pixels when hovered
|
|
1764
|
+
*/
|
|
1765
|
+
setKeyframeSize( size, sizeHovered = null ){
|
|
1766
|
+
this.keyframeSizeHovered = sizeHovered ?? size;
|
|
1767
|
+
this.keyframeSize = size;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1754
1770
|
onMouseUp( e, time ) {
|
|
1755
1771
|
|
|
1756
1772
|
let track = e.track;
|
|
@@ -1760,7 +1776,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1760
1776
|
if(e.shiftKey) {
|
|
1761
1777
|
// Manual multiple selection
|
|
1762
1778
|
if(!discard && track) {
|
|
1763
|
-
const
|
|
1779
|
+
const thresholdPixels = this.keyframeSize * 0.5; // radius of circle (curves) or rotated square (keyframes)
|
|
1780
|
+
const keyFrameIdx = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * thresholdPixels );
|
|
1764
1781
|
if ( keyFrameIdx > -1 ){
|
|
1765
1782
|
track.selected[keyFrameIdx] ?
|
|
1766
1783
|
this.deselectKeyFrame(track.trackIdx, keyFrameIdx) :
|
|
@@ -1794,7 +1811,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1794
1811
|
this.deselectAllKeyFrames();
|
|
1795
1812
|
}
|
|
1796
1813
|
if (track){
|
|
1797
|
-
const
|
|
1814
|
+
const thresholdPixels = this.keyframeSize * 0.5; // radius of circle (curves) or rotated square (keyframes)
|
|
1815
|
+
const keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * thresholdPixels );
|
|
1798
1816
|
if( keyFrameIndex > -1 ) {
|
|
1799
1817
|
this.processSelectionKeyFrame( track.trackIdx, keyFrameIndex, false ); // Settings this as multiple so time is not being set
|
|
1800
1818
|
}
|
|
@@ -1811,7 +1829,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1811
1829
|
let localY = e.localY;
|
|
1812
1830
|
let track = e.track;
|
|
1813
1831
|
|
|
1814
|
-
if(e.ctrlKey && this.lastKeyFramesSelected.length) { // move keyframes
|
|
1832
|
+
if( (e.ctrlKey || e.altKey) && this.lastKeyFramesSelected.length) { // move keyframes
|
|
1815
1833
|
this.movingKeys = true;
|
|
1816
1834
|
this.canvas.style.cursor = "grab";
|
|
1817
1835
|
this.canvas.classList.add('grabbing');
|
|
@@ -1819,31 +1837,28 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1819
1837
|
// Set pre-move state
|
|
1820
1838
|
this.moveKeyMinTime = Infinity;
|
|
1821
1839
|
const tracks = this.animationClip.tracks;
|
|
1822
|
-
|
|
1840
|
+
let lastTrackIdx = -1;
|
|
1841
|
+
for(let selectedKey of this.lastKeyFramesSelected) { // WARNING assumes lasKeyFramesSelected is sorted, so all keyframes of the same track are grouped
|
|
1823
1842
|
let [trackIdx, keyIndex, keyTime] = selectedKey;
|
|
1824
1843
|
const track = tracks[trackIdx];
|
|
1844
|
+
|
|
1845
|
+
selectedKey[2] = track.times[keyIndex]; // update original time just in case
|
|
1825
1846
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
let s = 0;
|
|
1830
|
-
for( s = 0; s < state.length; ++s){
|
|
1831
|
-
if ( state[s].trackIdx == track.trackIdx ){ break; }
|
|
1832
|
-
}
|
|
1833
|
-
if( s == state.length ){
|
|
1847
|
+
if ( lastTrackIdx != trackIdx ){
|
|
1848
|
+
// save track states only once
|
|
1849
|
+
if (this.moveKeyMinTime < Infinity){
|
|
1834
1850
|
this.saveState(track.trackIdx, true);
|
|
1851
|
+
}else{
|
|
1852
|
+
this.saveState(track.trackIdx, false);
|
|
1835
1853
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1854
|
+
this.moveKeyMinTime = Math.min( this.moveKeyMinTime, selectedKey[2] );
|
|
1855
|
+
lastTrackIdx = trackIdx;
|
|
1838
1856
|
}
|
|
1839
1857
|
|
|
1840
|
-
selectedKey[2] = track.times[keyIndex]; // update original time just in case
|
|
1841
|
-
this.moveKeyMinTime = Math.min( this.moveKeyMinTime, selectedKey[2] );
|
|
1842
1858
|
}
|
|
1843
1859
|
|
|
1844
1860
|
this.timeBeforeMove = this.xToTime( localX );
|
|
1845
|
-
|
|
1846
|
-
else if( e.altKey ){ // if only altkey, do not grab timeline
|
|
1861
|
+
|
|
1847
1862
|
this.grabbing = false;
|
|
1848
1863
|
this.grabbingTimeBar = false;
|
|
1849
1864
|
}
|
|
@@ -1918,46 +1933,45 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1918
1933
|
}
|
|
1919
1934
|
}
|
|
1920
1935
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
+
|
|
1937
|
+
// Track.dim == 1: move keyframes vertically (change values instead of time)
|
|
1938
|
+
// RELIES ON SORTED ARRAY OF lastKeyFramesSelected
|
|
1939
|
+
if ( e.altKey && e.buttons & 0x01 ){
|
|
1940
|
+
const tracks = this.animationClip.tracks;
|
|
1941
|
+
let lastTrackChanged = -1;
|
|
1942
|
+
for( let i = 0; i < this.lastKeyFramesSelected.length; ++i ){
|
|
1943
|
+
const [trackIdx, keyIndex, originalKeyTime] = this.lastKeyFramesSelected[i];
|
|
1944
|
+
track = tracks[trackIdx];
|
|
1945
|
+
if(track.locked || track.dim != 1 || !track.curves){
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
let value = track.values[keyIndex];
|
|
1950
|
+
let delta = e.deltay * this.keyValuePerPixel * (track.curvesRange[1]-track.curvesRange[0]);
|
|
1951
|
+
track.values[keyIndex] = Math.max(track.curvesRange[0], Math.min(track.curvesRange[1], value - delta)); // invert delta because of screen y
|
|
1952
|
+
track.edited[keyIndex] = true;
|
|
1953
|
+
|
|
1954
|
+
if ( this.onUpdateTrack && track.trackIdx != lastTrackChanged && lastTrackChanged > -1){ // do it only once all keyframes of the same track have been modified
|
|
1955
|
+
this.onUpdateTrack( [track.trackIdx] );
|
|
1956
|
+
}
|
|
1957
|
+
lastTrackChanged = track.trackIdx;
|
|
1936
1958
|
}
|
|
1937
|
-
|
|
1938
|
-
let value = track.values[keyIndex];
|
|
1939
|
-
let delta = e.deltay * this.keyValuePerPixel * (track.curvesRange[1]-track.curvesRange[0]);
|
|
1940
|
-
track.values[keyIndex] = Math.max(track.curvesRange[0], Math.min(track.curvesRange[1], value - delta)); // invert delta because of screen y
|
|
1941
|
-
track.edited[keyIndex] = true;
|
|
1942
|
-
|
|
1943
|
-
if ( this.onUpdateTrack && track.trackIdx != lastTrackChanged && lastTrackChanged > -1){ // do it only once all keyframes of the same track have been modified
|
|
1959
|
+
if( this.onUpdateTrack && lastTrackChanged > -1 ){ // do the last update, once the last track has been processed
|
|
1944
1960
|
this.onUpdateTrack( [track.trackIdx] );
|
|
1945
1961
|
}
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1948
|
-
if( this.onUpdateTrack && lastTrackChanged > -1 ){ // do the last update, once the last track has been processed
|
|
1949
|
-
this.onUpdateTrack( [track.trackIdx] );
|
|
1962
|
+
return;
|
|
1950
1963
|
}
|
|
1951
|
-
return;
|
|
1952
1964
|
}
|
|
1953
1965
|
|
|
1966
|
+
|
|
1954
1967
|
if( this.grabbing && e.button != 2) {
|
|
1955
1968
|
|
|
1956
1969
|
}
|
|
1957
1970
|
else if(track) {
|
|
1958
1971
|
|
|
1959
1972
|
this.unHoverAll();
|
|
1960
|
-
|
|
1973
|
+
const thresholdPixels = this.keyframeSize * 0.5; // radius of circle (curves) or rotated square (keyframes)
|
|
1974
|
+
let keyFrameIndex = this.getCurrentKeyFrame( track, this.xToTime( localX ), this.secondsPerPixel * thresholdPixels );
|
|
1961
1975
|
if(keyFrameIndex > -1 ) {
|
|
1962
1976
|
if(track && track.locked)
|
|
1963
1977
|
return;
|
|
@@ -2026,7 +2040,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2026
2040
|
if ( !e.track ){ return; }
|
|
2027
2041
|
const values = new Float32Array( e.track.dim );
|
|
2028
2042
|
values.fill(0);
|
|
2029
|
-
this.addKeyFrames( e.track, values, [this.currentTime] );
|
|
2043
|
+
this.addKeyFrames( e.track.trackIdx, values, [this.currentTime] );
|
|
2030
2044
|
}
|
|
2031
2045
|
}
|
|
2032
2046
|
);
|
|
@@ -2075,9 +2089,21 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2075
2089
|
const visibleElements = this.getVisibleItems();
|
|
2076
2090
|
|
|
2077
2091
|
let offset = scrollY;
|
|
2092
|
+
|
|
2093
|
+
// compute track from which to start rendering (avoid rendering unseen tracks)
|
|
2094
|
+
let startElIdx = 0;
|
|
2095
|
+
if ( offset < -this.lastTrackTreesComponentOffset ){ // offset 0 = (0 of canvas) + track-Tree-Offset. This renders tracks under the time zone
|
|
2096
|
+
startElIdx = Math.floor( -(offset + this.lastTrackTreesComponentOffset) / this.trackHeight ); // how many tracks to skip
|
|
2097
|
+
offset += startElIdx * this.trackHeight;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2078
2100
|
ctx.translate(0, offset);
|
|
2079
2101
|
|
|
2080
|
-
|
|
2102
|
+
// compute track to end rendering (avoid rendering unseen tracks)
|
|
2103
|
+
let endElIdx = startElIdx + Math.ceil( ( ctx.canvas.height - this.lastTrackTreesComponentOffset - offset ) / this.trackHeight );
|
|
2104
|
+
endElIdx = endElIdx > visibleElements.length ? visibleElements.length : endElIdx;
|
|
2105
|
+
|
|
2106
|
+
for(let t = startElIdx; t < endElIdx; t++) {
|
|
2081
2107
|
const track = visibleElements[t].treeData.trackData;
|
|
2082
2108
|
|
|
2083
2109
|
if (track){
|
|
@@ -2088,7 +2114,6 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2088
2114
|
}
|
|
2089
2115
|
}
|
|
2090
2116
|
|
|
2091
|
-
offset += trackHeight;
|
|
2092
2117
|
ctx.translate(0, trackHeight);
|
|
2093
2118
|
}
|
|
2094
2119
|
|
|
@@ -2115,6 +2140,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2115
2140
|
const keyframes = track.times;
|
|
2116
2141
|
const startTime = this.visualTimeRange[0];
|
|
2117
2142
|
const endTime = this.visualTimeRange[1] + 0.0000001;
|
|
2143
|
+
const defaultPointSize = this.keyframeSize / Math.SQRT2; // pythagoras with equal sides h2 = c2 + c2 = 2 * c2
|
|
2144
|
+
const hoverPointSize = this.keyframeSizeHovered / Math.SQRT2;
|
|
2118
2145
|
|
|
2119
2146
|
for(let j = 0; j < keyframes.length; ++j)
|
|
2120
2147
|
{
|
|
@@ -2124,7 +2151,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2124
2151
|
}
|
|
2125
2152
|
|
|
2126
2153
|
let keyframePosX = this.timeToX( time );
|
|
2127
|
-
let size =
|
|
2154
|
+
let size = defaultPointSize;
|
|
2128
2155
|
|
|
2129
2156
|
if(!this.active || track.active == false) {
|
|
2130
2157
|
ctx.fillStyle = Timeline.KEYFRAME_COLOR_INACTIVE;
|
|
@@ -2133,7 +2160,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2133
2160
|
ctx.fillStyle = Timeline.KEYFRAME_COLOR_LOCK;
|
|
2134
2161
|
}
|
|
2135
2162
|
else if(track.hovered[j]) {
|
|
2136
|
-
size =
|
|
2163
|
+
size = hoverPointSize;
|
|
2137
2164
|
ctx.fillStyle = Timeline.KEYFRAME_COLOR_HOVERED;
|
|
2138
2165
|
}
|
|
2139
2166
|
else if(track.selected[j]) {
|
|
@@ -2166,8 +2193,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2166
2193
|
ctx.globalAlpha = 1;
|
|
2167
2194
|
const keyframes = track.times;
|
|
2168
2195
|
const values = track.values;
|
|
2169
|
-
const defaultPointSize = 5;
|
|
2170
|
-
const hoverPointSize =
|
|
2196
|
+
const defaultPointSize = this.keyframeSize * 0.5; // radius
|
|
2197
|
+
const hoverPointSize = this.keyframeSizeHovered * 0.5; // radius
|
|
2171
2198
|
const valueRange = track.curvesRange; //[min, max]
|
|
2172
2199
|
const displayRange = trackHeight - defaultPointSize * 2;
|
|
2173
2200
|
const startTime = this.visualTimeRange[0];
|
|
@@ -2179,7 +2206,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2179
2206
|
if ( keyframes.length > 1){
|
|
2180
2207
|
let startPosX = this.timeToX( keyframes[0] );
|
|
2181
2208
|
let startValue = values[0];
|
|
2182
|
-
startValue = ((startValue - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2209
|
+
startValue = LX.clamp((startValue - valueRange[0]) / (valueRange[1] - valueRange[0]), 0,1) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2183
2210
|
ctx.moveTo( startPosX, startValue );
|
|
2184
2211
|
|
|
2185
2212
|
for(let j = 1; j < keyframes.length; ++j){
|
|
@@ -2187,7 +2214,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2187
2214
|
let time = keyframes[j];
|
|
2188
2215
|
let keyframePosX = this.timeToX( time );
|
|
2189
2216
|
let value = values[j];
|
|
2190
|
-
value = ((value - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2217
|
+
value = LX.clamp((value - valueRange[0]) / (valueRange[1] - valueRange[0]), 0,1) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2191
2218
|
|
|
2192
2219
|
if( time < startTime ){
|
|
2193
2220
|
ctx.moveTo( keyframePosX, value );
|
|
@@ -2199,7 +2226,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2199
2226
|
let dt = keyframePosX - lastKeyframePosX;
|
|
2200
2227
|
if ( dt > 0 ){
|
|
2201
2228
|
let lastValue = values[j-1];
|
|
2202
|
-
lastValue = ((lastValue - valueRange[0]) / (valueRange[1] - valueRange[0])) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2229
|
+
lastValue = LX.clamp((lastValue - valueRange[0]) / (valueRange[1] - valueRange[0]), 0,1) * (-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2203
2230
|
let f = (this.timeToX( endTime ) - lastKeyframePosX) / dt;
|
|
2204
2231
|
ctx.lineTo( lastKeyframePosX + dt * f, lastValue * (1-f) + value * f );
|
|
2205
2232
|
}
|
|
@@ -2239,7 +2266,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2239
2266
|
ctx.fillStyle = Timeline.KEYFRAME_COLOR
|
|
2240
2267
|
|
|
2241
2268
|
let value = values[j];
|
|
2242
|
-
value = ((value - valueRange[0]) / (valueRange[1] - valueRange[0])) *(-displayRange) + (trackHeight - defaultPointSize); // normalize and offset
|
|
2269
|
+
value = LX.clamp((value - valueRange[0]) / (valueRange[1] - valueRange[0]), 0,1) *(-displayRange) + (trackHeight - defaultPointSize); // normalize, clamp and offset
|
|
2243
2270
|
|
|
2244
2271
|
ctx.beginPath();
|
|
2245
2272
|
ctx.arc( keyframePosX, value, size, 0, Math.PI * 2);
|