kopia lustrzana https://github.com/backface/turtlestitch
Morphic: First Class Animations
rodzic
3c43d59bc3
commit
3d8ef5519c
228
morphic.js
228
morphic.js
|
@ -55,7 +55,8 @@
|
||||||
(7) turtle graphics
|
(7) turtle graphics
|
||||||
(8) damage list housekeeping
|
(8) damage list housekeeping
|
||||||
(9) supporting high-resolution "retina" screens
|
(9) supporting high-resolution "retina" screens
|
||||||
(10) minifying morphic.js
|
(10 animations
|
||||||
|
(11) minifying morphic.js
|
||||||
VIII. acknowledgements
|
VIII. acknowledgements
|
||||||
IX. contributors
|
IX. contributors
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@
|
||||||
indentation indicating inheritance. Refer to this list to get a
|
indentation indicating inheritance. Refer to this list to get a
|
||||||
contextual overview:
|
contextual overview:
|
||||||
|
|
||||||
|
Animation
|
||||||
Color
|
Color
|
||||||
Node
|
Node
|
||||||
Morph
|
Morph
|
||||||
|
@ -108,6 +110,7 @@
|
||||||
Global settings
|
Global settings
|
||||||
Global functions
|
Global functions
|
||||||
|
|
||||||
|
Animation
|
||||||
Color
|
Color
|
||||||
Point
|
Point
|
||||||
Rectangle
|
Rectangle
|
||||||
|
@ -1035,7 +1038,37 @@
|
||||||
stage (high-resolution) into a sprite-costume (normal resolution).
|
stage (high-resolution) into a sprite-costume (normal resolution).
|
||||||
|
|
||||||
|
|
||||||
(10) minifying morphic.js
|
(10) animations
|
||||||
|
---------------
|
||||||
|
Animations handle gradual transitions between one state and another over a
|
||||||
|
period of time. Transition effects can be specified using easing functions.
|
||||||
|
An easing function maps a fraction of the transition time to a fraction of
|
||||||
|
the state delta. This way accelerating / decelerating and bouncing sliding
|
||||||
|
effects can be accomplished.
|
||||||
|
|
||||||
|
Animations are generic and not limited to motion, i.e. they can also handle
|
||||||
|
other transitions such as color changes, transparency fadings, growing,
|
||||||
|
shrinking, turning etc.
|
||||||
|
|
||||||
|
Animations need to be stepped by a scheduler, e. g. an interval function.
|
||||||
|
In Morphic the preferred way to run an animation is to register it with
|
||||||
|
the World by adding it to the World's animation queue. The World steps each
|
||||||
|
registered animation once per display cycle independently of the Morphic
|
||||||
|
stepping mechanism.
|
||||||
|
|
||||||
|
For an example how to use animations look at how the Morph's methods
|
||||||
|
|
||||||
|
glideTo()
|
||||||
|
fadeTo()
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
slideBackTo()
|
||||||
|
|
||||||
|
are implemented.
|
||||||
|
|
||||||
|
|
||||||
|
(11) minifying morphic.js
|
||||||
-------------------------
|
-------------------------
|
||||||
Coming from Smalltalk and being a Squeaker at heart I am a huge fan
|
Coming from Smalltalk and being a Squeaker at heart I am a huge fan
|
||||||
of browsing the code itself to make sense of it. Therefore I have
|
of browsing the code itself to make sense of it. Therefore I have
|
||||||
|
@ -1103,7 +1136,7 @@
|
||||||
|
|
||||||
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
|
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
|
||||||
|
|
||||||
var morphicVersion = '2016-November-24';
|
var morphicVersion = '2016-November-25';
|
||||||
var modules = {}; // keep track of additional loaded modules
|
var modules = {}; // keep track of additional loaded modules
|
||||||
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
|
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
|
||||||
|
|
||||||
|
@ -1732,6 +1765,95 @@ function normalizeCanvas(aCanvas, getCopy) {
|
||||||
return aCanvas;
|
return aCanvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animations //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
Animations handle gradual transitions between one state and another over a
|
||||||
|
period of time. Transition effects can be specified using easing functions.
|
||||||
|
An easing function maps a fraction of the transition time to a fraction of
|
||||||
|
the state delta. This way accelerating / decelerating and bouncing sliding
|
||||||
|
effects can be accomplished.
|
||||||
|
|
||||||
|
Animations are generic and not limited to motion, i.e. they can also handle
|
||||||
|
other transitions such as color changes, transparency fadings, growing,
|
||||||
|
shrinking, turning etc.
|
||||||
|
|
||||||
|
Animations need to be stepped by a scheduler, e. g. an interval function.
|
||||||
|
In Morphic the preferred way to run an animation is to register it with
|
||||||
|
the World by adding it to the World's animation queue. The World steps each
|
||||||
|
registered animation once per display cycle independently of the Morphic
|
||||||
|
stepping mechanism.
|
||||||
|
|
||||||
|
For an example how to use animations look at how the Morph's methods
|
||||||
|
|
||||||
|
glideTo()
|
||||||
|
fadeTo()
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
slideBackTo()
|
||||||
|
|
||||||
|
are implemented.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Animation instance creation:
|
||||||
|
|
||||||
|
function Animation(setter, getter, delta, duration, easing, onComplete) {
|
||||||
|
this.setter = setter; // function
|
||||||
|
this.getter = getter; // function
|
||||||
|
this.delta = delta || 0; // number
|
||||||
|
this.duration = duration || 0; // milliseconds
|
||||||
|
this.easing = isString(easing) ? // string or function
|
||||||
|
this.easings[easing] || this.easings.sinusoidal
|
||||||
|
: easing || this.easings.sinusoidal;
|
||||||
|
this.onComplete = onComplete || null; // optional callback
|
||||||
|
this.endTime = null;
|
||||||
|
this.destination = null;
|
||||||
|
this.isActive = false;
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation.prototype.easings = {
|
||||||
|
// dictionary of a few pre-defined easing functions used to transgress the
|
||||||
|
//
|
||||||
|
linear: function (t) {return t; },
|
||||||
|
sinusoidal: function (t) {return 1 - Math.cos(radians(t * 90)); },
|
||||||
|
quadratic: function (t) {
|
||||||
|
return t < 0.5 ?
|
||||||
|
2 * t * t
|
||||||
|
: ((4 - (2 * t)) * t) - 1;
|
||||||
|
},
|
||||||
|
cubic: function (t) {
|
||||||
|
return t < 0.5 ?
|
||||||
|
4 * t * t * t
|
||||||
|
: ((t - 1) * ((2 * t) - 2) * ((2 * t) - 2)) + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Animation.prototype.start = function () {
|
||||||
|
// (re-) activate the animation, e.g. if is has previously completed,
|
||||||
|
// make sure to plug it into something that repeatedly triggers step(),
|
||||||
|
// e.g. the World's animations queue
|
||||||
|
this.endTime = Date.now() + this.duration;
|
||||||
|
this.destination = this.getter.call(this) + this.delta;
|
||||||
|
this.isActive = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Animation.prototype.step = function () {
|
||||||
|
if (!this.isActive) {return; }
|
||||||
|
var now = Date.now();
|
||||||
|
if (now > this.endTime) {
|
||||||
|
this.setter(this.destination);
|
||||||
|
this.isActive = false;
|
||||||
|
if (this.onComplete) {this.onComplete(); }
|
||||||
|
} else {
|
||||||
|
this.setter(
|
||||||
|
this.destination -
|
||||||
|
(this.delta * this.easing((this.endTime - now) / this.duration))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Colors //////////////////////////////////////////////////////////////
|
// Colors //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Color instance creation:
|
// Color instance creation:
|
||||||
|
@ -3613,31 +3735,88 @@ Morph.prototype.situation = function () {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
Morph.prototype.slideBackTo = function (situation, inSteps, onBeforeDrop) {
|
Morph.prototype.slideBackTo = function (
|
||||||
var steps = inSteps || 5,
|
situation,
|
||||||
pos = situation.origin.position().add(situation.position),
|
msecs,
|
||||||
xStep = -(this.left() - pos.x) / steps,
|
onBeforeDrop,
|
||||||
yStep = -(this.top() - pos.y) / steps,
|
onComplete
|
||||||
stepCount = 0,
|
) {
|
||||||
oldStep = this.step,
|
var pos = situation.origin.position().add(situation.position),
|
||||||
oldFps = this.fps,
|
|
||||||
myself = this;
|
myself = this;
|
||||||
|
this.glideTo(
|
||||||
this.fps = 0;
|
pos,
|
||||||
this.step = function () {
|
msecs,
|
||||||
myself.moveBy(new Point(xStep, yStep));
|
null, // easing
|
||||||
stepCount += 1;
|
function () {
|
||||||
if (stepCount === steps) {
|
|
||||||
situation.origin.add(myself);
|
situation.origin.add(myself);
|
||||||
if (onBeforeDrop) {onBeforeDrop(); }
|
if (onBeforeDrop) {onBeforeDrop(); }
|
||||||
if (myself.justDropped) {myself.justDropped(); }
|
if (myself.justDropped) {myself.justDropped(); }
|
||||||
if (situation.origin.reactToDropOf) {
|
if (situation.origin.reactToDropOf) {
|
||||||
situation.origin.reactToDropOf(myself);
|
situation.origin.reactToDropOf(myself);
|
||||||
}
|
}
|
||||||
myself.step = oldStep;
|
if (onComplete) {onComplete(); }
|
||||||
myself.fps = oldFps;
|
|
||||||
}
|
}
|
||||||
};
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Morph animating:
|
||||||
|
|
||||||
|
Morph.prototype.glideTo = function (endPoint, msecs, easing, onComplete) {
|
||||||
|
var world = this.world(),
|
||||||
|
myself = this;
|
||||||
|
world.animations.push(new Animation(
|
||||||
|
function (x) {myself.setLeft(x); },
|
||||||
|
function () {return myself.left(); },
|
||||||
|
-(this.left() - endPoint.x),
|
||||||
|
msecs || 100,
|
||||||
|
easing,
|
||||||
|
onComplete
|
||||||
|
));
|
||||||
|
world.animations.push(new Animation(
|
||||||
|
function (y) {myself.setTop(y); },
|
||||||
|
function () {return myself.top(); },
|
||||||
|
-(this.top() - endPoint.y),
|
||||||
|
msecs || 100,
|
||||||
|
easing
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Morph.prototype.fadeTo = function (endAlpha, msecs, easing, onComplete) {
|
||||||
|
// include all my children, restore all original transparencies
|
||||||
|
// on completion, so I can be recovered
|
||||||
|
var world = this.world(),
|
||||||
|
myself = this,
|
||||||
|
oldAlpha = this.alpha;
|
||||||
|
this.children.forEach(function (child) {
|
||||||
|
child.fadeTo(endAlpha, msecs, easing);
|
||||||
|
});
|
||||||
|
world.animations.push(new Animation(
|
||||||
|
function (n) {
|
||||||
|
myself.alpha = n;
|
||||||
|
myself.changed();
|
||||||
|
},
|
||||||
|
function () {return myself.alpha; },
|
||||||
|
endAlpha - this.alpha,
|
||||||
|
msecs || 200,
|
||||||
|
easing,
|
||||||
|
function () {
|
||||||
|
myself.alpha = oldAlpha;
|
||||||
|
if (onComplete) {onComplete(); }
|
||||||
|
}
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Morph.prototype.perish = function (msecs, onComplete) {
|
||||||
|
var myself = this;
|
||||||
|
this.fadeTo(
|
||||||
|
0,
|
||||||
|
msecs || 100,
|
||||||
|
null,
|
||||||
|
function () {
|
||||||
|
myself.destroy();
|
||||||
|
if (onComplete) {onComplete(); }
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Morph utilities:
|
// Morph utilities:
|
||||||
|
@ -10764,6 +10943,7 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) {
|
||||||
}
|
}
|
||||||
this.isDevMode = false;
|
this.isDevMode = false;
|
||||||
this.broken = [];
|
this.broken = [];
|
||||||
|
this.animations = [];
|
||||||
this.hand = new HandMorph(this);
|
this.hand = new HandMorph(this);
|
||||||
this.keyboardReceiver = null;
|
this.keyboardReceiver = null;
|
||||||
this.cursor = null;
|
this.cursor = null;
|
||||||
|
@ -10801,6 +10981,13 @@ WorldMorph.prototype.updateBroken = function () {
|
||||||
this.broken = [];
|
this.broken = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WorldMorph.prototype.stepAnimations = function () {
|
||||||
|
this.animations.forEach(function (anim) {anim.step(); });
|
||||||
|
this.animations = this.animations.filter(function (anim) {
|
||||||
|
return anim.isActive;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
WorldMorph.prototype.condenseDamages = function () {
|
WorldMorph.prototype.condenseDamages = function () {
|
||||||
// collapse clustered damaged rectangles into their unions,
|
// collapse clustered damaged rectangles into their unions,
|
||||||
// thereby reducing the array of brokens to a manageable size
|
// thereby reducing the array of brokens to a manageable size
|
||||||
|
@ -10831,6 +11018,7 @@ WorldMorph.prototype.condenseDamages = function () {
|
||||||
|
|
||||||
WorldMorph.prototype.doOneCycle = function () {
|
WorldMorph.prototype.doOneCycle = function () {
|
||||||
this.stepFrame();
|
this.stepFrame();
|
||||||
|
this.stepAnimations();
|
||||||
this.updateBroken();
|
this.updateBroken();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
39
morphic.txt
39
morphic.txt
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
Copyright (C) 2016 by Jens Mönig
|
Copyright (C) 2016 by Jens Mönig
|
||||||
|
|
||||||
this documentation last changed: July 14, 2016
|
this documentation last changed: November 25, 2016
|
||||||
|
|
||||||
This file is part of Snap!.
|
This file is part of Snap!.
|
||||||
|
|
||||||
|
@ -56,7 +56,8 @@
|
||||||
(7) turtle graphics
|
(7) turtle graphics
|
||||||
(8) damage list housekeeping
|
(8) damage list housekeeping
|
||||||
(9) supporting high-resolution "retina" screens
|
(9) supporting high-resolution "retina" screens
|
||||||
(10) minifying morphic.js
|
(10 animations
|
||||||
|
(11) minifying morphic.js
|
||||||
VIII. acknowledgements
|
VIII. acknowledgements
|
||||||
IX. contributors
|
IX. contributors
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
indentation indicating inheritance. Refer to this list to get a
|
indentation indicating inheritance. Refer to this list to get a
|
||||||
contextual overview:
|
contextual overview:
|
||||||
|
|
||||||
|
Animation
|
||||||
Color
|
Color
|
||||||
Node
|
Node
|
||||||
Morph
|
Morph
|
||||||
|
@ -109,6 +111,7 @@
|
||||||
Global settings
|
Global settings
|
||||||
Global functions
|
Global functions
|
||||||
|
|
||||||
|
Animation
|
||||||
Color
|
Color
|
||||||
Point
|
Point
|
||||||
Rectangle
|
Rectangle
|
||||||
|
@ -1036,7 +1039,37 @@
|
||||||
stage (high-resolution) into a sprite-costume (normal resolution).
|
stage (high-resolution) into a sprite-costume (normal resolution).
|
||||||
|
|
||||||
|
|
||||||
(10) minifying morphic.js
|
(10) animations
|
||||||
|
---------------
|
||||||
|
Animations handle gradual transitions between one state and another over a
|
||||||
|
period of time. Transition effects can be specified using easing functions.
|
||||||
|
An easing function maps a fraction of the transition time to a fraction of
|
||||||
|
the state delta. This way accelerating / decelerating and bouncing sliding
|
||||||
|
effects can be accomplished.
|
||||||
|
|
||||||
|
Animations are generic and not limited to motion, i.e. they can also handle
|
||||||
|
other transitions such as color changes, transparency fadings, growing,
|
||||||
|
shrinking, turning etc.
|
||||||
|
|
||||||
|
Animations need to be stepped by a scheduler, e. g. an interval function.
|
||||||
|
In Morphic the preferred way to run an animation is to register it with
|
||||||
|
the World by adding it to the World's animation queue. The World steps each
|
||||||
|
registered animation once per display cycle independently of the Morphic
|
||||||
|
stepping mechanism.
|
||||||
|
|
||||||
|
For an example how to use animations look at how the Morph's methods
|
||||||
|
|
||||||
|
glideTo()
|
||||||
|
fadeTo()
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
slideBackTo()
|
||||||
|
|
||||||
|
are implemented.
|
||||||
|
|
||||||
|
|
||||||
|
(11) minifying morphic.js
|
||||||
-------------------------
|
-------------------------
|
||||||
Coming from Smalltalk and being a Squeaker at heart I am a huge fan
|
Coming from Smalltalk and being a Squeaker at heart I am a huge fan
|
||||||
of browsing the code itself to make sense of it. Therefore I have
|
of browsing the code itself to make sense of it. Therefore I have
|
||||||
|
|
Ładowanie…
Reference in New Issue