2017-05-09 19:36:29 +00:00
|
|
|
var GetValue = require('../utils/object/GetValue');
|
2017-05-10 01:25:46 +00:00
|
|
|
var GetEaseFunction = require('./GetEaseFunction');
|
|
|
|
var CloneObject = require('../utils/object/Clone');
|
2017-05-10 03:30:00 +00:00
|
|
|
var MergeRight = require('../utils/object/MergeRight');
|
2017-05-09 19:36:29 +00:00
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
// var RESERVED = [ 'targets', 'ease', 'duration', 'yoyo', 'repeat', 'loop', 'paused', 'useFrames', 'offset' ];
|
2017-05-09 23:43:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
The following are all the same
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
x: 200,
|
|
|
|
duration: 2000,
|
|
|
|
ease: 'Power1',
|
|
|
|
yoyo: true
|
|
|
|
});
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
props: {
|
|
|
|
x: 200
|
|
|
|
}
|
|
|
|
duration: 2000,
|
|
|
|
ease: 'Power1',
|
|
|
|
yoyo: true
|
|
|
|
});
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
x: { value: 200, duration: 2000, ease: 'Power1', yoyo: true }
|
|
|
|
});
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
props: {
|
|
|
|
x: { value: 200, duration: 2000, ease: 'Power1', yoyo: true }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Chained property tweens:
|
|
|
|
// Each tween uses the same duration and ease because they've been 'globally' defined, except the middle one,
|
|
|
|
// which uses its own duration as it overrides the global one
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
x: [ { value: 200 }, { value: 300, duration: 50 }, { value: 400 } ],
|
|
|
|
duration: 2000,
|
|
|
|
ease: 'Power1',
|
|
|
|
yoyo: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Multiple property tweens:
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
x: { value: 400, duration: 2000, ease: 'Power1' },
|
|
|
|
y: { value: 300, duration: 1000, ease: 'Sine' }
|
|
|
|
});
|
|
|
|
|
2017-05-10 03:30:00 +00:00
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
props: {
|
|
|
|
x: { value: 400, duration: 2000, ease: 'Power1' },
|
|
|
|
y: { value: 300, duration: 1000, ease: 'Sine' }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// Multiple Targets + Multiple property tweens:
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: [ alien1, alien2, alien3, alienBoss ],
|
|
|
|
props: {
|
|
|
|
x: { value: 400, duration: 2000 },
|
|
|
|
y: { value: 300, duration: 1000 }
|
|
|
|
},
|
|
|
|
ease: 'Sine'
|
|
|
|
});
|
|
|
|
|
|
|
|
// Multiple Targets + Multiple properties + Multi-state Property tweens:
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: [ alien1, alien2, alien3, alienBoss ],
|
|
|
|
props: {
|
|
|
|
x: [ { value: 200, duration: 100 }, { value: 300, duration: 50 }, { value: 400 } ],
|
|
|
|
y: { value: 300, duration: 1000 }
|
|
|
|
},
|
|
|
|
ease: 'Sine'
|
|
|
|
});
|
|
|
|
|
|
|
|
// Multi-value Tween Property with static values
|
|
|
|
|
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: [ alien1, alien2, alien3, alienBoss ],
|
|
|
|
props: {
|
2017-05-11 16:19:18 +00:00
|
|
|
x: [ 200, 300, 400 ],
|
|
|
|
y: [ '+100', '-100', '+100' ]
|
2017-05-10 16:10:21 +00:00
|
|
|
},
|
|
|
|
duration: 1000,
|
|
|
|
ease: 'Sine'
|
|
|
|
});
|
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
// Timeline concept
|
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
var tween = this.tweens.add({
|
|
|
|
targets: player,
|
|
|
|
timeline: [
|
|
|
|
{ x: 400 },
|
|
|
|
{ y: 400 },
|
|
|
|
{ x: 100 },
|
|
|
|
{ y: 100 }
|
|
|
|
],
|
|
|
|
duration: 1000,
|
|
|
|
ease: 'Sine'
|
|
|
|
});
|
|
|
|
|
2017-05-09 23:43:28 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
var Tween = function (manager, config)
|
2017-05-09 19:24:39 +00:00
|
|
|
{
|
|
|
|
this.manager = manager;
|
2017-05-09 19:36:29 +00:00
|
|
|
|
2017-05-09 23:43:28 +00:00
|
|
|
// The following config properties are reserved words, i.e. they map to Tween related functions
|
|
|
|
// and properties. However if you've got a target that has a property that matches one of the
|
|
|
|
// reserved words, i.e. Target.duration - that you want to tween, then pass it inside a property
|
2017-05-10 16:10:21 +00:00
|
|
|
// called `props`. If present it will use the contents of the `props` object instead.
|
2017-05-11 16:19:18 +00:00
|
|
|
// If you have a Target property you want to tween called 'props' then you're SOL I'm afraid!
|
|
|
|
|
|
|
|
// Array of Targets
|
|
|
|
|
|
|
|
// [
|
|
|
|
// {
|
|
|
|
// target: targetRef,
|
|
|
|
// props: {
|
|
|
|
// x: {
|
|
|
|
// start: 0,
|
|
|
|
// current: 0,
|
|
|
|
// end: 0
|
|
|
|
// },
|
|
|
|
// y: {
|
|
|
|
// start: 0,
|
|
|
|
// current: 0,
|
|
|
|
// end: 0
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
|
|
|
|
this.targets = [];
|
|
|
|
|
|
|
|
this.props = [];
|
|
|
|
|
|
|
|
// One of these for every property being tweened
|
|
|
|
this.defaultTweenProp = {
|
|
|
|
key: '',
|
|
|
|
value: 0, // target value (as defined in the original tween, could be a number, string or function)
|
|
|
|
current: 0, // index to which TweenData in the queue it's processing
|
|
|
|
queue: [] // array of TweenData objects (always at least 1, but can be more)
|
|
|
|
};
|
2017-05-09 23:43:28 +00:00
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
// Keep start and end values to ensure we never go over them when ending
|
|
|
|
this.defaultTargetProp = {
|
|
|
|
start: 0,
|
|
|
|
current: 0,
|
|
|
|
end: 0
|
|
|
|
};
|
2017-05-09 23:43:28 +00:00
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
// 'Default' Tween properties, duplicated for each property
|
|
|
|
// Swap for local getValue for speed
|
|
|
|
// TODO: Add local getters (getLoop, getProgress, etc)
|
2017-05-09 23:43:28 +00:00
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
this.defaultTweenData = {
|
2017-05-10 16:10:21 +00:00
|
|
|
ease: GetEaseFunction(GetValue(config, 'ease', 'Power0')),
|
|
|
|
duration: GetValue(config, 'duration', 1000),
|
|
|
|
yoyo: GetValue(config, 'yoyo', false),
|
|
|
|
repeat: GetValue(config, 'repeat', 0),
|
|
|
|
loop: GetValue(config, 'loop', false),
|
|
|
|
delay: GetValue(config, 'delay', 0),
|
|
|
|
startAt: null,
|
|
|
|
progress: 0,
|
|
|
|
startTime: 0,
|
2017-05-11 16:19:18 +00:00
|
|
|
elapsed: 0,
|
|
|
|
direction: 0 // 0 = forward, 1 = reverse
|
2017-05-10 16:10:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// this.ease = GetEaseFunction(GetValue(config, 'ease', 'Power0'));
|
|
|
|
// this.duration = GetValue(config, 'duration', 1000);
|
2017-05-09 23:43:28 +00:00
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// Only applied if this Tween is part of a Timeline
|
|
|
|
// this.offset = GetValue(config, 'offset', 0);
|
2017-05-09 23:43:28 +00:00
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// this.yoyo = GetValue(config, 'yoyo', false);
|
|
|
|
// this.repeat = GetValue(config, 'repeat', 0);
|
|
|
|
// this.delay = GetValue(config, 'delay', 0);
|
|
|
|
// this.onCompleteDelay = GetValue(config, 'onCompleteDelay', 0);
|
2017-05-09 19:36:29 +00:00
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// Same as repeat -1 (if set, overrides repeat value)
|
|
|
|
// this.loop = GetValue(config, 'loop', (this.repeat === -1));
|
2017-05-10 03:30:00 +00:00
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// this.paused = GetValue(config, 'paused', false);
|
|
|
|
// this.useFrames = GetValue(config, 'useFrames', false);
|
|
|
|
// this.autoStart = GetValue(config, 'autoStart', true);
|
2017-05-10 03:30:00 +00:00
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// Callbacks
|
2017-05-10 03:30:00 +00:00
|
|
|
|
2017-05-09 19:36:29 +00:00
|
|
|
this.onStart;
|
|
|
|
this.onStartScope;
|
|
|
|
this.onStartParams;
|
|
|
|
|
|
|
|
this.onUpdate;
|
|
|
|
this.onUpdateScope;
|
|
|
|
this.onUpdateParams;
|
|
|
|
|
|
|
|
this.onRepeat;
|
|
|
|
this.onRepeatScope;
|
|
|
|
this.onRepeatParams;
|
|
|
|
|
|
|
|
this.onComplete;
|
|
|
|
this.onCompleteScope;
|
|
|
|
this.onCompleteParams;
|
|
|
|
|
|
|
|
this.callbackScope;
|
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// Running properties
|
2017-05-10 01:25:46 +00:00
|
|
|
|
2017-05-10 16:10:21 +00:00
|
|
|
// this.running = this.autoStart;
|
|
|
|
// this.progress = 0;
|
|
|
|
// this.totalDuration = 0;
|
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
this.build(config);
|
2017-05-09 19:24:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Tween.prototype.constructor = Tween;
|
|
|
|
|
|
|
|
Tween.prototype = {
|
2017-05-09 19:36:29 +00:00
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
// Build Process:
|
|
|
|
|
|
|
|
// For Each Prop
|
|
|
|
// Create TweenProp object
|
|
|
|
// Populate queue with TweenData objects (at least 1)
|
|
|
|
// Add to props array
|
|
|
|
// For Each Target
|
|
|
|
// Create Target object
|
|
|
|
// For Each Prop
|
|
|
|
// Create TargetProp object in props object
|
|
|
|
|
|
|
|
build: function (config)
|
|
|
|
{
|
|
|
|
// For now let's just assume `config.props` is being used:
|
|
|
|
|
|
|
|
var propKeys = [];
|
|
|
|
|
|
|
|
for (var key in config.props)
|
|
|
|
{
|
|
|
|
var prop = CloneObject(this.defaultTweenProp);
|
|
|
|
|
|
|
|
var data;
|
|
|
|
var value = config.props[key];
|
|
|
|
|
|
|
|
if (typeof value === 'number')
|
|
|
|
{
|
|
|
|
// props: {
|
|
|
|
// x: 400,
|
|
|
|
// y: 300
|
|
|
|
// }
|
|
|
|
data = CloneObject(this.defaultTweenData);
|
|
|
|
|
|
|
|
prop.value = parseFloat(value);
|
|
|
|
}
|
|
|
|
else if (typeof value === 'string')
|
|
|
|
{
|
|
|
|
// props: {
|
|
|
|
// x: '+400',
|
|
|
|
// y: '-300'
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
else if (typeof value === 'function')
|
|
|
|
{
|
|
|
|
// props: {
|
|
|
|
// x: function () { return Math.random() * 10 },
|
|
|
|
// y: someOtherCallback
|
|
|
|
// }
|
|
|
|
data = CloneObject(this.defaultTweenData);
|
|
|
|
|
|
|
|
prop.value = parseFloat(value.call());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// props: {
|
|
|
|
// x: { value: 400, ... },
|
|
|
|
// y: { value: 300, ... }
|
|
|
|
// }
|
|
|
|
|
|
|
|
data = MergeRight(this.defaultTweenData, value);
|
|
|
|
|
|
|
|
// Value may still be a string, function or number though
|
|
|
|
|
|
|
|
prop.value = parseFloat(data.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
prop.key = key;
|
|
|
|
prop.queue.push(data);
|
|
|
|
|
|
|
|
this.props.push(prop);
|
|
|
|
|
|
|
|
propKeys.push(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
var targets = this.getTargets(config);
|
|
|
|
|
|
|
|
for (var i = 0; i < targets.length; i++)
|
|
|
|
{
|
|
|
|
var target = {
|
|
|
|
ref: targets[i],
|
|
|
|
props: {}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (var k = 0; k < propKeys.length; k++)
|
|
|
|
{
|
|
|
|
target.props[propKeys[k]] = CloneObject(this.defaultTargetProp);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.targets.push(target);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// getPropValue
|
|
|
|
|
2017-05-09 23:43:28 +00:00
|
|
|
// Move to own functions
|
|
|
|
|
2017-05-10 01:25:46 +00:00
|
|
|
getV: function (obj, key)
|
|
|
|
{
|
|
|
|
if (obj.hasOwnProperty(key))
|
|
|
|
{
|
|
|
|
return obj[key];
|
|
|
|
}
|
|
|
|
else if (this[key])
|
|
|
|
{
|
|
|
|
return this[key];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
buildTweenData: function (config)
|
|
|
|
{
|
|
|
|
// For now let's just assume `config.props` is being used:
|
|
|
|
|
2017-05-10 03:30:00 +00:00
|
|
|
// props: {
|
|
|
|
// x: 400,
|
|
|
|
// y: 300
|
|
|
|
// }
|
|
|
|
|
|
|
|
// props: {
|
|
|
|
// x: { value: 400, duration: 2000, ease: 'Power1' },
|
|
|
|
// y: { value: 300, duration: 1000, ease: 'Sine' }
|
|
|
|
// }
|
2017-05-10 01:25:46 +00:00
|
|
|
|
|
|
|
for (var key in config.props)
|
|
|
|
{
|
2017-05-10 03:30:00 +00:00
|
|
|
// Check it's not a string or number (or function?)
|
|
|
|
// TODO: value might be an Array
|
|
|
|
|
|
|
|
var data;
|
|
|
|
var value = config.props[key];
|
|
|
|
|
|
|
|
if (typeof value === 'number')
|
|
|
|
{
|
|
|
|
data = CloneObject(this.defaultTweenData);
|
|
|
|
|
|
|
|
data.value = value;
|
|
|
|
}
|
|
|
|
else if (typeof value === 'string')
|
|
|
|
{
|
|
|
|
// Do something :)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
data = MergeRight(this.defaultTweenData, config.props[key]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// this.props = [
|
|
|
|
// {
|
|
|
|
// key: 'x',
|
|
|
|
// running: true,
|
|
|
|
// complete: false,
|
|
|
|
// current: 0,
|
|
|
|
// queue: [ TweenData, TweenData, TweenData ],
|
|
|
|
// totalDuration: Number (ms)
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
|
|
|
|
// Convert to ms
|
|
|
|
data.duration *= 1000;
|
|
|
|
|
|
|
|
var propertyMarker = CloneObject(this.defaultInstance);
|
|
|
|
|
|
|
|
propertyMarker.key = key;
|
|
|
|
|
|
|
|
// Adapt these to support array based multi-inserts
|
|
|
|
propertyMarker.queue.push(data);
|
|
|
|
propertyMarker.totalDuration = data.duration;
|
|
|
|
|
|
|
|
this.props.push(propertyMarker);
|
|
|
|
|
|
|
|
this.totalDuration += propertyMarker.totalDuration;
|
2017-05-10 01:25:46 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
// Update Loop:
|
|
|
|
|
|
|
|
// For Each Prop
|
|
|
|
// Get current TweenData
|
|
|
|
// Update elapsed time
|
|
|
|
// Get ease value
|
|
|
|
// For Each Target
|
|
|
|
// Apply ease value to Target value
|
|
|
|
// Has the tween finished?
|
|
|
|
// No: Wait for next update
|
|
|
|
// Yes: Advance queue index, or complete tween
|
|
|
|
|
2017-05-10 01:25:46 +00:00
|
|
|
update: function (timestep, delta)
|
|
|
|
{
|
2017-05-10 03:30:00 +00:00
|
|
|
if (!this.running)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate tweens
|
|
|
|
|
|
|
|
var list = this.props;
|
|
|
|
var targets = this.targets;
|
|
|
|
|
|
|
|
// this.props = [
|
|
|
|
// {
|
|
|
|
// key: 'x',
|
|
|
|
// start: [ Target0 startValue, Target1 startValue, Target2 startValue ],
|
2017-05-10 16:10:21 +00:00
|
|
|
// end: [ Target0 endValue, Target1 endValue, Target2 endValue ],
|
2017-05-10 03:30:00 +00:00
|
|
|
// running: true,
|
|
|
|
// complete: false,
|
|
|
|
// current: 0,
|
|
|
|
// queue: [ TweenData, TweenData, TweenData ],
|
|
|
|
// totalDuration: Number (ms)
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
|
|
|
|
for (var i = 0; i < list.length; i++)
|
|
|
|
{
|
|
|
|
var entry = list[i];
|
|
|
|
|
|
|
|
// Update TweenData
|
|
|
|
|
|
|
|
if (entry.running)
|
|
|
|
{
|
|
|
|
// TweenData = {
|
|
|
|
// value: undefined,
|
|
|
|
// progress: 0,
|
|
|
|
// startTime: 0,
|
|
|
|
// ease: this.ease,
|
|
|
|
// duration: this.duration,
|
|
|
|
// yoyo: this.yoyo,
|
|
|
|
// repeat: this.repeat,
|
|
|
|
// loop: this.loop,
|
|
|
|
// delay: this.delay,
|
|
|
|
// startAt: undefined,
|
|
|
|
// elapsed: 0
|
|
|
|
// };
|
|
|
|
|
|
|
|
var tweenData = entry.queue[entry.current];
|
|
|
|
|
|
|
|
tweenData.elapsed += delta;
|
|
|
|
|
|
|
|
if (tweenData.elapsed > tweenData.duration)
|
|
|
|
{
|
|
|
|
tweenData.elapsed = tweenData.duration;
|
|
|
|
}
|
|
|
|
|
|
|
|
// What % is that?
|
|
|
|
tweenData.progress = tweenData.elapsed / tweenData.duration;
|
|
|
|
|
|
|
|
for (var t = 0; t < targets.length; t++)
|
|
|
|
{
|
|
|
|
targets[i][entry.key] = tweenData.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-10 01:25:46 +00:00
|
|
|
|
|
|
|
},
|
|
|
|
|
2017-05-11 16:19:18 +00:00
|
|
|
getTargets: function (config)
|
2017-05-09 19:36:29 +00:00
|
|
|
{
|
2017-05-11 16:19:18 +00:00
|
|
|
var targets = GetValue(config, 'targets', null);
|
|
|
|
|
2017-05-09 23:43:28 +00:00
|
|
|
if (typeof targets === 'function')
|
|
|
|
{
|
|
|
|
targets = targets.call();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Array.isArray(targets))
|
|
|
|
{
|
|
|
|
targets = [ targets ];
|
|
|
|
}
|
2017-05-09 19:36:29 +00:00
|
|
|
|
2017-05-09 23:43:28 +00:00
|
|
|
return targets;
|
2017-05-09 19:36:29 +00:00
|
|
|
},
|
|
|
|
|
2017-05-09 23:43:28 +00:00
|
|
|
eventCallback: function (type, callback, params, scope)
|
|
|
|
{
|
|
|
|
var types = [ 'onStart', 'onUpdate', 'onRepeat', 'onComplete' ];
|
|
|
|
|
|
|
|
if (types.indexOf(type) !== -1)
|
|
|
|
{
|
|
|
|
this[type] = callback;
|
|
|
|
this[type + 'Params'] = params;
|
|
|
|
this[type + 'Scope'] = scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
timeScale: function ()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-05-09 19:24:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = Tween;
|