import { MovieClip } from './MovieClip';
// If the movieclip plugin is installed
let _prepare = null;
/**
* @namespace PIXI.animate.utils
* @description For keyframe conversions
*/
export default class AnimateUtils {
/**
* Convert the Hexidecimal string (e.g., "#fff") to uint
* @static
* @method PIXI.animate.utils.hexToUint
*/
static hexToUint(hex) {
// Remove the hash
hex = hex.substr(1);
// Convert shortcolors fc9 to ffcc99
if (hex.length === 3) {
hex = hex.replace(/([a-f0-9])/g, '$1$1');
}
return parseInt(hex, 16);
}
/**
* Fill frames with booleans of true (showing) and false (hidden).
* @static
* @method PIXI.animate.utils.fillFrames
* @param {Array<Boolean>} timeline
* @param {int} startFrame The start frame when the timeline shows up
* @param {int} duration The length of showing
*/
static fillFrames(timeline, startFrame, duration) {
//ensure that the timeline is long enough
const oldLength = timeline.length;
if (oldLength < startFrame + duration) {
timeline.length = startFrame + duration;
//fill any gaps with false to denote that the child should be removed for a bit
if (oldLength < startFrame) {
//if the browser has implemented the ES6 fill() function, use that
if (timeline.fill) {
timeline.fill(false, oldLength, startFrame);
} else {
//if we can't use fill, then do a for loop to fill it
for (let i = oldLength; i < startFrame; ++i) {
timeline[i] = false;
}
}
}
}
//if the browser has implemented the ES6 fill() function, use that
if (timeline.fill) {
timeline.fill(true, startFrame, startFrame + duration);
} else {
const length = timeline.length;
//if we can't use fill, then do a for loop to fill it
for (let i = startFrame; i < length; ++i) {
timeline[i] = true;
}
}
}
/**
* Convert serialized array into keyframes
* `"0x100y100 1x150"` to: `{ "0": {"x":100, "y": 100}, "1": {"x": 150} }`
* @static
* @method PIXI.animate.utils.deserializeKeyframes
* @param {String} keyframes
* @param {Object} Resulting keyframes
*/
static deserializeKeyframes(keyframes) {
let result = {};
let i = 0;
let keysMap = {
X: 'x', // x position
Y: 'y', // y position
A: 'sx', // scale x
B: 'sy', // scale y
C: 'kx', // skew x
D: 'ky', // skew y
R: 'r', // rotation
L: 'a', // alpha
T: 't', // tint
F: 'c', // colorTransform
V: 'v' // visibility
};
let c,
buffer = '',
isFrameStarted = false,
prop,
frame = {};
while (i <= keyframes.length) {
c = keyframes[i];
if (keysMap[c]) {
if (!isFrameStarted) {
isFrameStarted = true;
result[buffer] = frame;
}
if (prop) {
frame[prop] = this.parseValue(prop, buffer);
}
prop = keysMap[c];
buffer = '';
i++;
}
// Start a new prop
else if (!c || c === ' ') {
i++;
frame[prop] = this.parseValue(prop, buffer);
buffer = '';
prop = null;
frame = {};
isFrameStarted = false;
} else {
buffer += c;
i++;
}
}
return result;
}
/**
* Convert serialized shapes into draw commands for PIXI.Graphics.
* @static
* @method PIXI.animate.utils.deserializeShapes
* @param {String} str
* @param {Array} Resulting shapes map
*/
static deserializeShapes(str) {
const result = [];
// each shape is a new line
let shapes = str.split("\n");
let isCommand = /^[a-z]{1,2}$/;
for (let i = 0; i < shapes.length; i++) {
let shape = shapes[i].split(' '); // arguments are space separated
for (let j = 0; j < shape.length; j++) {
// Convert all numbers to floats, ignore colors
let arg = shape[j];
if (arg[0] !== '#' && !isCommand.test(arg)) {
shape[j] = parseFloat(arg);
}
}
result.push(shape);
}
return result;
}
/**
* Parse the value of the compressed keyframe.
* @method PIXI.animate.utils.parseValue
* @static
* @private
* @param {String} prop The property key
* @param {String} buffer The contents
* @return {*} The parsed value
*/
static parseValue(prop, buffer) {
switch (prop) {
// Color transforms are parsed as an array
case 'c':
{
buffer = buffer.split(',');
buffer.forEach(function(val, i, buffer) {
buffer[i] = parseFloat(val);
});
return buffer;
}
// Tint value should not be converted
// can be color uint or string
case 't':
{
return buffer;
}
// The visiblity parse as boolean
case 'v':
{
return !!parseInt(buffer);
}
// Everything else parse a floats
default:
{
return parseFloat(buffer);
}
}
}
/**
* Upload all the textures and graphics to the GPU.
* @method PIXI.animate.utils.upload
* @static
* @param {PIXI.WebGLRenderer} renderer Render to upload to
* @param {PIXI.DisplayObject} clip MovieClip to upload
* @param {function} done When complete
*/
static upload(renderer, displayObject, done) {
if (!_prepare) {
_prepare = renderer.plugins.prepare;
_prepare.register(this.addMovieClips);
}
_prepare.upload(displayObject, done);
}
/**
* Add movie clips to the upload prepare.
* @method PIXI.animate.utils.addMovieClips
* @static
* @private
* @param {*} item To add to the queue
*/
static addMovieClips(item) {
if (item instanceof MovieClip) {
item._timedChildTimelines.forEach((timeline) => {
const index = item.children.indexOf(timeline.target);
if (index === -1) {
_prepare.add(timeline.target);
}
});
return true;
}
return false;
}
}