diff --git a/index.html b/index.html index 0e13b13..9ec3afe 100644 --- a/index.html +++ b/index.html @@ -890,7 +890,7 @@ :title="tl('timeline.'+keyframe.channel)" @contextmenu.prevent="keyframe.showContextMenu($event)" > - stop + {{ keyframe.interpolation == 'catmullrom' ? 'lens' : 'stop' }} diff --git a/js/animations/animation.js b/js/animations/animation.js index 2a45583..53f0e30 100644 --- a/js/animations/animation.js +++ b/js/animations/animation.js @@ -1215,6 +1215,7 @@ const Animator = { ba.addKeyframe({ time: parseFloat(timestamp), channel, + interpolation: b[channel][timestamp].lerp_mode, data_points: getKeyframeDataPoints(b[channel][timestamp]), }); } @@ -1369,6 +1370,26 @@ Blockbench.addDragHandler('animation', { }) BARS.defineActions(function() { + new NumSlider('slider_animation_length', { + category: 'animation', + condition: () => Animator.open && Animation.selected, + getInterval(event) { + if (event && event.shiftKey) return 1; + return Timeline.getStep() + }, + get: function() { + return Animation.selected.length + }, + change: function(modify) { + Animation.selected.setLength(limitNumber(modify(Animation.selected.length), 0, 1e4)) + }, + onBefore: function() { + Undo.initEdit({animations: [Animation.selected]}); + }, + onAfter: function() { + Undo.finishEdit('Change Animation Length') + } + }) new Action('add_animation', { icon: 'fa-plus-circle', category: 'animation', diff --git a/js/animations/keyframe.js b/js/animations/keyframe.js index 47bcf93..f42083e 100644 --- a/js/animations/keyframe.js +++ b/js/animations/keyframe.js @@ -218,11 +218,19 @@ class Keyframe { compileBedrockKeyframe() { if (this.transform) { if (this.data_points.length == 1) { - return this.getArray() + if (this.interpolation == 'linear') { + return this.getArray(); + } else { + return { + post: this.getArray(), + lerp_mode: this.interpolation, + } + } } else { return { pre: this.getArray(0), post: this.getArray(1), + lerp_mode: this.interpolation != 'linear' ? this.interpolation : undefined, } } } else if (this.channel == 'timeline') { @@ -237,7 +245,7 @@ class Keyframe { let points = []; this.data_points.forEach(data_point => { if (data_point.effect) { - let script = kf.script || undefined; + let script = this.script || undefined; if (script && !script.match(/;$/)) script += ';'; points.push({ effect: data_point.effect, @@ -375,6 +383,8 @@ class Keyframe { },*/ 'change_keyframe_file', '_', + // todo: integrate + 'keyframe_interpolation', {name: 'menu.cube.color', icon: 'color_lens', children: [ {icon: 'bubble_chart', name: 'generic.unset', click: function(kf) {kf.forSelected(kf2 => {kf2.color = -1}, 'change color')}}, {icon: 'bubble_chart', color: markerColors[0].standard, name: 'cube.color.'+markerColors[0].name, click: function(kf) {kf.forSelected(function(kf2){kf2.color = 0}, 'change color')}}, @@ -391,6 +401,7 @@ class Keyframe { ]) new Property(Keyframe, 'number', 'time') new Property(Keyframe, 'number', 'color', {default: -1}) + new Property(Keyframe, 'string', 'interpolation', {default: 'linear'}) Keyframe.selected = []; // Misc Functions @@ -410,6 +421,7 @@ function updateKeyframeSelection() { }) if (Timeline.selected.length) { BarItems.slider_keyframe_time.update() + BarItems.keyframe_interpolation.set(Timeline.selected[0].interpolation) } BARS.updateConditions() Blockbench.dispatchEvent('update_keyframe_selection'); @@ -564,26 +576,6 @@ BARS.defineActions(function() { } }) - new NumSlider('slider_animation_length', { - category: 'animation', - condition: () => Animator.open && Animation.selected, - getInterval(event) { - if (event && event.shiftKey) return 1; - return Timeline.getStep() - }, - get: function() { - return Animation.selected.length - }, - change: function(modify) { - Animation.selected.setLength(limitNumber(modify(Animation.selected.length), 0, 1e4)) - }, - onBefore: function() { - Undo.initEdit({animations: [Animation.selected]}); - }, - onAfter: function() { - Undo.finishEdit('Change Animation Length') - } - }) new NumSlider('slider_keyframe_time', { category: 'animation', condition: () => Animator.open && Timeline.selected.length, @@ -607,6 +599,21 @@ BARS.defineActions(function() { Undo.finishEdit('move keyframes') } }) + new BarSelect('keyframe_interpolation', { + category: 'animation', + condition: () => Animator.open && Timeline.selected.length, + options: { + linear: true, + catmullrom: true, + }, + onChange: function(sel, event) { + Undo.initEdit({keyframes: Timeline.selected}) + Timeline.selected.forEach((kf) => { + kf.interpolation = sel.value; + }) + Undo.finishEdit('change keyframes interpolation') + } + }) new Action('reset_keyframe', { icon: 'replay', category: 'animation', diff --git a/js/animations/timeline.js b/js/animations/timeline.js index 7a696a5..e428b4d 100644 --- a/js/animations/timeline.js +++ b/js/animations/timeline.js @@ -146,6 +146,8 @@ const Timeline = { if (bot < 0) body.scrollTop = Math.clamp(body.scrollTop + speed, 0, body_inner.clientHeight - body.clientHeight + 3); if (lef < 0) body.scrollLeft = body.scrollLeft - speed; if (rig < 0) body.scrollLeft = Math.clamp(body.scrollLeft + speed, 0, body_inner.clientWidth - body.clientWidth); + + updateKeyframeSelection() }, end(e) { if (!Timeline.selector.selecting) return false; diff --git a/js/interface/actions.js b/js/interface/actions.js index ab040c8..1bdbaa1 100644 --- a/js/interface/actions.js +++ b/js/interface/actions.js @@ -1621,6 +1621,7 @@ const BARS = { id: 'keyframe', children: [ 'slider_keyframe_time', + 'keyframe_interpolation', 'change_keyframe_file', 'reset_keyframe' ] diff --git a/js/interface/menu.js b/js/interface/menu.js index a2b1e8b..7642d2f 100644 --- a/js/interface/menu.js +++ b/js/interface/menu.js @@ -147,61 +147,103 @@ class Menu { if (last.length && !last.hasClass('menu_separator')) { parent.append(entry) } - } else if (typeof s === 'string' || s instanceof Action) { - if (typeof s === 'string') { - s = BarItems[s] - } - if (!s) { - return; - } - entry = $(s.menu_node) - if (BARS.condition(s.condition)) { + return; + } + if (typeof s == 'string' && BarItems[s]) { + s = BarItems[s]; + } + if (!Condition(s.condition, context)) return; - entry.off('click') - entry.off('mouseenter mousedown') - entry.on('mouseenter mousedown', function(e) { - if (this == e.target) { - scope.hover(this, e) - } - }) - //Submenu - if (typeof s.children == 'function' || typeof s.children == 'object') { - createChildList(s, entry) - } else { - entry.on('click', (e) => {s.trigger(e)}) - //entry[0].addEventListener('click', ) + if (s instanceof Action) { + + entry = $(s.menu_node) + + entry.off('click') + entry.off('mouseenter mousedown') + entry.on('mouseenter mousedown', function(e) { + if (this == e.target) { + scope.hover(this, e) } + }) + //Submenu + if (typeof s.children == 'function' || typeof s.children == 'object') { + createChildList(s, entry) + } else { + entry.on('click', (e) => {s.trigger(e)}) + //entry[0].addEventListener('click', ) + } + parent.append(entry) + + } else if (s instanceof BarSelect) { + + if (typeof s.icon === 'function') { + var icon = Blockbench.getIconNode(s.icon(context), s.color) + } else { + var icon = Blockbench.getIconNode(s.icon, s.color) + } + entry = $(`
  • ${tl(s.name)}
  • `) + entry.prepend(icon) + + //Submenu + var children = []; + for (var key in s.options) { + + let val = s.options[key]; + if (val) { + (function() { + var save_key = key; + children.push({ + name: s.getNameFor(key), + id: key, + icon: val.icon || ((s.value == save_key) ? 'far.fa-dot-circle' : 'far.fa-circle'), + condition: val.condition, + click: (e) => { + s.set(save_key); + if (s.onChange) { + s.onChange(s, e); + } + } + }) + })() + } + } + + let child_count = createChildList({children}, entry) + + if (child_count !== 0 || typeof s.click === 'function') { parent.append(entry) } + entry.mouseenter(function(e) { + scope.hover(this, e) + }) + } else if (typeof s === 'object') { - if (BARS.condition(s.condition, context)) { - let child_count; - if (typeof s.icon === 'function') { - var icon = Blockbench.getIconNode(s.icon(context), s.color) - } else { - var icon = Blockbench.getIconNode(s.icon, s.color) - } - entry = $(`
  • ${tl(s.name)}
  • `) - entry.prepend(icon) - if (typeof s.click === 'function') { - entry.click(e => { - if (e.target == entry.get(0)) { - s.click(context, e) - } - }) - } - //Submenu - if (typeof s.children == 'function' || typeof s.children == 'object') { - child_count = createChildList(s, entry) - } - if (child_count !== 0 || typeof s.click === 'function') { - parent.append(entry) - } - entry.mouseenter(function(e) { - scope.hover(this, e) + let child_count; + if (typeof s.icon === 'function') { + var icon = Blockbench.getIconNode(s.icon(context), s.color) + } else { + var icon = Blockbench.getIconNode(s.icon, s.color) + } + entry = $(`
  • ${tl(s.name)}
  • `) + entry.prepend(icon) + if (typeof s.click === 'function') { + entry.click(e => { + if (e.target == entry.get(0)) { + s.click(context, e) + } }) } + //Submenu + if (typeof s.children == 'function' || typeof s.children == 'object') { + child_count = createChildList(s, entry) + } + if (child_count !== 0 || typeof s.click === 'function') { + parent.append(entry) + } + entry.mouseenter(function(e) { + scope.hover(this, e) + }) } } diff --git a/lang/en.json b/lang/en.json index c9a3405..d65cbe2 100644 --- a/lang/en.json +++ b/lang/en.json @@ -958,6 +958,10 @@ "action.slider_animation_length.desc": "Change the length of the selected animation", "action.slider_keyframe_time": "Timecode", "action.slider_keyframe_time.desc": "Change the timecode of the selected keyframes", + "action.keyframe_interpolation": "Interpolation", + "action.keyframe_interpolation.desc": "Select the keyframe interpolation mode", + "action.keyframe_interpolation.linear": "Linear", + "action.keyframe_interpolation.catmullrom": "Smooth", "action.change_keyframe_file": "Select File", "action.change_keyframe_file.desc": "Select an audio file to preview a sound effect.", "action.reset_keyframe": "Reset Keyframe",