Kanvas/Public/ecma/Modules/Parallax.ecma.js

601 lines
18 KiB
JavaScript

"use strict";
import {Kanvas} from "../Kanvas.ecma.js";
/**
* @class
* @constructor
* @param {!Kanvas} kanvas
* @returns {void}
* @access public
*/
export const Parallax = (function(){
/**
* @constructs Parallax
* @param {!Kanvas} kanvas
* @returns {void}
* @access private
*/
const Parallax = function(kanvas){
/** @type {Parallax} */
const self = this;
/**
* @returns {void}
* @access private
*/
const constructor = () => {};
constructor();
};
/**
* @class
* @constructor
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {void}
* @access public
* @static
*/
Parallax.Position = function(inputs = null){
/** @type {Parallax.Position} */
const self = this;
/** @type {number} */
this.x = Kanvas.get_value("x", inputs, 0);
/** @type {number} */
this.y = Kanvas.get_value("y", inputs, 0);
/** @type {number} */
this.limit_x = Kanvas.get_value("limit_x", inputs, 1);
/** @type {number} */
this.limit_y = Kanvas.get_value("limit_y", inputs, 1);
/**
* @returns {void}
* @access private
*/
const fix_x = () => {
self.x = (self.x + self.limit_x) % self.limit_x;
};
/**
* @returns {void}
* @access private
*/
const fix_y = () => {
self.y = (self.y + self.limit_y) % self.limit_y;
};
/**
* @param {!number} x
* @returns {void}
* @access public
*/
this.add_x = x => {
if(Kanvas.is_number(x)){
self.x += x;
fix_x();
};
};
/**
* @param {!number} y
* @returns {void}
* @access public
*/
this.add_y = y => {
if(Kanvas.is_number(y)){
self.y += y;
fix_y();
};
};
/**
* @param {!number} x
* @returns {void}
* @access public
*/
this.set_x = x => {
if(Kanvas.is_number(x)){
self.x = x;
fix_x();
};
};
/**
* @param {!number} y
* @returns {void}
* @access public
*/
this.set_y = y => {
if(Kanvas.is_number(y)){
self.y = y;
fix_y();
};
};
/**
* @returns {number}
* @access public
*/
this.get_x = () => self.x || 0;
/**
* @returns {number}
* @access public
*/
this.get_y = () => self.y || 0;
/**
* @param {!number} x
* @param {!number} y
* @returns {void}
* @access public
*/
this.add = (x, y) => {
self.add_x(x);
self.add_y(y);
};
/**
* @param {!number} x
* @param {!number} y
* @returns {void}
* @access public
*/
this.set = (x, y) => {
self.set_x(x);
self.set_y(x);
};
/**
* @returns {Array.<number, number>}
* @access public
*/
this.get = () => [self.get_x(), self.get_y()];
};
/**
* @class
* @constructor
* @param {!Kanvas} kanvas
* @param {?Parallax.Class} [parent = null]
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {void}
* @access public
* @static
*/
Parallax.Stage = function(kanvas, parent = null, inputs = null){
/** @type {Parallax.Stage} */
const self = this;
/** @type {Parallax.Class|null} */
this.parent = parent;
/** @type {number} */
this.width = Kanvas.get_value(["width", "size", "x"], inputs, kanvas.cells);
/** @type {number} */
this.height = Kanvas.get_value(["height", "size", "y"], inputs, kanvas.cells);
/** @type {Array.<Object.<string, any|null>|Array.<any|null>|Kanvas.ObjectBase|null>} */
this.childs = Kanvas.get_array(Kanvas.get_value("childs", inputs, []));
/** @type {boolean} */
this.fixed = !!Kanvas.get_value("fixed", inputs, false);
/** @type {Array.<number, number>} */
this.framed_in = Kanvas.get_value("framed_in", inputs, [1, 1]);
/** @type {boolean} */
this.repeated = !!Kanvas.get_value("repeated", inputs, false);
/** @type {Array.<Array.<number, number, number, number, Array.<number>, HTMLCanvasElement|null>>} */
this.frames = [];
/** @type {boolean} */
this.done = false;
/** @type {number} */
this.childs_margin_x = Kanvas.get_value("childs_margin_x", 0);
/** @type {number} */
this.childs_margin_y = Kanvas.get_value("childs_margin_y", 0);
/** @type {number} */
this.start_x = Kanvas.get_value("start_x", inputs, kanvas.start_x);
/** @type {number} */
this.start_y = Kanvas.get_value("start_y", inputs, kanvas.start_y);
/**
* @returns {void}
* @access private
*/
const constructor = () => {
self.rebuild();
};
/**
* @returns {void}
* @access public
*/
this.build_frames = () => {
/** @type {[number, number]} */
const [frames_x, frames_y] = self.framed_in,
/** @type {[number, number]} */
[size_x, size_y] = [frames_x, frames_y].map(value => 1 / value);
self.frames = [];
for(let x = 0, _x; x < frames_x; x ++)
for(let y = 0, _x = x + 1; y < frames_y;)
self.frames.push([x * size_x, y * size_y, _x * size_x, ++ y * size_y, [], null]);
};
/**
* @param {!HTMLCanvasElement} canvas
* @returns {void}
* @access private
*/
const set_canvas_size = canvas => {
canvas.setAttribute("width", self.width * kanvas.cell_size * kanvas.quality);
canvas.setAttribute("height", self.height * kanvas.cell_size * kanvas.quality);
};
/**
* @param {!number} i
* @returns {Array.<number, number>}
* @access public
*/
this.get_frame_position = i => [i % self.framed_in[Parallax.X], i / self.framed_in[Parallax.Y] >> 0];
/**
* @param {!(Object.<string, any|null>|Kanvas.ObjectBase)} item
* @returns {Array.<Array.<number, number>, Array.<number, number>>}
* @access private
*/
const get_item_range_position = item => {
/** @type {Array.<Array.<number, number>, Array.<number, number>>} */
const ranges = [
[item.x - item.width * item.align_x, item.x + item.width * (1 - item.align_x)],
[item.y - item.height * item.align_y, item.y + item.height * (1 - item.align_y)]
];
["width", "height"].forEach((key, i) => {
ranges[i][0] < 0 && (ranges[i][0] += self[key]);
ranges[i][1] %= self[key];
});
return ranges;
};
/**
* @param {!Array.<number>} ks
* @returns {void}
* @access public
*/
this.recalculate_items_in_frames = (ks = []) => {
/** @type {Array.<number, number>} */
const frame_size = [self.width / framed_in[Parallax.X], self.height / framed_in[Parallax.Y]],
/** @type {number} */
l = self.frames.length;
Kanvas.get_array(ks).forEach(k => {
/** @type {Array.<Array.<number, number>, Array.<number, number>>} */
const [x_positions, y_positions] = get_item_range_position(self.childs[k]).map((range, i) => (
Parallax.get_range_positions(range.map(value => value / frame_size[i] >> 0), framed_in[i])
));
self.frames.forEach(frame => {
frame.includes(k) && frame.splice(frame.indexOf(k), 1);
});
x_positions.forEach(x => {
y_positions.forEach(y => {
/** @type {number} */
const i = y * framed_in[0] + x;
self.frames[i] && self.frames[i].push(k);
});
});
});
};
// /**
// * @returns {void}
// * @access public
// */
// this.recalculate_frames_items = () => {
// self.recalculate_items_in_frames(Object.keys(self.childs));
// };
this.build_cache = callback => {
// if(self.fixed){
// /** @type {number} */
// let loaded = 0,
// /** @type {number
// * } */
// items_l = 0;
// /** @type {number} */
// const l = self.frames.length,
// end = () => {
// ++ loaded == l && Kanvas.execute(callback);
// };
// self.done = false;
// self.frames.forEach(([from_x, from_y, to_x, to_y, childs, cache], i) => {
// /** @type {HTMLCanvasElement} */
// const canvas = cache || document.createElement("canvas"),
// /** @type {CanvasRenderingContext2D} */
// context = canvas.getContext("2d"),
// /** @type {[number, number]} */
// [main_x, main_y] = self.get_frame_position(i);
// set_canvas_size(canvas);
// cache || (self.frames[i][Parallax.CACHE] = canvas);
// Parallax.FRAMES_BROTHERS.forEach(plus_x => {
// /** @type {number} */
// const x = (main_x + plus_x + self.framed_in[Parallax.X]) % self.framed_in[Parallax.X];
// Parallax.FRAMES_BROTHERS.forEach(plus_y => {
// /** @type {number} */
// const y = (main_y + plus_y + self.framed_in[Parallax.Y]) % self.framed_in[Parallax.Y],
// /** @type {number} */
// j = y * self.framed_in[Parallax.X] + x;
// kanvas.draw(context, self.frames[j][Parallax.CHILDS].map(k => {
// items_l += Parallax.set_on_load(self.childs[k], end, (i, item) => {
// self.childs[k] = item;
// });
// return self.childs[k];
// }), [{
// child_margin_x : -from_x * self.width,
// child_margin_y : -from_y * self.height
// }, self.parent]);
// });
// });
// items_l || end();
// });
// };
};
/**
* @returns {void}
* @access public
*/
this.resize_cache = () => {
self.fixed &&
self.frames.forEach(([from_x, from_y, to_x, to_y, childs, cache]) => {
set_canvas_size(cache);
});
};
/**
* @param {?kanvas_default_callback} [callback = null]
* @returns {void}
* @access public
*/
this.rebuild = (callback = null) => {
self.build_frames();
self.build_cache(() => {
self.resize_cache();
Kanvas.execute(callback);
});
};
constructor();
};
/**
* @class
* @param {!Kanvas} kanvas
* @returns {void}
* @access public
* @static
*/
Parallax.Class = class extends Kanvas.ObjectBase{
/**
* @constructor
* @param {!Kanvas} kanvas
* @param {!Kanvas.ObjectBase} parent
* @param {!(Object.<string, any|null>|Array)} inputs
* @param {?number} [i = null]
* @access public
*/
constructor(kanvas, parent, inputs, i = null){
super(kanvas, parent, inputs, i, {}, {
number : ["x", "y", "width", "height"],
array : ["stages", "childs"]
});
this.type_name = "Parallax";
this.type = Kanvas.SHAPES.Parallax;
/** @type {Parallax.Position} */
this.position = new Parallax.Position();
/** @type {Array.<Parallax.Stage|null>} */
this.stages = Kanvas.get_array(this._get("stages", [])).map(item => (
Parallax.is_stage(item) ? item :
Kanvas.is_array(item) || Kanvas.is_dictionary(item) ? new Parallax.Stage(this.kanvas, this, item) :
null));
this.set_status(Kanvas.LOADED);
};
/**
* @param {!CanvasRenderingContext2D} context
* @param {!number} x
* @param {!number} y
* @returns {void}
* @access public
*/
draw(context, x, y){
// try{
// this.stages.forEach(stage => {
// /** @type {number} */
// const x = stage.width * this.position.x,
// /** @type {number} */
// y = stage.height * this.position.y;
// stage.frames.forEach(([from_x, from_y, to_x, to_y, childs, cache], i) => {
// /** @type {Array.<number>} */
// const ks = [];
// from_x *= stage.width;
// to_x *= stage.width;
// from_y *= stage.height;
// to_y *= stage.height;
// if(x >= from_x && x <= to_x && y >= from_y && y >= to_y){
// console.log(stage.fixed);
// if(stage.fixed)
// cache && context.drawImage(cache, from_x - x, from_y - y);
// else
// childs.forEach(k => {
// if(!ks.includes(k)){
// ks.push(k);
// };
// });
// };
// stage.fixed ||
// this.kanvas.draw(context, ks.sort((a, b) => a - b).map(k => stage.childs[k]), [{
// margin_x : from_x - x,
// margin_y : from_y - y,
// without_parent : true
// }, this]);
// });
// });
// }catch(exception){
// console.error(exception);
// };
};
};
/** @type {number} */
Parallax.X = 0;
/** @type {number} */
Parallax.Y = 1;
/** @type {number} */
Parallax.FROM_X = 0;
/** @type {number} */
Parallax.FROM_Y = 1;
/** @type {number} */
Parallax.TO_X = 2;
/** @type {number} */
Parallax.TO_Y = 3;
/** @type {number} */
Parallax.CHILDS = 4;
/** @type {number} */
Parallax.CACHE = 5;
/** @type {Array.<number, number, number>} */
Parallax.FRAMES_BROTHERS = [-1, 0, 1];
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
Parallax.is_stage = item => item instanceof Parallax.Stage;
/**
* @param {!(Object.<string, any|null>|Array.<any|null>|Kanvas.ObjectBase|null)} item
* @param {!kanvas_default_callback} callback
* @param {?kanvas_event_callback} [child_callback = null]
* @returns {number}
* @access public
*/
Parallax.set_on_load = (item, callback, child_callback = null) => {
/** @type {number} */
let i = 0;
const end = () => {
Kanvas.execute(callback);
Kanvas.execute(child_callback);
};
if(item){
if(Kanvas.is_array(item))
item.forEach(sub_item => i += Parallax.set_on_load(sub_item, callback));
else{
const callbacks = [callback, child_callback];
i = 1;
if(item.status && item.status & Kanvas.LOADED)
setTimeout(end, 0);
else if(Kanvas.is_dictionary){
item.on_load && callbacks.push(item.on_load);
item.on_load = (j, item_loaded) => {
callbacks.forEach(subcallback => Kanvas.execute(subcallback, j, item_loaded));
};
}else
item.on_load.add(end);
};
};
return i;
};
/**
* @param {!Array.<number, number>} range
* @param {!number} limit
* @returns {Array.<number>}
* @access public
* @static
*/
Parallax.get_range_positions = (range, limit) => {
/** @type {Array.<number>} */
const positions = [];
if(range[0] > range[1]){
for(let i = 0; i <= range[0]; i ++)
positions.push(i);
for(let i = range[1]; i < limit; i ++)
positions.push(i);
}else
for(let i = range[0]; i <= range[1]; i ++)
positions.push(i);
return positions;
};
return Parallax;
})();
Kanvas.SHAPES.Parallax = Parallax.Class;