mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-11-27 04:21:46 +08:00
Add blend transition curve generator
This commit is contained in:
parent
06fd67e0fe
commit
50c8358738
@ -89,6 +89,7 @@
|
||||
<script src="lib/fik.min.js"></script>
|
||||
<script src="lib/molang.umd.js"></script>
|
||||
<script src="lib/wintersky.umd.js"></script>
|
||||
<script src="lib/easing.js"></script>
|
||||
|
||||
<script src="bundle.js"></script>
|
||||
<script src="js/preview/OrbitControls.js"></script>
|
||||
|
@ -196,20 +196,22 @@ class AnimationControllerState {
|
||||
return new oneLiner({[state ? state.name : 'missing_state']: condition})
|
||||
})
|
||||
}
|
||||
if (this.blend_transition) object.blend_transition = this.blend_transition;
|
||||
let curve_keys = this.blend_transition_curve && Object.keys(this.blend_transition_curve);
|
||||
if (curve_keys?.length) {
|
||||
let curve_output = {};
|
||||
let points = curve_keys.map(key => ({time: parseFloat(key), value: this.blend_transition_curve[key]}));
|
||||
points.sort((a, b) => a.time - b.time);
|
||||
for (let point of points) {
|
||||
let timecode = trimFloatNumber(point.time * this.blend_transition, 4).toString();
|
||||
if (!timecode.includes('.')) timecode += '.0';
|
||||
curve_output[timecode] = Math.roundTo(point.value, 6);
|
||||
if (this.blend_transition) {
|
||||
object.blend_transition = this.blend_transition;
|
||||
let curve_keys = this.blend_transition_curve && Object.keys(this.blend_transition_curve);
|
||||
if (curve_keys?.length) {
|
||||
let curve_output = {};
|
||||
let points = curve_keys.map(key => ({time: parseFloat(key), value: this.blend_transition_curve[key]}));
|
||||
points.sort((a, b) => a.time - b.time);
|
||||
for (let point of points) {
|
||||
let timecode = trimFloatNumber(point.time * this.blend_transition, 4).toString();
|
||||
if (!timecode.includes('.')) timecode += '.0';
|
||||
curve_output[timecode] = Math.roundTo(point.value, 6);
|
||||
}
|
||||
object.blend_transition = curve_output;
|
||||
}
|
||||
object.blend_transition = curve_output;
|
||||
if (this.blend_via_shortest_path) object.blend_via_shortest_path = this.blend_via_shortest_path;
|
||||
}
|
||||
if (this.blend_via_shortest_path) object.blend_via_shortest_path = this.blend_via_shortest_path;
|
||||
Blockbench.dispatchEvent('compile_bedrock_animation_controller_state', {state: this, json: object});
|
||||
return object;
|
||||
}
|
||||
@ -475,19 +477,73 @@ class AnimationControllerState {
|
||||
'generic.reset',
|
||||
tl('dialog.blend_transition_edit.ease_in_out', [6]),
|
||||
tl('dialog.blend_transition_edit.ease_in_out', [10]),
|
||||
tl('dialog.blend_transition_edit.ease_in_out', [16])
|
||||
'dialog.blend_transition_edit.generate',
|
||||
],
|
||||
click(index) {
|
||||
let point_amount = ([2, 6, 10, 16])[index];
|
||||
function hermiteBlend(t) {
|
||||
return 3*(t**2) - 2*(t**3);
|
||||
function generate(easing, point_amount) {
|
||||
points.empty();
|
||||
for (let i = 0; i < point_amount; i++) {
|
||||
let time = i / (point_amount-1);
|
||||
points.push({time, value: 1-easing(time), uuid: guid()})
|
||||
}
|
||||
dialog.content_vue.updateGraph();
|
||||
}
|
||||
points.empty();
|
||||
for (let i = 0; i < point_amount; i++) {
|
||||
let time = i / (point_amount-1);
|
||||
points.push({time, value: 1-hermiteBlend(time), uuid: guid()})
|
||||
if (index == 3) {
|
||||
let easings = {
|
||||
easeInSine: 'In Sine',
|
||||
easeOutSine: 'Out Sine',
|
||||
easeInOutSine: 'In Out Sine',
|
||||
easeInQuad: 'In Quad',
|
||||
easeOutQuad: 'Out Quad',
|
||||
easeInOutQuad: 'In Out Quad',
|
||||
easeInCubic: 'In Cubic',
|
||||
easeOutCubic: 'Out Cubic',
|
||||
easeInOutCubic: 'In Out Cubic',
|
||||
easeInQuart: 'In Quart',
|
||||
easeOutQuart: 'Out Quart',
|
||||
easeInOutQuart: 'In Out Quart',
|
||||
easeInQuint: 'In Quint',
|
||||
easeOutQuint: 'Out Quint',
|
||||
easeInOutQuint: 'In Out Quint',
|
||||
easeInExpo: 'In Expo',
|
||||
easeOutExpo: 'Out Expo',
|
||||
easeInOutExpo: 'In Out Expo',
|
||||
easeInCirc: 'In Circ',
|
||||
easeOutCirc: 'Out Circ',
|
||||
easeInOutCirc: 'In Out Circ',
|
||||
easeInBack: 'In Back',
|
||||
easeOutBack: 'Out Back',
|
||||
easeInOutBack: 'In Out Back',
|
||||
easeInElastic: 'In Elastic',
|
||||
easeOutElastic: 'Out Elastic',
|
||||
easeInOutElastic: 'In Out Elastic',
|
||||
easeOutBounce: 'Out Bounce',
|
||||
easeInBounce: 'In Bounce',
|
||||
easeInOutBounce: 'In Out Bounce',
|
||||
};
|
||||
new Dialog('blend_transition_edit_easing', {
|
||||
title: 'dialog.blend_transition_edit.generate',
|
||||
width: 380,
|
||||
form: {
|
||||
easings: {type: 'info', text: tl('dialog.blend_transition_edit.generate.learn_more') + ': [easings.net](https://easings.net)'},
|
||||
curve: {type: 'select', label: 'dialog.blend_transition_edit.generate.curve', options: easings},
|
||||
steps: {type: 'number', label: 'dialog.blend_transition_edit.generate.steps', value: 10, step: 1, min: 3, max: 64}
|
||||
},
|
||||
onConfirm(result) {
|
||||
console.log(result);
|
||||
let easing_func = Easings[result.curve];
|
||||
generate(easing_func, result.steps);
|
||||
}
|
||||
}).show();
|
||||
|
||||
} else {
|
||||
let point_amount = ([2, 6, 10])[index];
|
||||
function hermiteBlend(t) {
|
||||
return 3*(t**2) - 2*(t**3);
|
||||
}
|
||||
generate(hermiteBlend, point_amount);
|
||||
}
|
||||
dialog.content_vue.updateGraph();
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -562,7 +618,13 @@ class AnimationControllerState {
|
||||
},
|
||||
preview() {
|
||||
if (this.points.length == 0) return 0;
|
||||
let time = (((performance.now() - preview_loop_start_time) / 1000) / this.duration) % 1;
|
||||
let pause = 0.4;
|
||||
let absolute_time = ((performance.now() - preview_loop_start_time) / 1000);
|
||||
let time = (absolute_time % (this.duration + pause)) / this.duration;
|
||||
if (time > 1) {
|
||||
this.preview_value = 0;
|
||||
return;
|
||||
}
|
||||
let prev_time = -Infinity, prev = 0;
|
||||
let next_time = Infinity, next = 0;
|
||||
for (let pt of points) {
|
||||
|
@ -116,6 +116,7 @@ BARS.defineActions(() => {
|
||||
<li><a class="open-in-browser" href="https://jqueryui.com">jQuery UI</a></li>
|
||||
<li><a class="open-in-browser" href="https://github.com/furf/jquery-ui-touch-punch">jQuery UI Touch Punch</a></li>
|
||||
<li><a class="open-in-browser" href="https://github.com/eligrey/FileSaver.js">FileSaver.js</a></li>
|
||||
<li><a class="open-in-browser" href="https://github.com/AndrewRayCode/easing-utils">easing-utils</a></li>
|
||||
<li><a class="open-in-browser" href="https://peerjs.com">PeerJS</a></li>
|
||||
<li><a class="open-in-browser" href="https://github.com/markedjs/marked">Marked</a></li>
|
||||
<li><a class="open-in-browser" href="https://github.com/cure53/DOMPurify">DOMPurify</a></li>
|
||||
|
@ -627,6 +627,10 @@
|
||||
"dialog.merge_animation.merge_target": "Merge into",
|
||||
|
||||
"dialog.blend_transition_edit.ease_in_out": "Ease-in-out (%0)",
|
||||
"dialog.blend_transition_edit.generate": "Generate...",
|
||||
"dialog.blend_transition_edit.generate.learn_more": "More info and preview",
|
||||
"dialog.blend_transition_edit.generate.curve": "Easing Type",
|
||||
"dialog.blend_transition_edit.generate.steps": "Keyframes",
|
||||
|
||||
"dialog.create_texture.folder": "Folder",
|
||||
"dialog.create_texture.type": "Type",
|
||||
|
307
lib/easing.js
Normal file
307
lib/easing.js
Normal file
@ -0,0 +1,307 @@
|
||||
// Based on https://gist.github.com/gre/1650294
|
||||
class Easings {
|
||||
// No easing, no acceleration
|
||||
static linear( t ) {
|
||||
return t;
|
||||
}
|
||||
|
||||
// Slight acceleration from zero to full speed
|
||||
static easeInSine( t ) {
|
||||
return -1 * Math.cos( t * ( Math.PI / 2 ) ) + 1;
|
||||
}
|
||||
|
||||
// Slight deceleration at the end
|
||||
static easeOutSine( t ) {
|
||||
return Math.sin( t * ( Math.PI / 2 ) );
|
||||
}
|
||||
|
||||
// Slight acceleration at beginning and slight deceleration at end
|
||||
static easeInOutSine( t ) {
|
||||
return -0.5 * ( Math.cos( Math.PI * t ) - 1 );
|
||||
}
|
||||
|
||||
// Accelerating from zero velocity
|
||||
static easeInQuad( t ) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
// Decelerating to zero velocity
|
||||
static easeOutQuad( t ) {
|
||||
return t * ( 2 - t );
|
||||
}
|
||||
|
||||
// Acceleration until halfway, then deceleration
|
||||
static easeInOutQuad( t ) {
|
||||
return t < 0.5 ? 2 * t * t : - 1 + ( 4 - 2 * t ) * t;
|
||||
}
|
||||
|
||||
// Accelerating from zero velocity
|
||||
static easeInCubic( t ) {
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
// Decelerating to zero velocity
|
||||
static easeOutCubic( t ) {
|
||||
const t1 = t - 1;
|
||||
return t1 * t1 * t1 + 1;
|
||||
}
|
||||
|
||||
// Acceleration until halfway, then deceleration
|
||||
static easeInOutCubic( t ) {
|
||||
return t < 0.5 ? 4 * t * t * t : ( t - 1 ) * ( 2 * t - 2 ) * ( 2 * t - 2 ) + 1;
|
||||
}
|
||||
|
||||
// Accelerating from zero velocity
|
||||
static easeInQuart( t ) {
|
||||
return t * t * t * t;
|
||||
}
|
||||
|
||||
// Decelerating to zero velocity
|
||||
static easeOutQuart( t ) {
|
||||
const t1 = t - 1;
|
||||
return 1 - t1 * t1 * t1 * t1;
|
||||
}
|
||||
|
||||
// Acceleration until halfway, then deceleration
|
||||
static easeInOutQuart( t ) {
|
||||
const t1 = t - 1;
|
||||
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * t1 * t1 * t1 * t1;
|
||||
}
|
||||
|
||||
// Accelerating from zero velocity
|
||||
static easeInQuint( t ) {
|
||||
return t * t * t * t * t;
|
||||
}
|
||||
|
||||
// Decelerating to zero velocity
|
||||
static easeOutQuint( t ) {
|
||||
const t1 = t - 1;
|
||||
return 1 + t1 * t1 * t1 * t1 * t1;
|
||||
}
|
||||
|
||||
// Acceleration until halfway, then deceleration
|
||||
static easeInOutQuint( t ) {
|
||||
const t1 = t - 1;
|
||||
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * t1 * t1 * t1 * t1 * t1;
|
||||
}
|
||||
|
||||
// Accelerate exponentially until finish
|
||||
static easeInExpo( t ) {
|
||||
|
||||
if( t === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.pow( 2, 10 * ( t - 1 ) );
|
||||
|
||||
}
|
||||
|
||||
// Initial exponential acceleration slowing to stop
|
||||
static easeOutExpo( t ) {
|
||||
|
||||
if( t === 1 ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ( -Math.pow( 2, -10 * t ) + 1 );
|
||||
|
||||
}
|
||||
|
||||
// Exponential acceleration and deceleration
|
||||
static easeInOutExpo( t ) {
|
||||
|
||||
if( t === 0 || t === 1 ) {
|
||||
return t;
|
||||
}
|
||||
|
||||
const scaledTime = t * 2;
|
||||
const scaledTime1 = scaledTime - 1;
|
||||
|
||||
if( scaledTime < 1 ) {
|
||||
return 0.5 * Math.pow( 2, 10 * ( scaledTime1 ) );
|
||||
}
|
||||
|
||||
return 0.5 * ( -Math.pow( 2, -10 * scaledTime1 ) + 2 );
|
||||
|
||||
}
|
||||
|
||||
// Increasing velocity until stop
|
||||
static easeInCirc( t ) {
|
||||
|
||||
const scaledTime = t / 1;
|
||||
return -1 * ( Math.sqrt( 1 - scaledTime * t ) - 1 );
|
||||
|
||||
}
|
||||
|
||||
// Start fast, decreasing velocity until stop
|
||||
static easeOutCirc( t ) {
|
||||
|
||||
const t1 = t - 1;
|
||||
return Math.sqrt( 1 - t1 * t1 );
|
||||
|
||||
}
|
||||
|
||||
// Fast increase in velocity, fast decrease in velocity
|
||||
static easeInOutCirc( t ) {
|
||||
|
||||
const scaledTime = t * 2;
|
||||
const scaledTime1 = scaledTime - 2;
|
||||
|
||||
if( scaledTime < 1 ) {
|
||||
return -0.5 * ( Math.sqrt( 1 - scaledTime * scaledTime ) - 1 );
|
||||
}
|
||||
|
||||
return 0.5 * ( Math.sqrt( 1 - scaledTime1 * scaledTime1 ) + 1 );
|
||||
|
||||
}
|
||||
|
||||
// Slow movement backwards then fast snap to finish
|
||||
static easeInBack( t, magnitude = 1.70158 ) {
|
||||
|
||||
return t * t * ( ( magnitude + 1 ) * t - magnitude );
|
||||
|
||||
}
|
||||
|
||||
// Fast snap to backwards point then slow resolve to finish
|
||||
static easeOutBack( t, magnitude = 1.70158 ) {
|
||||
|
||||
const scaledTime = ( t / 1 ) - 1;
|
||||
|
||||
return (
|
||||
scaledTime * scaledTime * ( ( magnitude + 1 ) * scaledTime + magnitude )
|
||||
) + 1;
|
||||
|
||||
}
|
||||
|
||||
// Slow movement backwards, fast snap to past finish, slow resolve to finish
|
||||
static easeInOutBack( t, magnitude = 1.70158 ) {
|
||||
|
||||
const scaledTime = t * 2;
|
||||
const scaledTime2 = scaledTime - 2;
|
||||
|
||||
const s = magnitude * 1.525;
|
||||
|
||||
if( scaledTime < 1) {
|
||||
|
||||
return 0.5 * scaledTime * scaledTime * (
|
||||
( ( s + 1 ) * scaledTime ) - s
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return 0.5 * (
|
||||
scaledTime2 * scaledTime2 * ( ( s + 1 ) * scaledTime2 + s ) + 2
|
||||
);
|
||||
|
||||
}
|
||||
// Bounces slowly then quickly to finish
|
||||
static easeInElastic( t, magnitude = 0.7 ) {
|
||||
|
||||
if( t === 0 || t === 1 ) {
|
||||
return t;
|
||||
}
|
||||
|
||||
const scaledTime = t / 1;
|
||||
const scaledTime1 = scaledTime - 1;
|
||||
|
||||
const p = 1 - magnitude;
|
||||
const s = p / ( 2 * Math.PI ) * Math.asin( 1 );
|
||||
|
||||
return -(
|
||||
Math.pow( 2, 10 * scaledTime1 ) *
|
||||
Math.sin( ( scaledTime1 - s ) * ( 2 * Math.PI ) / p )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// Fast acceleration, bounces to zero
|
||||
static easeOutElastic( t, magnitude = 0.7 ) {
|
||||
|
||||
if( t === 0 || t === 1 ) {
|
||||
return t;
|
||||
}
|
||||
|
||||
const p = 1 - magnitude;
|
||||
const scaledTime = t * 2;
|
||||
|
||||
const s = p / ( 2 * Math.PI ) * Math.asin( 1 );
|
||||
return (
|
||||
Math.pow( 2, -10 * scaledTime ) *
|
||||
Math.sin( ( scaledTime - s ) * ( 2 * Math.PI ) / p )
|
||||
) + 1;
|
||||
|
||||
}
|
||||
|
||||
// Slow start and end, two bounces sandwich a fast motion
|
||||
static easeInOutElastic( t, magnitude = 0.65 ) {
|
||||
|
||||
if( t === 0 || t === 1 ) {
|
||||
return t;
|
||||
}
|
||||
|
||||
const p = 1 - magnitude;
|
||||
const scaledTime = t * 2;
|
||||
const scaledTime1 = scaledTime - 1;
|
||||
|
||||
const s = p / ( 2 * Math.PI ) * Math.asin( 1 );
|
||||
|
||||
if( scaledTime < 1 ) {
|
||||
return -0.5 * (
|
||||
Math.pow( 2, 10 * scaledTime1 ) *
|
||||
Math.sin( ( scaledTime1 - s ) * ( 2 * Math.PI ) / p )
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
Math.pow( 2, -10 * scaledTime1 ) *
|
||||
Math.sin( ( scaledTime1 - s ) * ( 2 * Math.PI ) / p ) * 0.5
|
||||
) + 1;
|
||||
|
||||
}
|
||||
|
||||
// Bounce to completion
|
||||
static easeOutBounce( t ) {
|
||||
|
||||
const scaledTime = t / 1;
|
||||
|
||||
if( scaledTime < ( 1 / 2.75 ) ) {
|
||||
|
||||
return 7.5625 * scaledTime * scaledTime;
|
||||
|
||||
} else if( scaledTime < ( 2 / 2.75 ) ) {
|
||||
|
||||
const scaledTime2 = scaledTime - ( 1.5 / 2.75 );
|
||||
return ( 7.5625 * scaledTime2 * scaledTime2 ) + 0.75;
|
||||
|
||||
} else if( scaledTime < ( 2.5 / 2.75 ) ) {
|
||||
|
||||
const scaledTime2 = scaledTime - ( 2.25 / 2.75 );
|
||||
return ( 7.5625 * scaledTime2 * scaledTime2 ) + 0.9375;
|
||||
|
||||
} else {
|
||||
|
||||
const scaledTime2 = scaledTime - ( 2.625 / 2.75 );
|
||||
return ( 7.5625 * scaledTime2 * scaledTime2 ) + 0.984375;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Bounce increasing in velocity until completion
|
||||
static easeInBounce( t ) {
|
||||
return 1 - Easings.easeOutBounce( 1 - t );
|
||||
}
|
||||
|
||||
// Bounce in and bounce out
|
||||
static easeInOutBounce( t ) {
|
||||
|
||||
if( t < 0.5 ) {
|
||||
|
||||
return Easings.easeInBounce( t * 2 ) * 0.5;
|
||||
|
||||
}
|
||||
|
||||
return ( Easings.easeOutBounce( ( t * 2 ) - 1 ) * 0.5 ) + 0.5;
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user