Keyframe interpolation interface and io
This commit is contained in:
parent
c3b6922d52
commit
992f42e3a0
@ -890,7 +890,7 @@
|
||||
:title="tl('timeline.'+keyframe.channel)"
|
||||
@contextmenu.prevent="keyframe.showContextMenu($event)"
|
||||
>
|
||||
<i class="material-icons">stop</i>
|
||||
<i class="material-icons">{{ keyframe.interpolation == 'catmullrom' ? 'lens' : 'stop' }}</i>
|
||||
<svg class="keyframe_waveform" v-if="keyframe.channel == 'sound' && keyframe.file && waveforms[keyframe.file]" :style="{width: waveforms[keyframe.file].duration * size}">
|
||||
<polygon :points="getWaveformPoints(waveforms[keyframe.file].samples, size)"></polygon>
|
||||
</svg>
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -1621,6 +1621,7 @@ const BARS = {
|
||||
id: 'keyframe',
|
||||
children: [
|
||||
'slider_keyframe_time',
|
||||
'keyframe_interpolation',
|
||||
'change_keyframe_file',
|
||||
'reset_keyframe'
|
||||
]
|
||||
|
@ -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 = $(`<li title="${s.description||''}" menu_item="${s.id}">${tl(s.name)}</li>`)
|
||||
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 = $(`<li title="${s.description||''}" menu_item="${s.id}">${tl(s.name)}</li>`)
|
||||
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 = $(`<li title="${s.description||''}" menu_item="${s.id}">${tl(s.name)}</li>`)
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user