diff --git a/css/panels.css b/css/panels.css index 468df0b59..ddc73ca00 100644 --- a/css/panels.css +++ b/css/panels.css @@ -1936,13 +1936,24 @@ opacity: 0.25; } #timeline_graph_editor .keyframe { - height: 16px; - width: 11px; + height: 16px; + width: 16px; + justify-content: center; + display: flex; + align-items: center; + } + #timeline_graph_editor .keyframe:hover { + height: 20px; + width: 20px; + translate: -2px -3px; + color: var(--hover-color) !important; } #timeline_graph_editor .keyframe > i { font-size: 12px; - margin-top: 2px; - margin-left: -2px; + margin: 0; + } + #timeline_graph_editor .keyframe:hover > i { + font-size: 16px; } #timeline_graph_editor_amplifier { diff --git a/css/setup.css b/css/setup.css index fc44a1118..9285cc062 100644 --- a/css/setup.css +++ b/css/setup.css @@ -184,146 +184,182 @@ } - .icon-keyframe_discontinuous_bezier:before { - content: "\e92d"; - } - .icon-keyframe_smooth:before { - content: "\e92c"; - } - .icon-keyframe_step:before { - content: "\e92b"; - } - .icon-keyframe_bezier:before { - content: "\e92a"; - } .icon-collada:before { - content: "\e926"; + content: "\e926"; } .icon-fbx:before { - content: "\e927"; + content: "\e927"; } .icon-format_bedrock_block:before { - content: "\e928"; + content: "\e928"; } .icon-format_bedrock_entity:before { - content: "\e929"; - } - .icon-keyframe:before { - content: "\e924"; - } - .icon-keyframe_discontinuous:before { - content: "\e925"; + content: "\e929"; } .icon-gizmo:before { - content: "\e922"; + content: "\e922"; } .icon-gltf:before { - content: "\e923"; + content: "\e923"; } .icon-format_bedrock_legacy:before { - content: "\e91a"; + content: "\e91a"; } .icon-crossbow:before { - content: "\e91b"; + content: "\e91b"; } .icon-format_bedrock:before { - content: "\e91c"; + content: "\e91c"; } .icon-format_block:before { - content: "\e91d"; + content: "\e91d"; } .icon-format_free:before { - content: "\e91e"; + content: "\e91e"; } .icon-format_hytale:before { - content: "\e91f"; + content: "\e91f"; } .icon-format_java:before { - content: "\e920"; + content: "\e920"; } .icon-format_optifine:before { - content: "\e921"; + content: "\e921"; } .icon-sketchfab:before { - content: "\e919"; + content: "\e919"; } .icon-blockbench_file:before { - content: "\e900"; + content: "\e900"; } .icon-vertexsnap:before { - content: "\e901"; + content: "\e901"; } .icon-create_bitmap:before { - content: "\e902"; + content: "\e907"; } .icon-objects:before { - content: "\e903"; + content: "\e908"; } .icon-bow:before { - content: "\e904"; + content: "\e90b"; } .icon-bb_interface:before { - content: "\e905"; + content: "\e90d"; } .icon-blockbench:before { - content: "\e906"; + content: "\e911"; } .icon-x11:before { - content: "\e907"; + content: "\e912"; } .icon-baby_zombie:before { - content: "\e908"; + content: "\e913"; } .icon-armor_stand:before { - content: "\e909"; + content: "\e914"; } .icon-armor_stand_small:before { - content: "\e90a"; + content: "\e915"; } .icon-ground:before { - content: "\e90b"; + content: "\e916"; } .icon-hud:before { - content: "\e90c"; + content: "\e917"; } .icon-inventory_full:before { - content: "\e90d"; + content: "\e918"; } .icon-inventory_nine:before { - content: "\e90e"; + content: "\e924"; } .icon-inventory_single:before { - content: "\e90f"; + content: "\e925"; } .icon-player_head:before { - content: "\e910"; + content: "\e92a"; } .icon-zombie:before { - content: "\e911"; + content: "\e92b"; } .icon-blockbench_inverted:before { - content: "\e912"; + content: "\e92c"; } .icon-optifine_file:before { - content: "\e913"; + content: "\e92d"; } .icon-saved:before { - content: "\e914"; + content: "\e92e"; } .icon-player:before { - content: "\e915"; + content: "\e92f"; } .icon-mirror_x:before { - content: "\e916"; + content: "\e930"; } .icon-mirror_y:before { - content: "\e917"; + content: "\e931"; } .icon-mirror_z:before { - content: "\e918"; + content: "\e932"; + } + + + .icon-keyframe_linear:before { + content: "\e933"; + } + .icon-keyframe_bezier:before { + content: "\e934"; + } + .icon-keyframe_smooth:before { + content: "\e935"; + } + .icon-keyframe_step:before { + content: "\e936"; + } + .icon-keyframe_linear_discontinuous:before { + content: "\e937"; + } + .icon-keyframe_bezier_discontinuous:before { + content: "\e938"; + } + .icon-keyframe_step_discontinuous:before { + content: "\e90e"; + } + .icon-keyframe_linear_molang:before { + content: "\e90a"; + } + .icon-keyframe_smooth_molang:before { + content: "\e90c"; + } + .icon-keyframe_bezier_molang:before { + content: "\e903"; + } + .icon-keyframe_step_molang:before { + content: "\e910"; + } + .icon-keyframe_linear_discontinuous_molang:before { + content: "\e909"; + } + .icon-keyframe_bezier_discontinuous_molang:before { + content: "\e902"; + } + .icon-keyframe_step_discontinuous_molang:before { + content: "\e90f"; + } + .icon-keyframe_hidden:before { + content: "\e904"; + } + .icon-keyframe_hidden_discontinuous:before { + content: "\e905"; + } + .icon-keyframe_hidden_molang:before { + content: "\e906"; } + .icon { user-select: none; @@ -412,6 +448,25 @@ --color-spline-handle-aligned: #F0B000; --color-spline-handle-free: #00D123; + --color-keyframe: var(--color-text); + --color-keyframe-selected: var(--color-accent); + --color-keyframe-hovered: var(--color-light); + --color-keyframe-collapsed: #495061; + --color-keyframe-bezier-handle-dot: var(--color-back); + --color-keyframe-bezier-handle-line: var(--color-grid); + + /* values for keyframe and timeline styling are used in js, so the pixel suffix is not use */ + --timeline-channel-height: 24; + --keyframe-radius: 16; + --keyframe-radius-hover: 20; + --keyframe-radius-hidden: 12; + + --keyframe-graph-radius: 12; + --keyframe-graph-radius-hover: 16; + --keyframe-bezier-handle-diameter: 10; + --keyframe-bezier-stroke-width: 3; + /* end of timeline & keyframe values */ + --font-custom-main: ''; --font-custom-headline: ''; --font-custom-code: ''; diff --git a/font/icomoon.ttf b/font/icomoon.ttf index 24c276197..460a7dc9b 100644 Binary files a/font/icomoon.ttf and b/font/icomoon.ttf differ diff --git a/font/icomoon.woff b/font/icomoon.woff index 4e5cf1cb7..9897269cf 100644 Binary files a/font/icomoon.woff and b/font/icomoon.woff differ diff --git a/icons/icomoon/keyframes/blank/keyframe_bezier_blank.svg b/icons/icomoon/keyframes/blank/keyframe_bezier_blank.svg new file mode 100644 index 000000000..ae4fc3b4c --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_bezier_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_bezier_discontinuous_blank.svg b/icons/icomoon/keyframes/blank/keyframe_bezier_discontinuous_blank.svg new file mode 100644 index 000000000..3f5f249a5 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_bezier_discontinuous_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_bezier_discontinuous_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_bezier_discontinuous_molang_blank.svg new file mode 100644 index 000000000..455e8b247 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_bezier_discontinuous_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_bezier_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_bezier_molang_blank.svg new file mode 100644 index 000000000..399baba39 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_bezier_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_hidden_blank.svg b/icons/icomoon/keyframes/blank/keyframe_hidden_blank.svg new file mode 100644 index 000000000..133ee8e42 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_hidden_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_hidden_discontinuous_blank.svg b/icons/icomoon/keyframes/blank/keyframe_hidden_discontinuous_blank.svg new file mode 100644 index 000000000..18764e543 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_hidden_discontinuous_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_hidden_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_hidden_molang_blank.svg new file mode 100644 index 000000000..382338671 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_hidden_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_linear_blank.svg b/icons/icomoon/keyframes/blank/keyframe_linear_blank.svg new file mode 100644 index 000000000..a2b4a6c4f --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_linear_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_linear_discontinuous_blank.svg b/icons/icomoon/keyframes/blank/keyframe_linear_discontinuous_blank.svg new file mode 100644 index 000000000..897a895f8 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_linear_discontinuous_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_linear_discontinuous_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_linear_discontinuous_molang_blank.svg new file mode 100644 index 000000000..c9628cb1e --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_linear_discontinuous_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_linear_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_linear_molang_blank.svg new file mode 100644 index 000000000..77c667989 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_linear_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_smooth_blank.svg b/icons/icomoon/keyframes/blank/keyframe_smooth_blank.svg new file mode 100644 index 000000000..0af781690 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_smooth_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_smooth_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_smooth_molang_blank.svg new file mode 100644 index 000000000..7a1103fd0 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_smooth_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_step_blank.svg b/icons/icomoon/keyframes/blank/keyframe_step_blank.svg new file mode 100644 index 000000000..1ca95fd6c --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_step_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_step_discontinuous_blank.svg b/icons/icomoon/keyframes/blank/keyframe_step_discontinuous_blank.svg new file mode 100644 index 000000000..9d225ee2b --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_step_discontinuous_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_step_discontinuous_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_step_discontinuous_molang_blank.svg new file mode 100644 index 000000000..a73176b34 --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_step_discontinuous_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframes/blank/keyframe_step_molang_blank.svg b/icons/icomoon/keyframes/blank/keyframe_step_molang_blank.svg new file mode 100644 index 000000000..32772d59d --- /dev/null +++ b/icons/icomoon/keyframes/blank/keyframe_step_molang_blank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icomoon/keyframe.svg b/icons/icomoon/keyframes/keyframe.svg similarity index 100% rename from icons/icomoon/keyframe.svg rename to icons/icomoon/keyframes/keyframe.svg diff --git a/icons/icomoon/keyframe_bezier.svg b/icons/icomoon/keyframes/keyframe_bezier.svg similarity index 100% rename from icons/icomoon/keyframe_bezier.svg rename to icons/icomoon/keyframes/keyframe_bezier.svg diff --git a/icons/icomoon/keyframe_discontinuous.svg b/icons/icomoon/keyframes/keyframe_discontinuous.svg similarity index 100% rename from icons/icomoon/keyframe_discontinuous.svg rename to icons/icomoon/keyframes/keyframe_discontinuous.svg diff --git a/icons/icomoon/keyframe_discontinuous_bezier.svg b/icons/icomoon/keyframes/keyframe_discontinuous_bezier.svg similarity index 100% rename from icons/icomoon/keyframe_discontinuous_bezier.svg rename to icons/icomoon/keyframes/keyframe_discontinuous_bezier.svg diff --git a/icons/icomoon/keyframes/keyframe_icons.af b/icons/icomoon/keyframes/keyframe_icons.af new file mode 100644 index 000000000..e74736e96 Binary files /dev/null and b/icons/icomoon/keyframes/keyframe_icons.af differ diff --git a/icons/icomoon/keyframe_smooth.svg b/icons/icomoon/keyframes/keyframe_smooth.svg similarity index 100% rename from icons/icomoon/keyframe_smooth.svg rename to icons/icomoon/keyframes/keyframe_smooth.svg diff --git a/icons/icomoon/keyframe_step.svg b/icons/icomoon/keyframes/keyframe_step.svg similarity index 100% rename from icons/icomoon/keyframe_step.svg rename to icons/icomoon/keyframes/keyframe_step.svg diff --git a/js/animations/Timeline.vue b/js/animations/Timeline.vue new file mode 100644 index 000000000..3d567923e --- /dev/null +++ b/js/animations/Timeline.vue @@ -0,0 +1,1724 @@ + + + + + \ No newline at end of file diff --git a/js/animations/keyframe.js b/js/animations/keyframe.js index d1e2aa882..c42f68d41 100644 --- a/js/animations/keyframe.js +++ b/js/animations/keyframe.js @@ -474,6 +474,7 @@ export class Keyframe { if (this.transform) Timeline.vue.graph_editor_channel = this.channel; + Timeline.vue.refreshTimelineCanvas(); return this; } clickSelect(event) { @@ -681,6 +682,9 @@ export function updateKeyframeSelection() { } BARS.updateConditions() Blockbench.dispatchEvent('update_keyframe_selection'); + + // If the selector isn't the one calling this, framerate drops dramatically. + if (!Timeline.selector.selecting) Timeline.vue.refreshTimelineCanvas(); } export function selectAllKeyframes() { if (!Animation.selected) return; @@ -878,6 +882,8 @@ BARS.defineActions(function() { Animator.preview() BarItems.slider_keyframe_time.update() Undo.finishEdit('Move keyframes back') + + Timeline.vue.refreshTimelineCanvas(); } }) new Action('move_keyframe_forth', { @@ -894,6 +900,8 @@ BARS.defineActions(function() { Animator.preview() BarItems.slider_keyframe_time.update() Undo.finishEdit('Move keyframes forwards') + + Timeline.vue.refreshTimelineCanvas(); } }) function slideKeyframes(difference, event) { diff --git a/js/animations/timeline.js b/js/animations/timeline.js index 0862cfab6..1ca7ab646 100644 --- a/js/animations/timeline.js +++ b/js/animations/timeline.js @@ -1,5 +1,6 @@ import { markerColors } from "../marker_colors"; import { dragHelper } from "../util/drag_helper"; +import TimelineComponent from "./Timeline.vue" export class TimelineMarker { constructor(data) { @@ -94,7 +95,8 @@ export const Timeline = { !e.target.classList.contains('keyframe_section') && !e.target.classList.contains('animator_head_bar') && e.target.id !== 'timeline_graph_editor' && - e.target.id !== 'timeline_body_inner' + e.target.id !== 'timeline_body_keyframe_canvas' && + Timeline.vue.keyframeHoverUuid !== "" )) { return }; @@ -207,6 +209,7 @@ export const Timeline = { } updateKeyframeSelection() + Timeline.vue.refreshTimelineCanvas(); }, end(e) { e.stopPropagation(); @@ -232,6 +235,8 @@ export const Timeline = { .hide(); Undo.finishSelection('Select keyframes'); } + + Timeline.vue.refreshTimelineCanvas(); }, }, setTime(seconds, editing) { @@ -581,6 +586,7 @@ export const Timeline = { body.scrollTop += event.deltaY/4; } Timeline.updateSize() + Timeline.vue.refreshTimelineCanvas(); event.preventDefault(); }); @@ -623,6 +629,7 @@ export const Timeline = { Timeline.selected.empty(); Timeline.vue.markers = []; Timeline.vue.animation_length = 0; + Timeline.vue.clearTimelineCanvas(); updateKeyframeSelection(); }, start() { @@ -787,19 +794,6 @@ StateMemory.init("timeline_channels", "object", { }); Interface.definePanels(() => { - function eventTargetToAnimator(target) { - let target_node = target; - let i = 0; - while (target_node && target_node.classList && !target_node.classList.contains('animator')) { - if (i < 3 && target_node) { - target_node = target_node.parentNode; - i++; - } else { - return []; - } - } - return [Timeline.animators.find(animator => animator.uuid == target_node.attributes.uuid.value), target_node]; - } Timeline.panel = new Panel('timeline', { icon: 'timeline', condition: {modes: ['animate'], method: () => !AnimationController.selected}, @@ -836,1074 +830,7 @@ Interface.definePanels(() => { this.inside_vue.timeline_height = this.inside_vue.$el.clientHeight; } }, - component: { - name: 'panel-timeline', - data() {return { - size: 300, - length: 10, - timeline_height: 100, - animation_length: 0, - scroll_left: 0, - scroll_top: 0, - head_width: Blockbench.isMobile ? 108 : Interface.data.timeline_head, - timecodes: [], - animators: Timeline.animators, - markers: [], - custom_range: Timeline.custom_range, - waveforms: Timeline.waveforms, - focus_channel: null, - playhead: Timeline.time, - timestamp: '0', - framenumber: '0', - - graph_editor_open: false, - graph_editor_channel: 'rotation', - graph_editor_axis: 'x', - graph_offset: 200, - graph_size: 200, - show_other_graphs: true, - include_other_graphs: true, - show_zero_line: true, - show_all_handles: !Settings.get('only_selected_bezier_handles'), - loop_graphs: [''], - - onion_skin_selectable: BarItems.animation_onion_skin.value, - onion_skin_time: 0, - - channels: StateMemory.timeline_channels - }}, - computed: { - graph_editor_animator() { - return this.animators.find(animator => animator.selected && animator instanceof BoneAnimator); - }, - zero_line() { - let height = this.graph_offset; - return `M0 ${height} L10000 ${height}` - }, - one_line() { - let height = this.graph_offset - this.graph_size; - return `M0 ${height} L10000 ${height}` - }, - rulers() { - let lines = []; - let values = [0]; - - let interval_options = [1, 2, 4, 8, 10, 20, 25, 50, 100, 200, 250, 400, 500]; - let estimate = 100 / Timeline.vue.graph_size; - if (estimate > 500) return values; - let interval = Math.snapToValues(estimate, interval_options); - let box_height = Timeline.vue.$refs.timeline_body.clientHeight; - - for (let i = 1; i < 20; i += 1) { - let value = i * interval; - let keep_going = false; - if (this.graph_offset - value * this.graph_size > 0) { - values.push(value); - keep_going = true; - } - if (this.graph_offset + value * this.graph_size < box_height) { - values.push(i * -interval); - keep_going = true; - } - if (!keep_going) break; - } - - values.forEach(value => { - let height = this.graph_offset - this.graph_size * value; - lines.push({ - position: height, - label: Math.round(value), - path: `M0 ${height} L10000 ${height}` - }); - }) - return lines; - }, - graphs() { - let ba = this.graph_editor_animator; - if (!ba || !ba[this.graph_editor_channel] || !ba[this.graph_editor_channel].length) { - this.loop_graphs.empty(); - return []; - } - let original_time = Timeline.time; - let step = 2; - let clientWidth = this.$refs.timeline_body ? this.$refs.timeline_body.clientWidth : 400; - let clientHeight = this.$refs.timeline_body ? this.$refs.timeline_body.clientHeight : 400; - let keyframes = ba[this.graph_editor_channel]; - let points = []; - let loop_points = []; - - let min = this.show_zero_line ? -1 : Timeline.graph_editor_limit, - max = this.show_zero_line ? 1 : -Timeline.graph_editor_limit; - - for (let time = Math.clamp(this.scroll_left - 9, 0, Infinity); time < (clientWidth + this.scroll_left - this.head_width); time += step) { - Timeline.time = time / this.size; - - let snap_kf = keyframes.find(kf => Timeline.time <= kf.time && Timeline.time > kf.time - step / this.size ); - if (snap_kf) { - Timeline.time = snap_kf.time; - } - Animator.resetLastValues(); - let values = [ - (this.show_other_graphs || this.graph_editor_axis === 'x') ? ba.interpolate(this.graph_editor_channel, false, 'x') : 0, - (this.show_other_graphs || this.graph_editor_axis === 'y') ? ba.interpolate(this.graph_editor_channel, false, 'y') : 0, - (this.show_other_graphs || this.graph_editor_axis === 'z') ? ba.interpolate(this.graph_editor_channel, false, 'z') : 0 - ]; - let value = values[this.graph_editor_axis_number]; - if (snap_kf) snap_kf.display_value = value; - - if (Timeline.time > Animation.selected.length && Animation.selected.length && Animation.selected.loop === 'loop') { - if (points.length && !loop_points.length) loop_points.push(points.last()) - loop_points.push(values); - } else { - points.push(values); - if (this.show_other_graphs && this.include_other_graphs) { - min = Math.min(min, ...values); - max = Math.max(max, ...values); - } else { - min = Math.min(min, value); - max = Math.max(max, value); - } - } - } - keyframes.forEach(kf => { - if (kf.interpolation === 'bezier') { - min = Math.min(min, kf.display_value + kf.bezier_left_value[this.graph_editor_axis_number]); - max = Math.max(max, kf.display_value + kf.bezier_left_value[this.graph_editor_axis_number]); - min = Math.min(min, kf.display_value + kf.bezier_right_value[this.graph_editor_axis_number]); - max = Math.max(max, kf.display_value + kf.bezier_right_value[this.graph_editor_axis_number]); - } - }) - - Timeline.time = original_time; - - let padding = 16; - let min_size = 2.4; - let unit_size = Math.clamp(max-min, min_size, Timeline.graph_editor_limit); - this.graph_size = (clientHeight - 2*padding) / unit_size; - let blend = Math.clamp(1 - (max-min) / min_size, 0, 1) - this.graph_offset = clientHeight - padding + (this.graph_size * (min - unit_size/2 * blend ) ); - - let graphs = this.show_other_graphs ? ['', '', ''] : ['']; - points.forEach((values, i) => { - let command = i == 0 ? 'M' : 'L'; - if (this.show_other_graphs) { - values.forEach((value, axis) => { - graphs[axis] += `${command}${i*step} ${this.graph_offset - value * this.graph_size} `; - }) - } else { - graphs[0] += `${command}${i*step} ${this.graph_offset - values[this.graph_editor_axis_number] * this.graph_size} `; - } - }) - - this.loop_graphs.empty(); - if (loop_points.length) { - if (this.show_other_graphs) { - this.loop_graphs.push('', '', ''); - } else { - this.loop_graphs.push(''); - } - loop_points.forEach((values, i) => { - let command = i == 0 ? 'M' : 'L'; - i = i + points.length - 1; - if (this.show_other_graphs) { - values.forEach((value, axis) => { - this.loop_graphs[axis] += `${command}${i*step} ${this.graph_offset - value * this.graph_size} `; - }) - } else { - this.loop_graphs[0] += `${command}${i*step} ${this.graph_offset - values[this.graph_editor_axis_number] * this.graph_size} `; - } - }) - } - - return graphs; - }, - graph_editor_axis_number() { - return getAxisNumber(this.graph_editor_axis) - } - }, - methods: { - tl, - updateTimecodes() { - if (!this._isMounted) return; - this.timecodes.empty(); - let timeline_container_width = Panels.timeline.node.clientWidth - this.head_width; - this.length = Timeline.getMaxLength(); - - var step = 1 - if (this.size < 1) {step = 1} - else if (this.size < 20) {step = 4} - else if (this.size < 40) {step = 2} - else if (this.size < 100) {step = 1} - else if (this.size < 256) {step = 0.5} - else if (this.size < 520) {step = 0.25} - else if (this.size < 660) {step = 0.2} - else if (this.size < 860) {step = 0.1} - else {step = 0.05} - - // Rounding to "FPS" to better match snapping - if (step < 1) { - let substep_simplification = Math.max((Math.floor(Math.sqrt(step / Timeline.getStep()))-1), 1); - var FPS = Timeline.getStep() / substep_simplification; - step = Math.round(step/FPS) * FPS - } - - // Substep simplification - let substeps = step / Timeline.getStep() - while (substeps > 8) { - substeps /= 2; - } - - // Generate - var i = Math.floor(this.scroll_left / this.size / step) * step; - while (i < Math.ceil((this.scroll_left + timeline_container_width) / this.size / step) * step) { - if (settings.timecode_frame_number.value) { - var text = Math.round(i / Timeline.getStep()); - } else { - var text = Math.round(i*100)/100; - } - this.timecodes.push({ - time: i, - width: step, - substeps, - text, - }) - i += step; - } - - if (this.graph_editor_open) { - this.graph_size += 1e-5; - } - }, - updateGraph() { - if (this.graph_editor_open) { - this.graph_size += 1e-7; - } - }, - toggleAnimator(animator) { - animator.expanded = !animator.expanded; - }, - removeAnimator(animator) { - Timeline.animators.remove(animator); - }, - toggleGlobalSpace(animator) { - Undo.initEdit({animations: [Animation.selected]}); - animator.rotation_global = !animator.rotation_global; - Undo.finishEdit('Toggle rotation in global space'); - Animator.preview(); - }, - selectChannel(animator, channel) { - if (this.graph_editor_channel == channel && animator.selected) return; - if (!animator.channels[channel].transform) return; - Undo.initSelection(); - if (!animator.selected) animator.select(); - // Select keyframe in new channel - if (animator[channel].length && Keyframe.selected.length > 0) { - if (animator[channel].length == 1 && Math.epsilon(animator[channel][0].time, Timeline.time, 0.002)) { - animator[channel][0].select(); - } else if (animator[channel].find(kf => Math.epsilon(kf.time, Keyframe.selected[0].time))) { - let kf = animator[channel].find(kf => Math.epsilon(kf.time, Keyframe.selected[0].time, 0.002)); - kf.select(); - } else { - let kf = animator[channel].slice().sort((a, b) => Math.abs(a.time - Timeline.time) - Math.abs(b.time - Timeline.time))[0]; - kf.select(); - } - } - this.graph_editor_channel = channel; - Undo.finishSelection('Select animation channel'); - }, - getColor(index) { - if (index == -1 || index == undefined) return; - return markerColors[index % markerColors.length].standard; - }, - getWaveformPoints(samples, size) { - let height = 23; - let points = [`0,${height}`]; - samples.forEach((sample, i) => { - points.push(`${(i + 0.5) / Timeline.waveform_sample_rate * size},${(1 - sample) * height}`); - }) - points.push(`${(samples.length) / Timeline.waveform_sample_rate * size},${height}`) - return points.join(' '); - }, - updateScroll() { - this.scroll_left = this.$refs.timeline_body ? this.$refs.timeline_body.scrollLeft : 0; - this.scroll_top = this.$refs.timeline_body ? this.$refs.timeline_body.scrollTop : 0; - }, - openContextMenu(event) { - if (event.target.nodeName == 'KEYFRAME' || event.target.parentElement.nodeName == 'KEYFRAME') return; - if (Blockbench.hasFlag('no_context_menu')) return; - event.preventDefault(); - Timeline.menu.open(event, event); - }, - dragAnimator(animator, e1) { - if (getFocusedTextInput()) return; - if (e1.button == 1 || e1.button == 2) return; - convertTouchEvent(e1); - - let active = false; - let helper; - let timeout; - let drop_target, drop_target_node, order; - let last_event = e1; - - function move(e2) { - convertTouchEvent(e2); - let offset = [ - e2.clientX - e1.clientX, - e2.clientY - e1.clientY, - ] - if (!active) { - let distance = Math.sqrt(Math.pow(offset[0], 2) + Math.pow(offset[1], 2)) - if (Blockbench.isTouch) { - if (distance > 20 && timeout) { - clearTimeout(timeout); - timeout = null; - } else { - document.getElementById('timeline_body_inner').scrollTop += last_event.clientY - e2.clientY; - } - } else if (distance > 6) { - active = true; - Undo.initSelection(); - } - } else { - if (e2) e2.preventDefault(); - - if (Menu.open) Menu.open.hide(); - - if (!helper) { - helper = document.createElement('div'); - helper.id = 'animation_drag_helper'; - let icon = document.createElement('i'); icon.className = 'material-icons'; icon.innerText = 'chevron_right'; helper.append(icon); - let span = document.createElement('span'); span.innerText = animator.name; helper.append(span); - document.body.append(helper); - } - helper.style.left = `${e2.clientX}px`; - helper.style.top = `${e2.clientY}px`; - - // drag - $('.drag_hover').removeClass('drag_hover'); - $('.animator[order]').attr('order', null); - - let target = document.elementFromPoint(e2.clientX, e2.clientY); - [drop_target, drop_target_node] = eventTargetToAnimator(target); - if (drop_target) { - let location = e2.clientY - $(drop_target_node).offset().top; - let half_height = drop_target_node.clientHeight/2; - let order = location <= half_height ? -1 : 1; - drop_target_node.setAttribute('order', order) - drop_target_node.classList.add('drag_hover'); - } - } - last_event = e2; - } - function off(e2) { - if (helper) helper.remove(); - removeEventListeners(document, 'mousemove touchmove', move); - removeEventListeners(document, 'mouseup touchend', off); - $('.drag_hover').removeClass('drag_hover'); - $('.animator[order]').attr('order', null); - if (Blockbench.isTouch) clearTimeout(timeout); - - if (active && !open_menu) { - convertTouchEvent(e2); - let target = document.elementFromPoint(e2.clientX, e2.clientY); - let [target_animator] = eventTargetToAnimator(target); - if (!target_animator || target_animator == animator ) return; - - let index = Timeline.animators.indexOf(target_animator); - if (index == -1) return; - if (order == 1) index++; - if (Timeline.animators[index] == animator) return; - Timeline.animators.remove(animator); - Timeline.animators.splice(index, 0, animator); - Undo.finishSelection('Rearrange animators in timeline'); - } - } - - if (Blockbench.isTouch) { - timeout = setTimeout(() => { - active = true; - move(e1); - }, 320) - } - - addEventListeners(document, 'mousemove touchmove', move, {passive: false}); - addEventListeners(document, 'mouseup touchend', off, {passive: false}); - }, - dragKeyframes(clicked, e1) { - if (e1.target.classList.contains('keyframe_bezier_handle')) return; - if (e1.button > 0) return; - convertTouchEvent(e1); - let dragging_range; - let dragging_restriction; - let originalValue; - let previousValue; - let time_stretching; - let values_changed; - let is_setup = false; - let old_bezier_values = {}; - let scope = this; - - function setup() { - dragging_range = [Infinity, 0]; - dragging_restriction; - originalValue; - previousValue = 0; - time_stretching = !Timeline.vue.graph_editor_open && (e1.ctrlOrCmd || Pressing.overrides.ctrl) && Timeline.selected.length > 1; - values_changed = false; - - if (!clicked.selected && !e1.shiftKey && !Pressing.overrides.shift && Timeline.selected.length != 0) { - clicked.select() - } else if (clicked && !clicked.selected) { - clicked.select({shiftKey: true}) - } - - Undo.initEdit({keyframes: Timeline.selected}); - Timeline.dragging_keyframes = true; - - for (var kf of Timeline.selected) { - kf.time_before = kf.time; - dragging_range[0] = Math.min(dragging_range[0], kf.time); - dragging_range[1] = Math.max(dragging_range[1], kf.time); - old_bezier_values[kf.uuid] = { - left: kf.bezier_left_time.slice(), - right: kf.bezier_right_time.slice(), - } - } - - if (Timeline.vue.graph_editor_open) { - // Find dragging restriction - dragging_restriction = [-Infinity, Infinity]; - let ba = scope.graph_editor_animator || 0; - let all_keyframes = ba[scope.graph_editor_channel]; - if (all_keyframes) { - - let frst_keyframe; - let last_keyframe; - Timeline.selected.forEach(kf => { - if (!frst_keyframe || frst_keyframe.time > kf.time) frst_keyframe = kf; - if (!last_keyframe || last_keyframe.time < kf.time) last_keyframe = kf; - }) - let prvs_keyframe; - let next_keyframe; - all_keyframes.forEach(kf => { - if (kf.time < frst_keyframe.time && (!prvs_keyframe || prvs_keyframe.time < kf.time)) prvs_keyframe = kf; - if (kf.time > last_keyframe.time && (!next_keyframe || next_keyframe.time > kf.time)) next_keyframe = kf; - }) - if (prvs_keyframe) dragging_restriction[0] = prvs_keyframe.time; - if (next_keyframe) dragging_restriction[1] = next_keyframe.time; - } - } - is_setup = true; - } - - function slide(e2) { - convertTouchEvent(e2); - e2.preventDefault(); - let offset = [ - e2.clientX - e1.clientX, - e2.clientY - e1.clientY, - ] - if (!is_setup) { - if (Math.pow(offset[0], 2) + Math.pow(offset[1], 2) > 40) { - setup(); - } else { - return; - } - } - - // Time - let difference = 0; - let max, min; - let time_factor = 1; - if ((!e2.ctrlOrCmd && !e2.shiftKey) || time_stretching || !Timeline.vue.graph_editor_open) { - difference = Math.clamp(offset[0] / Timeline.vue._data.size, -256, 256); - [min, max] = dragging_range; - - if (time_stretching) { - time_factor = (clicked && clicked.time_before < (min + max) / 2) - ? ((max-min-difference) / (max-min)) - : ((max-min+difference) / (max-min)); - time_factor = Math.roundTo(time_factor, 2); - } - } - - // Value - let value = 0; - let value_diff = 0; - if (Timeline.vue.graph_editor_open) { - value = -offset[1] / Timeline.vue.graph_size; - var round_num = canvasGridSize(e2.shiftKey || Pressing.overrides.shift, e2.ctrlOrCmd || Pressing.overrides.ctrl); - if (Toolbox.selected.id === 'resize_tool') { - round_num *= 0.1; - } - value = Math.round(value/round_num)*round_num - previousValue = previousValue == undefined ? value : previousValue; - originalValue = originalValue == undefined ? value : originalValue; - - if (value !== previousValue) { - value_diff = value - (previousValue||0); - previousValue = value; - } - } - - for (let kf of Timeline.selected) { - if (time_stretching) { - if (clicked && clicked.time_before < (min + max) / 2) { - var t = max - (kf.time_before - max) * -time_factor; - } else { - var t = min + (kf.time_before - min) * time_factor; - } - } else { - var t = kf.time_before + difference; - } - let old_time = kf.time; - if (dragging_restriction) { - let step = Timeline.getStep(); - kf.time = Timeline.snapTime(Math.clamp(t, dragging_restriction[0] + step, dragging_restriction[1] - step)); - } else { - kf.time = Timeline.snapTime(t); - } - if (old_time !== kf.time) { - values_changed = true; - } - if (time_stretching && kf.interpolation == 'bezier') { - let old_bezier = old_bezier_values[kf.uuid]; - kf.bezier_left_time.V3_set(old_bezier.left).V3_multiply(time_factor); - kf.bezier_right_time.V3_set(old_bezier.right).V3_multiply(time_factor); - } - - if (Timeline.vue.graph_editor_open && value_diff) { - kf.offset(Timeline.vue.graph_editor_axis, value_diff); - values_changed = true; - } - } - if (time_stretching) { - Blockbench.setStatusBarText(Math.round(time_factor * 100) + '%'); - } else { - let text = trimFloatNumber(Math.round(difference * Animation.selected.snapping)); - if (Timeline.vue.graph_editor_open) { - text += ` ⨉ ${trimFloatNumber(value - originalValue)}` - } - Blockbench.setStatusBarText(text); - } - BarItems.slider_keyframe_time.update() - Animator.showMotionTrail(null, true) - Animator.preview() - - } - function off() { - removeEventListeners(document, 'mousemove touchmove', slide); - removeEventListeners(document, 'mouseup touchend', off); - - if (is_setup) { - var deleted = [] - for (var kf of Timeline.selected) { - delete kf.time_before; - kf.replaceOthers(deleted); - } - Blockbench.setStatusBarText(); - if (values_changed) { - Animation.selected.setLength(); - if (time_stretching) { - Undo.finishEdit('Stretch keyframes'); - } else { - Undo.addKeyframeCasualties(deleted); - Undo.finishEdit('Drag keyframes'); - } - } else { - Undo.cancelEdit(); - } - setTimeout(() => { - Timeline.dragging_keyframes = false; - }, 20); - } - } - addEventListeners(document, 'mousemove touchmove', slide, {passive: false}); - addEventListeners(document, 'mouseup touchend', off); - }, - dragBezierHandle(clicked, side, e1) { - if (e1.button > 0) return; - convertTouchEvent(e1); - let values_changed; - let is_setup = false; - let axis_number = getAxisNumber(this.graph_editor_axis); - let old_values = {}; - let lock_direction; - - function setup(offset) { - - if (!clicked.selected && !e1.shiftKey && !Pressing.overrides.shift && Timeline.selected.length != 0) { - clicked.select() - } else if (clicked && !clicked.selected) { - clicked.select({shiftKey: true}) - } - lock_direction = Math.abs(offset[0]) > Math.abs(offset[1]); - - Keyframe.selected.forEach(kf => { - if (kf.interpolation == 'bezier') { - old_values[kf.uuid] = { - bezier_left_time: kf.bezier_left_time.slice(), - bezier_left_value: kf.bezier_left_value.slice(), - bezier_right_time: kf.bezier_right_time.slice(), - bezier_right_value: kf.bezier_right_value.slice(), - } - } - }) - - Undo.initEdit({keyframes: Timeline.selected}); - Timeline.dragging_keyframes = true; - - is_setup = true; - } - - function slide(e2) { - convertTouchEvent(e2); - e2.preventDefault(); - let offset = [ - e2.clientX - e1.clientX, - e2.clientY - e1.clientY, - ] - if (!is_setup) { - if (Math.pow(offset[0], 2) + Math.pow(offset[1], 2) > 20) { - setup(offset); - } else { - return; - } - } - let difference_time = Math.clamp(offset[0] / Timeline.vue._data.size, -256, 256); - let difference_value = Math.clamp(-offset[1] / Timeline.vue.graph_size, -256, 256); - if (e2.shiftKey || Pressing.overrides.shift) { - if (lock_direction) { - difference_value = 0; - } else { - difference_time = 0; - } - } - if (e2.ctrlOrCmd || Pressing.overrides.ctrl) { - let time_snap = Timeline.getStep(); - let val_snap = 0.25; - difference_time = Math.round(difference_time / time_snap) * time_snap; - difference_value = Math.round(difference_value / val_snap) * val_snap; - } - - for (let kf of Timeline.selected) { - if (kf.interpolation == 'bezier') { - - kf.bezier_left_time.V3_set(old_values[kf.uuid].bezier_left_time); - kf.bezier_left_value.V3_set(old_values[kf.uuid].bezier_left_value); - kf.bezier_right_time.V3_set(old_values[kf.uuid].bezier_right_time); - kf.bezier_right_value.V3_set(old_values[kf.uuid].bezier_right_value); - - if (side === 'left') { - kf.bezier_left_time[axis_number] = Math.min(0, old_values[kf.uuid].bezier_left_time[axis_number] + difference_time); - kf.bezier_left_value[axis_number] = old_values[kf.uuid].bezier_left_value[axis_number] + difference_value; - if (kf.bezier_linked) { - kf.bezier_right_time[axis_number] = -kf.bezier_left_time[axis_number]; - kf.bezier_right_value[axis_number] = -kf.bezier_left_value[axis_number]; - } - } - if (side === 'right') { - kf.bezier_right_time[axis_number] = Math.max(0, old_values[kf.uuid].bezier_right_time[axis_number] + difference_time); - kf.bezier_right_value[axis_number] = old_values[kf.uuid].bezier_right_value[axis_number] + difference_value; - if (kf.bezier_linked) { - kf.bezier_left_time[axis_number] = -kf.bezier_right_time[axis_number]; - kf.bezier_left_value[axis_number] = -kf.bezier_right_value[axis_number]; - } - } - if (kf.uniform) { - let off_axis_a = (axis_number+1) % 3; - let off_axis_b = (axis_number+2) % 3; - kf.bezier_right_time[off_axis_a] = kf.bezier_right_time[off_axis_b] = kf.bezier_right_time[axis_number]; - kf.bezier_right_value[off_axis_a] = kf.bezier_right_value[off_axis_b] = kf.bezier_right_value[axis_number]; - kf.bezier_left_time[off_axis_a] = kf.bezier_left_time[off_axis_b] = kf.bezier_left_time[axis_number]; - kf.bezier_left_value[off_axis_a] = kf.bezier_left_value[off_axis_b] = kf.bezier_left_value[axis_number]; - } - values_changed = true; - } - } - let text = `${trimFloatNumber(Math.roundTo(difference_time, 2))} ⨉ ${trimFloatNumber(Math.roundTo(difference_value, 2))}`; - Blockbench.setStatusBarText(text); - - Timeline.vue.show_zero_line = !Timeline.vue.show_zero_line; - Timeline.vue.show_zero_line = !Timeline.vue.show_zero_line; - Animator.showMotionTrail(null, true) - Animator.preview() - } - function off() { - removeEventListeners(document, 'mousemove touchmove', slide); - removeEventListeners(document, 'mouseup touchend', off); - - if (is_setup) { - Blockbench.setStatusBarText(); - if (values_changed) { - Undo.finishEdit('Adjust keyframe bezier handles'); - } else { - Undo.cancelEdit(); - } - setTimeout(() => { - Timeline.dragging_keyframes = false; - }, 20); - } - } - addEventListeners(document, 'mousemove touchmove', slide, {passive: false}); - addEventListeners(document, 'mouseup touchend', off); - }, - slideGraphAmplify(e1, anchor_side) { - if (e1.button > 0) return; - convertTouchEvent(e1); - let original_values = {}; - let values_changed; - let is_setup = false; - let keyframes = this.graph_editor_animator[this.graph_editor_channel].filter(kf => kf.selected); - let original_range = this.getSelectedGraphRange(); - let original_pixel_range = (original_range[1] - original_range[0]) * this.graph_size; - let axis = this.graph_editor_axis; - - function setup() { - Undo.initEdit({keyframes}); - values_changed = false; - Timeline.dragging_keyframes = true; - is_setup = true; - - for (let kf of keyframes) { - original_values[kf.uuid] = kf.display_value || kf.get(axis); - } - } - - function slide(e2) { - convertTouchEvent(e2); - e2.preventDefault(); - let offset = e2.clientY - e1.clientY; - if (anchor_side == 1) offset *= -1; - if (!is_setup) { - if (Math.abs(offset) > 4) { - setup(); - } else { - return; - } - } - - let value = 1 - offset / original_pixel_range; - value = Math.round(value*100)/100; - - for (let kf of keyframes) { - let origin = original_range[anchor_side]; - if (e2.altKey) { - origin = Math.lerp(original_range[0], original_range[1], 0.5); - } - let target_value = (original_values[kf.uuid] - origin) * value + origin; - kf.offset(axis, -kf.get(axis) + target_value); - values_changed = true; - } - let text = Math.round(value * 100) + '%'; - Blockbench.setStatusBarText(text); - Animator.showMotionTrail(null, true) - Animator.preview() - - } - function off() { - removeEventListeners(document, 'mousemove touchmove', slide); - removeEventListeners(document, 'mouseup touchend', off); - - if (is_setup) { - Blockbench.setStatusBarText(); - if (values_changed) { - Undo.finishEdit('Amplify keyframes'); - } else { - Undo.cancelEdit(); - } - setTimeout(() => { - Timeline.dragging_keyframes = false; - }, 20); - } - } - addEventListeners(document, 'mousemove touchmove', slide, {passive: false}); - addEventListeners(document, 'mouseup touchend', off); - }, - clickGraphEditor(event) { - if (!this.show_other_graphs || !this.graph_editor_animator) return; - let value = (this.graph_offset - event.offsetY) / this.graph_size; - let original_time = Timeline.time; - - let tryAt = (x_coord) => { - let time = (x_coord) / this.size; - Timeline.time = time; - - let distances = ['x', 'y', 'z'].map(axis => { - let axis_value = this.graph_editor_animator.interpolate(this.graph_editor_channel, false, axis); - let diff = Math.abs(axis_value - value) * this.graph_size; - if (diff < 12.5) { - return {axis, diff}; - } - }).filter(a => a); - if (distances.length) return distances; - } - - let real_x_coord = event.offsetX-8; - let distances = tryAt(real_x_coord) || tryAt(real_x_coord + 4) || tryAt(real_x_coord - 4); - - if (distances) { - distances.sort((a, b) => a.diff - b.diff); - this.graph_editor_axis = distances[0].axis; - } - Timeline.time = original_time; - }, - getBezierHandleStyle(keyframe, side) { - let axis_number = getAxisNumber(this.graph_editor_axis); - let x_offset = -keyframe[`bezier_${side}_time`][axis_number] * this.size; - let y_offset = -keyframe[`bezier_${side}_value`][axis_number] * this.graph_size; - let length = Math.sqrt(Math.pow(x_offset, 2) + Math.pow(y_offset, 2)); - let angle = Math.atan2(-y_offset, x_offset); - return { - right: x_offset + 'px', - top: y_offset + 'px', - '--length': Math.max(length - 6, 0) + 'px', - '--angle': Math.radToDeg(angle) + 'deg', - } - }, - getSelectedGraphRange() { - if (Keyframe.selected.length == 0 || !this.graph_editor_animator) return null; - let keyframes = this.graph_editor_animator[this.graph_editor_channel]; - if (!keyframes || keyframes.length < 2) return null; - let range = [Infinity, -Infinity]; - keyframes.forEach(kf => { - if (!kf.selected) return; - range[0] = Math.min(range[0], kf.display_value); - range[1] = Math.max(range[1], kf.display_value); - }) - if (range[0] == range[1]) return null; - return range; - }, - getNodeColor(node) { - if (node.color >= 0) { - return markerColors[node.color % markerColors.length].pastel; - } - return ''; - }, - clamp: Math.clamp, - Condition, - trimFloatNumber, - getAxisLetter - }, - watch: { - size() {this.updateTimecodes()}, - length() {this.updateTimecodes()}, - scroll_left() {this.updateTimecodes()}, - }, - template: ` -
-
-
-
{{ timestamp }}
- / -
{{ framenumber }}
-
-
X
-
Y
-
Z
-
-
-
-
-
-
- {{ t.text }} -
-
-
-
-
-
-
-
- beenhere -
{{ marker.name }}
-
-
-
-
-
-
-
-
-
-
-
  • -
    -
    -
    - -
    - - - - - {{animator.name}} - -
    - remove -
    -
    -
    - -
    -
    -
    -
    -
    - - {{ channel === 'sound' ? (animator.muted[channel] ? 'volume_off' : 'volume_up') : (animator.muted[channel] ? 'visibility_off' : 'visibility') }} - -
    -
    - {{ channel_options.name }} -
    - {{ animator.rotation_global ? 'public' : 'public_off' }} -
    -
    - add -
    -
    -
    -
    - - - - - - - -
    -
    -
    -
  • -
    -
    -
    -
    - - - - - - - - - - - -
    -
    -
    -
    - ` - } + component: TimelineComponent }) Timeline.vue = Timeline.panel.inside_vue; Timeline.panel.on('change_zindex', ({zindex}) => { @@ -1930,7 +857,10 @@ BARS.defineActions(function() { ) { Timeline.vue.graph_editor_channel = Timeline.selected[0].channel; } + + Timeline.vue.refreshTimelineCanvas(); } + }) new Toggle('graph_editor_other_graphs', { icon: 'exposure_zero', @@ -2138,6 +1068,7 @@ BARS.defineActions(function() { ba.addToTimeline(); } } + Timeline.vue.refreshTimelineCanvas(); } }) new Action('add_all_to_timeline', { @@ -2150,6 +1081,7 @@ BARS.defineActions(function() { let ba = Animation.selected.getBoneAnimator(node); if (ba) ba.addToTimeline(); }) + Timeline.vue.refreshTimelineCanvas(); } }) new Action('fold_all_animations', { @@ -2160,7 +1092,7 @@ BARS.defineActions(function() { for (var animator of Timeline.animators) { animator.expanded = false; } - + Timeline.vue.refreshTimelineCanvas(); } }) new Action('clear_timeline', { @@ -2169,6 +1101,7 @@ BARS.defineActions(function() { condition: {modes: ['animate']}, click: function () { Timeline.vue._data.animators.purge(); + Timeline.vue.clearTimelineCanvas(); unselectAllElements(); } }) @@ -2212,6 +1145,7 @@ BARS.defineActions(function() { click() { Vue.set(channels, id, channels[id] == false); StateMemory.save('timeline_channels'); + Timeline.vue.refreshTimelineCanvas(); } }) } @@ -2222,6 +1156,7 @@ BARS.defineActions(function() { {name: 'action.timeline_focus.hide_empty', icon: channels.hide_empty ? on : off, click() { Vue.set(channels, 'hide_empty', !channels.hide_empty); StateMemory.save('timeline_channels'); + Timeline.vue.refreshTimelineCanvas(); }}, ] } diff --git a/js/animations/timeline_animators.js b/js/animations/timeline_animators.js index 47af99ff1..e5ad1a06f 100644 --- a/js/animations/timeline_animators.js +++ b/js/animations/timeline_animators.js @@ -128,6 +128,8 @@ export class GeneralAnimator { if (undo) { Undo.finishEdit('Add keyframe') } + + Timeline.vue.refreshTimelineCanvas(); return keyframe; } getOrMakeKeyframe(channel) { diff --git a/js/util/array_util.js b/js/util/array_util.js index ec57be548..49d335805 100644 --- a/js/util/array_util.js +++ b/js/util/array_util.js @@ -218,4 +218,7 @@ Array.prototype.V2_divide = function(x, y) { this[0] /= parseFloat(x)||1; this[1] /= parseFloat(y)||1; return this; +} +Array.prototype.V2_toThree = function() { + return new THREE.Vector2(this[0], this[1]); } \ No newline at end of file diff --git a/js/util/global.d.ts b/js/util/global.d.ts index 2e7e2a107..f7f89aa89 100644 --- a/js/util/global.d.ts +++ b/js/util/global.d.ts @@ -130,6 +130,19 @@ declare global { V3_divide(scalar: number): ArrayVector3 V3_toThree(): THREE.Vector3 V3_toEuler(): THREE.Euler + + V2_set(x: number, y: number): ArrayVector2 + V2_set(values: ArrayVector2): ArrayVector2 + V2_add(x: number, y: number): ArrayVector2 + V2_add(values: ArrayVector2): ArrayVector2 + V2_subtract(x: number, y: number): ArrayVector2 + V2_subtract(values: ArrayVector2): ArrayVector2 + V2_multiply(x: number, y: number): ArrayVector2 + V2_multiply(values: ArrayVector2): ArrayVector2 + V2_divide(x: number, y: number): ArrayVector2 + V2_divide(values: ArrayVector2): ArrayVector2 + V2_toThree(): THREE.Vector2 + } interface Event {