601 lines
18 KiB
JavaScript
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; |