lexgui 0.1.31 → 0.1.32
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/timeline.js +768 -442
- package/build/components/videoeditor.js +539 -0
- package/build/lexgui.css +87 -3
- package/build/lexgui.js +272 -254
- package/build/lexgui.module.js +247 -229
- package/changelog.md +11 -0
- package/examples/index.html +2 -1
- package/examples/timeline.html +279 -0
- package/examples/video_editor.html +35 -0
- package/package.json +1 -1
package/changelog.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# lexgui.js changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.32
|
|
4
|
+
|
|
5
|
+
Timeline:
|
|
6
|
+
- Allow addition of widgets in top bar.
|
|
7
|
+
- General improvements.
|
|
8
|
+
- Docs started.
|
|
9
|
+
|
|
10
|
+
New VideoEditor component (still wip).
|
|
11
|
+
Added skipCallback for `Widget.set()`.
|
|
12
|
+
Minor bug fixes.
|
|
13
|
+
|
|
3
14
|
## 0.1.31
|
|
4
15
|
|
|
5
16
|
GraphEditor:
|
package/examples/index.html
CHANGED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
|
5
|
+
<title>LexGUI Timelines Demo</title>
|
|
6
|
+
<link rel="stylesheet" href="../build/lexgui.css">
|
|
7
|
+
<link rel="icon" href="../images/icon.png">
|
|
8
|
+
<script type="importmap">
|
|
9
|
+
{
|
|
10
|
+
"imports": {
|
|
11
|
+
"lexgui": "../build/lexgui.module.js",
|
|
12
|
+
"lexgui/components/": "../build/components/"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
</head>
|
|
17
|
+
<body></body>
|
|
18
|
+
<script type="module">
|
|
19
|
+
|
|
20
|
+
import { LX } from 'lexgui';
|
|
21
|
+
import { Timeline, KeyFramesTimeline, ClipsTimeline, CurvesTimeline } from 'lexgui/components/timeline.js';
|
|
22
|
+
|
|
23
|
+
// init library and get main area
|
|
24
|
+
let area = LX.init();
|
|
25
|
+
|
|
26
|
+
// split main area
|
|
27
|
+
var [leftArea, rightArea] = area.split({sizes:["75%","25%"]});
|
|
28
|
+
// split left area
|
|
29
|
+
var [upArea, bottomArea] = leftArea.split({ type: 'vertical', sizes:["50%", null], minimizable: true });
|
|
30
|
+
|
|
31
|
+
// add canvas to left upper part
|
|
32
|
+
var canvas = document.createElement('canvas');
|
|
33
|
+
canvas.id = "mycanvas";
|
|
34
|
+
canvas.width = leftArea.root.clientWidth;
|
|
35
|
+
canvas.height = leftArea.root.clientHeight;
|
|
36
|
+
canvas.style.width = "100%";
|
|
37
|
+
canvas.style.height = "100%";
|
|
38
|
+
|
|
39
|
+
upArea.attach(canvas);
|
|
40
|
+
|
|
41
|
+
// add on resize event to control canvas size
|
|
42
|
+
leftArea.onresize = function( bounding ) {
|
|
43
|
+
canvas.width = bounding.width;
|
|
44
|
+
canvas.height = bounding.height;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const bottomTabs = bottomArea.addTabs();
|
|
48
|
+
const types = { KEYFRAMES: 0, CLIPS: 1, CURVES: 2};
|
|
49
|
+
|
|
50
|
+
let panel = new LX.Panel();
|
|
51
|
+
panel = rightArea.addPanel(panel);
|
|
52
|
+
fillPanel( panel );
|
|
53
|
+
|
|
54
|
+
let activeTimeline = null
|
|
55
|
+
createKyeframeTimeline( bottomTabs );
|
|
56
|
+
createClipsTimeline( bottomTabs );
|
|
57
|
+
createCurvesTimeline( bottomTabs );
|
|
58
|
+
|
|
59
|
+
let mode = types.KEYFRAMES;
|
|
60
|
+
|
|
61
|
+
bottomArea.onresize = (size) => {
|
|
62
|
+
activeTimeline.resize(size);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function loop(dt) {
|
|
66
|
+
|
|
67
|
+
if (!lastTime) {
|
|
68
|
+
lastTime = dt;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let elapsed = dt - lastTime;
|
|
72
|
+
lastTime = dt;
|
|
73
|
+
|
|
74
|
+
if(activeTimeline) {
|
|
75
|
+
activeTimeline.draw();
|
|
76
|
+
if(activeTimeline.playing) {
|
|
77
|
+
|
|
78
|
+
activeTimeline.currentTime += elapsed* 0.001;
|
|
79
|
+
activeTimeline.onSetTime(activeTimeline.currentTime);
|
|
80
|
+
if(activeTimeline.currentTime >= activeTimeline.animationClip.duration) {
|
|
81
|
+
activeTimeline.currentTime = 0;
|
|
82
|
+
if(!activeTimeline.loop) {
|
|
83
|
+
activeTimeline.changeState();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if(mode == types.KEYFRAMES) {
|
|
91
|
+
let ctx = canvas.getContext("2d");
|
|
92
|
+
ctx.fillStyle = LX.getThemeColor("global-selected-light");
|
|
93
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
94
|
+
ctx.font = panel.getValue('Font Size') + "px Monospace";
|
|
95
|
+
ctx.fillStyle = panel.getValue('Font Color');
|
|
96
|
+
|
|
97
|
+
// Get values from panel widgets (e.g. position value)
|
|
98
|
+
const text = panel.getValue('Text');
|
|
99
|
+
const pos2D = panel.getValue('2D Position');
|
|
100
|
+
ctx.fillText(text, pos2D[0], pos2D[1]);
|
|
101
|
+
}
|
|
102
|
+
requestAnimationFrame(loop);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let lastTime = 0;
|
|
106
|
+
requestAnimationFrame(loop);
|
|
107
|
+
|
|
108
|
+
// **** **** **** **** **** **** **** **** **** **** **** ****
|
|
109
|
+
|
|
110
|
+
function fillPanel( panel ) {
|
|
111
|
+
panel.clear();
|
|
112
|
+
panel.branch("Canvas", {icon: "fa-solid fa-palette", filter: true});
|
|
113
|
+
panel.addColor("Background", LX.getThemeColor("global-selected-light"));
|
|
114
|
+
panel.addText("Track", "", null)
|
|
115
|
+
panel.addText("Text", "Lexgui.js @evallsg", null, {placeholder: "e.g. ColorPicker", icon: "fa fa-font"});
|
|
116
|
+
panel.addColor("Font Color", [0.1, 0.1, 0.8], (value, event) => {
|
|
117
|
+
console.log("Font Color: ", value);
|
|
118
|
+
});
|
|
119
|
+
panel.addNumber("Font Size", 36, (value, event) => {
|
|
120
|
+
console.log(value);
|
|
121
|
+
}, { min: 1, max: 48, step: 1});
|
|
122
|
+
panel.addVector2("2D Position", [250, 350], (value, event) => {
|
|
123
|
+
console.log(value);
|
|
124
|
+
}, { min: 0, max: 1024 });
|
|
125
|
+
panel.branch_open = true;
|
|
126
|
+
panel.merge();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
function fillClipPanel( panel, clip ) {
|
|
131
|
+
|
|
132
|
+
panel.clear();
|
|
133
|
+
|
|
134
|
+
if(!clip) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
panel.branch(clip.name || "Clip", {icon: "fa-solid fa-clip", filter: true});
|
|
139
|
+
panel.addNumber("Start", clip.start, (v) => {
|
|
140
|
+
const diff = v - clip.start;
|
|
141
|
+
clip.start = v;
|
|
142
|
+
clip.fadein += diff;
|
|
143
|
+
clip.fadeout += diff;
|
|
144
|
+
LX.emit("@on_start_change_fadein", clip.fadein);
|
|
145
|
+
LX.emit("@on_start_change_fadeout", clip.fadeout);
|
|
146
|
+
}, {step: 0.01});
|
|
147
|
+
if(clip.fadein != undefined) {
|
|
148
|
+
panel.addNumber("Fade-In", clip.fadein, (v) => {
|
|
149
|
+
clip.fadein = v;
|
|
150
|
+
}, {min: clip.start, max: clip.fadeout, step: 0.01, signal: "@on_start_change_fadein"});
|
|
151
|
+
}
|
|
152
|
+
if(clip.fadeout != undefined) {
|
|
153
|
+
panel.addNumber("Fade-Out", clip.fadeout, (v) => {
|
|
154
|
+
clip.fadeout = v;
|
|
155
|
+
}, {min: clip.fadein, max: clip.start + clip.duration, step: 0.01, signal: "@on_start_change_fadeout"});
|
|
156
|
+
}
|
|
157
|
+
panel.addNumber("Duration", clip.duration, (v) => {
|
|
158
|
+
clip.duration = v;
|
|
159
|
+
}, {step: 0.01});
|
|
160
|
+
|
|
161
|
+
panel.merge();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function createKyeframeTimeline( bottomTabs ) {
|
|
165
|
+
|
|
166
|
+
let keyframesPanel = new LX.Panel();
|
|
167
|
+
bottomTabs.add( "Keyframes", keyframesPanel, {onSelect: () => {
|
|
168
|
+
activeTimeline = kfTimeline;
|
|
169
|
+
kfTimeline.show();
|
|
170
|
+
fillPanel(panel);
|
|
171
|
+
mode = types.KEYFRAMES;
|
|
172
|
+
}} );
|
|
173
|
+
keyframesPanel.onresize = () => {
|
|
174
|
+
kfTimeline.resize();
|
|
175
|
+
}
|
|
176
|
+
let kfTimeline = new LX.KeyFramesTimeline('', {
|
|
177
|
+
disableNewTracks: true,
|
|
178
|
+
onBeforeCreateTopBar: (panel) => {
|
|
179
|
+
panel.addDropdown("Animation", ["Anim 1", "Anim 2", "Anim 3"], "Anim 1", ()=> {})
|
|
180
|
+
},
|
|
181
|
+
onAfterCreateTopBar: (panel) => {
|
|
182
|
+
panel.addButton("", "autoKeyEnable", null, { icon: 'fa fa-wand-magic-sparkles', name: 'autoKeyEnabled', width: "40px" });
|
|
183
|
+
panel.addButton("", "unselectAll", (value, event) => { kfTimeline.unSelectAllKeyFrames();}, { icon: 'fa-regular fa-rectangle-xmark', name: 'unselectAll', callback: (value, event) => { kfTimeline.unSelectAllKeyFrames();}, width: "40px"});
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
keyframesPanel.attach(kfTimeline.root);
|
|
188
|
+
kfTimeline.setSelectedItems(["Item 1", "Item 2", "Item 3"]);
|
|
189
|
+
kfTimeline.setAnimationClip({tracks: [{name: "Item 1.position", values: [250, 350, 250, 250, 250, 350], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 1.scale", values: [0,1,0, 0.5], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 2", values: [0,1,0,1], times: [0.1, 0.2, 0.3, 0.8]}, {name: "Item 3.position", values: [0,1,0], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 3.scale", values: [0,1,0], times: [0, 0.1, 0.2, 0.3]}], duration: 1});
|
|
190
|
+
activeTimeline = kfTimeline;
|
|
191
|
+
kfTimeline.show();
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
kfTimeline.onSelectKeyFrame = (data, currentSelection, keyFrameIndex) => {
|
|
195
|
+
if(data.multipleSelection) {
|
|
196
|
+
rightArea.hide();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
rightArea.show();
|
|
200
|
+
let id = data.track.idx;
|
|
201
|
+
let values = kfTimeline.animationClip.tracks[id].values;
|
|
202
|
+
panel.setValue("Track", data.track.name)
|
|
203
|
+
panel.setValue("2D Position", values.slice(keyFrameIndex, keyFrameIndex+2))
|
|
204
|
+
//kfTimeline.animationClip.tracks[currentSelection[0][0]];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
kfTimeline.onSetTime = ( time ) => {
|
|
208
|
+
rightArea.show();
|
|
209
|
+
let keyframes = kfTimeline.getKeyFramesInRange(kfTimeline.animationClip.tracks[0], time , time, 0.15);
|
|
210
|
+
|
|
211
|
+
let values = kfTimeline.animationClip.tracks[0].values;
|
|
212
|
+
let valueX = values[keyframes[0] * 2];
|
|
213
|
+
let valueY = values[keyframes[0] * 2 + 1];
|
|
214
|
+
if(keyframes.length > 1) {
|
|
215
|
+
let t = Math.abs(kfTimeline.animationClip.tracks[0].times[keyframes[0] * 2] - time);
|
|
216
|
+
valueX = valueX * (1 - t) + values[keyframes[1] * 2] * t;
|
|
217
|
+
valueY = valueY * (1 - t) + values[keyframes[1] * 2 + 1] * t;
|
|
218
|
+
}
|
|
219
|
+
panel.setValue("2D Position", [valueX, valueY]);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function createClipsTimeline( bottomTabs ) {
|
|
224
|
+
|
|
225
|
+
let clipsPanel = new LX.Panel();
|
|
226
|
+
bottomTabs.add( "Clips", clipsPanel, {onSelect: () => {
|
|
227
|
+
activeTimeline = clipsTimeline;
|
|
228
|
+
clipsTimeline.show();
|
|
229
|
+
panel.clear();
|
|
230
|
+
mode = types.CLIPS;
|
|
231
|
+
}});
|
|
232
|
+
|
|
233
|
+
let clipsTimeline = new LX.ClipsTimeline("clips-timeline");
|
|
234
|
+
clipsPanel.attach(clipsTimeline.root);
|
|
235
|
+
|
|
236
|
+
let clip = {id:"Clip 1", start:0, duration:1, type:""};
|
|
237
|
+
clipsTimeline.addClip(clip);
|
|
238
|
+
clip = {id:"Clip 2", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
|
|
239
|
+
clipsTimeline.addClip(clip);
|
|
240
|
+
clip = {id:"Clip 3", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
|
|
241
|
+
clipsTimeline.addClip(clip);
|
|
242
|
+
clip = {id:"Clip 4", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
|
|
243
|
+
clipsTimeline.addClip(clip);
|
|
244
|
+
clip = {id:"Clip 5", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
|
|
245
|
+
clipsTimeline.addClip(clip);
|
|
246
|
+
|
|
247
|
+
// clipsTimeline.setAnimationClip({tracks: [{clips: [clip]}], duration: 2});
|
|
248
|
+
clipsTimeline.selectedItems = ["Clip 1"];
|
|
249
|
+
clipsTimeline.show();
|
|
250
|
+
|
|
251
|
+
clipsTimeline.onSelectClip = (clip) => {
|
|
252
|
+
rightArea.show();
|
|
253
|
+
fillClipPanel(panel, clip);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
clipsTimeline.draw(0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function createCurvesTimeline( bottomTabs ) {
|
|
260
|
+
|
|
261
|
+
let curvesPanel = new LX.Panel();
|
|
262
|
+
bottomTabs.add( "Curves", curvesPanel, {onSelect: () => {
|
|
263
|
+
activeTimeline = curvesTimeline;
|
|
264
|
+
curvesTimeline.show();
|
|
265
|
+
panel.clear();
|
|
266
|
+
mode = types.CLIPS;
|
|
267
|
+
|
|
268
|
+
}});
|
|
269
|
+
|
|
270
|
+
let curvesTimeline = new LX.CurvesTimeline("curves-timeline", { range: [-1,1]});
|
|
271
|
+
curvesPanel.attach(curvesTimeline.root);
|
|
272
|
+
curvesTimeline.setSelectedItems(["Item 1", "Item 2", "Item 3"]);
|
|
273
|
+
curvesTimeline.setAnimationClip({tracks: [{name: "Item 1.position", values: [0,1,0,-1], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 1.scale", values: [0,1,0, 0.5], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 2", values: [0,1,0,1], times: [0.1, 0.2, 0.3, 0.8]}, {name: "Item 3.position", values: [0,0,0,1], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 3.scale", values: [0,1,0], times: [0, 0.1, 0.2, 0.3]}], duration: 1});
|
|
274
|
+
curvesTimeline.show();
|
|
275
|
+
curvesTimeline.draw(0);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
</script>
|
|
279
|
+
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
|
5
|
+
<title>LexGUI Video Editor Demo</title>
|
|
6
|
+
<link rel="stylesheet" href="../build/lexgui.css">
|
|
7
|
+
<link rel="icon" href="../images/icon.png">
|
|
8
|
+
<script type="importmap">
|
|
9
|
+
{
|
|
10
|
+
"imports": {
|
|
11
|
+
"lexgui": "../build/lexgui.module.js",
|
|
12
|
+
"lexgui/components/": "../build/components/"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
</head>
|
|
17
|
+
<body></body>
|
|
18
|
+
<script type="module">
|
|
19
|
+
|
|
20
|
+
import { LX } from 'lexgui';
|
|
21
|
+
import { VideoEditor } from 'lexgui/components/videoeditor.js';
|
|
22
|
+
// init library and get main area
|
|
23
|
+
let area = LX.init();
|
|
24
|
+
|
|
25
|
+
// split main area
|
|
26
|
+
let [leftArea, rightArea] = area.split({sizes:["75%","25%"], minimizable: true});
|
|
27
|
+
area.extend();
|
|
28
|
+
|
|
29
|
+
let videoEditor = new LX.VideoEditor(leftArea, { src: "../data/video.mp4"})
|
|
30
|
+
|
|
31
|
+
let panel = new LX.Panel();
|
|
32
|
+
panel = rightArea.addPanel(panel);
|
|
33
|
+
|
|
34
|
+
</script>
|
|
35
|
+
</html>
|