phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
325 lines (282 loc) • 10.1 kB
JavaScript
/**
* @author Richard Davey <rich@phaser.io>
* @copyright 2013-2025 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Between = require('../../math/Between');
var FloatBetween = require('../../math/FloatBetween');
/**
* @ignore
*/
function hasGetActive (def)
{
return (!!def.getActive && typeof def.getActive === 'function');
}
/**
* @ignore
*/
function hasGetStart (def)
{
return (!!def.getStart && typeof def.getStart === 'function');
}
/**
* @ignore
*/
function hasGetEnd (def)
{
return (!!def.getEnd && typeof def.getEnd === 'function');
}
/**
* @ignore
*/
function hasGetters (def)
{
return hasGetStart(def) || hasGetEnd(def) || hasGetActive(def);
}
/**
* Returns `getActive`, `getStart` and `getEnd` functions for a TweenData based on a target property and end value.
*
* `getActive` if not null, is invoked _immediately_ as soon as the TweenData is running, and is set on the target property.
* `getEnd` is invoked once any start delays have expired and returns what the value should tween to.
* `getStart` is invoked when the tween reaches the end and needs to either repeat or yoyo, it returns the value to go back to.
*
* If the end value is a number, it will be treated as an absolute value and the property will be tweened to it.
* A string can be provided to specify a relative end value which consists of an operation
* (`+=` to add to the current value, `-=` to subtract from the current value, `*=` to multiply the current
* value, or `/=` to divide the current value) followed by its operand.
*
* A function can be provided to allow greater control over the end value; it will receive the target
* object being tweened, the name of the property being tweened, and the current value of the property
* as its arguments and must return a value.
*
* If both the starting and the ending values need to be controlled, an object with `getStart` and `getEnd`
* callbacks, which will receive the same arguments, can be provided instead. If an object with a `value`
* property is provided, the property will be used as the effective value under the same rules described here.
*
* @function Phaser.Tweens.Builders.GetValueOp
* @since 3.0.0
*
* @param {string} key - The name of the property to modify.
* @param {*} propertyValue - The ending value of the property, as described above.
*
* @return {function} An array of functions, `getActive`, `getStart` and `getEnd`, which return the starting and the ending value of the property based on the provided value.
*/
var GetValueOp = function (key, propertyValue)
{
var callbacks;
// The returned value sets what the property will be at the END of the Tween (usually called at the start of the Tween)
var getEnd = function (target, key, value) { return value; };
// The returned value sets what the property will be at the START of the Tween (usually called at the end of the Tween)
var getStart = function (target, key, value) { return value; };
// What to set the property to the moment the TweenData is invoked
var getActive = null;
var t = typeof(propertyValue);
if (t === 'number')
{
// props: {
// x: 400,
// y: 300
// }
getEnd = function ()
{
return propertyValue;
};
}
else if (Array.isArray(propertyValue))
{
// props: {
// x: [ 400, 300, 200 ],
// y: [ 10, 500, 10 ]
// }
getStart = function ()
{
return propertyValue[0];
};
getEnd = function ()
{
return propertyValue[propertyValue.length - 1];
};
}
else if (t === 'string')
{
// props: {
// x: '+=400',
// y: '-=300',
// z: '*=2',
// w: '/=2',
// p: 'random(10, 100)' - random float
// p: 'int(10, 100)' - random int
// }
var op = propertyValue.toLowerCase();
var isRandom = (op.substring(0, 6) === 'random');
var isInt = (op.substring(0, 3) === 'int');
if (isRandom || isInt)
{
// random(0.5, 3.45)
// int(10, 100)
var brace1 = op.indexOf('(');
var brace2 = op.indexOf(')');
var comma = op.indexOf(',');
if (brace1 && brace2 && comma)
{
var value1 = parseFloat(op.substring(brace1 + 1, comma));
var value2 = parseFloat(op.substring(comma + 1, brace2));
if (isRandom)
{
getEnd = function ()
{
return FloatBetween(value1, value2);
};
}
else
{
getEnd = function ()
{
return Between(value1, value2);
};
}
}
else
{
throw new Error('invalid random() format');
}
}
else
{
op = op[0];
var num = parseFloat(propertyValue.substr(2));
switch (op)
{
case '+':
getEnd = function (target, key, value)
{
return value + num;
};
break;
case '-':
getEnd = function (target, key, value)
{
return value - num;
};
break;
case '*':
getEnd = function (target, key, value)
{
return value * num;
};
break;
case '/':
getEnd = function (target, key, value)
{
return value / num;
};
break;
default:
getEnd = function ()
{
return parseFloat(propertyValue);
};
}
}
}
else if (t === 'function')
{
// The same as setting just the getEnd function and no getStart
// props: {
// x: function (target, key, value, targetIndex, totalTargets, tween, tweenData) { return value + 50); },
// }
getEnd = propertyValue;
}
else if (t === 'object')
{
if (hasGetters(propertyValue))
{
/*
x: {
// Called the moment Tween is active. The returned value sets the property on the target immediately.
getActive: function (target, key, value, targetIndex, totalTargets, tween, tweenData)
{
return value;
},
// Called at the start of the Tween. The returned value sets what the property will be at the END of the Tween.
getEnd: function (target, key, value, targetIndex, totalTargets, tween, tweenData)
{
return value;
},
// Called at the end of the Tween. The returned value sets what the property will be at the START of the Tween.
getStart: function (target, key, value, targetIndex, totalTargets, tween, tweenData)
{
return value;
}
}
*/
if (hasGetActive(propertyValue))
{
getActive = propertyValue.getActive;
}
if (hasGetEnd(propertyValue))
{
getEnd = propertyValue.getEnd;
}
if (hasGetStart(propertyValue))
{
getStart = propertyValue.getStart;
}
}
else if (propertyValue.hasOwnProperty('value'))
{
// 'value' may still be a string, function or a number
// props: {
// x: { value: 400, ... },
// y: { value: 300, ... }
// }
callbacks = GetValueOp(key, propertyValue.value);
}
else
{
// 'from' and 'to' may still be a string, function or a number
// props: {
// x: { from: 400, to: 600 },
// y: { from: 300, to: 500 }
// }
// Same as above, but the 'start' value is set immediately on the target
// props: {
// x: { start: 400, to: 600 },
// y: { start: 300, to: 500 }
// }
// 'start' value is set immediately, then it goes 'from' to 'to' during the tween
// props: {
// x: { start: 200, from: 400, to: 600 },
// y: { start: 300, from: 300, to: 500 }
// }
var hasTo = propertyValue.hasOwnProperty('to');
var hasFrom = propertyValue.hasOwnProperty('from');
var hasStart = propertyValue.hasOwnProperty('start');
if (hasTo && (hasFrom || hasStart))
{
callbacks = GetValueOp(key, propertyValue.to);
if (hasStart)
{
var startCallbacks = GetValueOp(key, propertyValue.start);
callbacks.getActive = startCallbacks.getEnd;
}
if (hasFrom)
{
var fromCallbacks = GetValueOp(key, propertyValue.from);
callbacks.getStart = fromCallbacks.getEnd;
}
}
}
}
// If callback not set by the else if block above then set it here and return it
if (!callbacks)
{
callbacks = {
getActive: getActive,
getEnd: getEnd,
getStart: getStart
};
}
return callbacks;
};
module.exports = GetValueOp;