import * as THREE from 'three';
import { RequestAnimationFrameProcess } from '@ud-viz/utils_browser';
import * as itowns from 'itowns';
/**
* @typedef {object} TextureFile
* @property {number} index Index of the file (use to order file)
* @property {string} name Name of the file
* @property {THREE.CanvasTexture|THREE.VideoTexture} texture Texture to apply on the plane
* @property {{height:number,width:number}} size Size of the texture
* @property {HTMLVideoElement} [video] Html video element if it is an video texture.
*/
/**
* @example
* Config Example
* {
"slides": [
{
"name": "diapo1",
"folder": "./assets/img/slide",
"diapositives": ["1.jpeg", "2.jpeg", "3.jpeg"]
},
{
"name": "diapo2",
"folder": "./assets/img/slide",
"diapositives": ["11.jpeg", "12.jpeg", "13.jpeg"]
}
],
"textureRotation": 0,
"durationLoopInSec":10
}
* @classdesc Slideshow Widget class
*/
export class SlideShow {
/**
* It initializes the widget.
*
*
* @param {itowns.PlanarView} itownsView - The itowns view.
* @param {object} [configSlideShow] - The configuration of the widget. need description
* @param {Array<object>} configSlideShow.slides - Array of slide Object
* @param {string} configSlideShow.slides[].name - Name of a slideshow
* @param {string} configSlideShow.slides[].folder - Path of the folder
* @param {Array<string>} configSlideShow.slides[].diapositives - Array of path of name files
* @param {number} configSlideShow.durationLoopInSec - Seconds between two slides
* @param {number} configSlideShow.textureRotation - Rotation in degrees of textures
* @param {import('itowns').Extent} extent - The extent of the widget.
*/
constructor(itownsView, configSlideShow, extent) {
/** @type {import('itowns').Extent} */
this.extent = extent;
/** @type {import('itowns').PlanarView} */
this.itownsView = itownsView;
/**
* Root html of slideshow view
*
@type {HTMLElement} */
this.domElement = null;
// Ids
this.coordinatesInputVector = null;
this.rotationInputVector = null;
this.sizeInputVector = null;
this.aspectRatioCheckbox = null;
this.loopSlideShowCheckbox = null;
this.slideSelect = null;
this.durationLoopInputID = null;
this.counterLoopTimeDiv = null;
// Vectors
this.coordinatesVector = new THREE.Vector3();
this.rotationVector = new THREE.Vector3();
this.sizeVector = new THREE.Vector2();
/**
* List of callbacks to set when the window is created
*
@type {Array<{event:string,element:HTMLElement,cb:Function}>} */
this.callbacksHTMLEl = [];
/** @type {THREE.Mesh} */
this.plane = null;
/**
* if true the application update its view3D eachFrame
*
@type {boolean} */
this.notifyValue = false;
this.defaultTexture = null;
this.initDefaultTexture();
/**
* List of textures with data
*
@type {TextureFile[]} */
this.textureFiles = [this.createDefaultTextureFile(0)];
this.currentTextureFile = null;
this.currentTexture = null;
this.intervalLoop = null;
this.counterIntervalLoop = null;
this.configSlideShow = configSlideShow || {};
this.slides = this.configSlideShow.slides;
this.durationLoopInSec = this.configSlideShow.durationLoopInSec || 10; // Take config value or 10s by default
this.textureRotation = this.configSlideShow.textureRotation || 0;
this.textureRotation = (this.textureRotation * Math.PI) / 180.0;
this.initHtml();
if (this.slides) {
this.setSlideshowInConfig(0);
} else {
this.setTexture(0);
}
// listeners
this.dropListener = null;
this.dragOverListener = null;
this.nextListener = null;
this.previousListener = null;
this.hidePlaneListener = null;
// Through this.callbacksHTMLEl and addEventListeners to HTMLElements in DOM (elements which created by Window class)
this.callbacksHTMLEl.forEach((callbackHtmlEl) => {
callbackHtmlEl.element.addEventListener(
callbackHtmlEl.event,
callbackHtmlEl.cb.bind(this)
);
});
this.matchExtent();
/** A function call at 30 fps by the browser */
const process = new RequestAnimationFrameProcess(30);
process.start(() => {
if (this.notifyValue) {
this.itownsView.notifyChange();
}
});
}
addListeners() {
this.initCBDrop();
this.initInputListener();
}
removeListeners() {
window.removeEventListener('keydown', this.hidePlaneListener);
window.removeEventListener('keydown', this.previousListener);
window.removeEventListener('keydown', this.nextListener);
document.body.removeEventListener('drop', this.dropListener);
document.body.removeEventListener('dragover', this.dragOverListener);
}
dispose() {
this.domElement.remove();
this.stopLoopSlideShow();
if (this.plane) {
this.plane.removeFromParent();
}
this.itownsView.notifyChange();
this.removeListeners();
}
/**
* It loads the textures and videos of the slideshow and stores them in the `this.textureFiles` array
*
* @param {number} slideIndex - the index of the slideshow in the config file
*/
setSlideshowInConfig(slideIndex) {
if (isNaN(slideIndex)) return;
const slide = this.slides[slideIndex];
const folder = slide.folder;
const diapos = slide.diapositives;
this.textureFiles = [];
for (let i = 0; i < diapos.length; i++) {
this.textureFiles.push(this.createDefaultTextureFile(i));
const xhr = new XMLHttpRequest();
xhr.open('GET', folder.concat('/'.concat(diapos[i])));
xhr.onload = (response) => {
const type = response.target.getResponseHeader('Content-Type');
if (type.includes('image')) {
new THREE.TextureLoader().load(
response.target.responseURL,
(texture) => {
// Rotate the texture with
texture.center.set(0.5, 0.5);
texture.rotation = this.textureRotation;
this.textureFiles[i] = {
index: i,
name: diapos[i],
size: {
height: texture.image.height,
width: texture.image.width,
},
texture: texture,
};
if (i == 0) this.setTexture(0);
}
);
} else if (type.includes('video')) {
const video = document.createElement('video');
video.src = response.target.responseURL;
video.autoplay = false;
video.muted = false;
video.loop = true;
video.load();
video.onloadedmetadata = () => {
const videoTexture = new THREE.VideoTexture(video);
// Rotate the texture with
videoTexture.center.set(0.5, 0.5);
videoTexture.rotation = this.textureRotation;
this.textureFiles[i] = {
index: i,
name: diapos[i],
size: {
height: video.videoHeight,
width: video.videoWidth,
},
texture: videoTexture,
video: video,
};
if (i == 0) this.setTexture(0);
};
} else {
console.error(
response.target.responseURL,
' is not a valid video or image file'
);
}
};
xhr.send();
}
this.setTexture(0);
}
/** Set the callback function of event 'drop' @warn !event.preventDefault! */
initCBDrop() {
const body = document.body;
this.dropListener = (event) => {
event.preventDefault();
// Setting the value of the select element to null.
this.slideSelect.value = null;
if (!this.plane) return;
const files = Array.from(event.dataTransfer.files);
files.sort(function (a, b) {
return a.name.localeCompare(b.name);
});
this.textureFiles = [];
for (let i = 0; i < files.length; i++) {
this.textureFiles.push(this.createDefaultTextureFile(i));
const file = files[i];
if (file) {
try {
const reader = new FileReader();
reader.onload = (data) => {
if (file.type.includes('image/')) {
new THREE.TextureLoader().load(
data.target.result,
(texture) => {
// Rotate the texture with
texture.center.set(0.5, 0.5);
texture.rotation = this.textureRotation;
this.textureFiles[i] = {
index: i,
name: file.name,
texture: texture,
size: {
height: texture.image.height,
width: texture.image.width,
},
};
if (i == 0) this.setTexture(0);
}
);
} else if (file.type.includes('video/')) {
const video = document.createElement('video');
video.src = data.target.result;
video.autoplay = false;
video.muted = false;
video.loop = true;
video.load();
video.onloadedmetadata = () => {
const videoTexture = new THREE.VideoTexture(video);
// Rotate the video texture with
videoTexture.center.set(0.5, 0.5);
videoTexture.rotation = this.textureRotation;
this.textureFiles[i] = {
index: i,
name: file.name,
texture: videoTexture,
video: video,
size: {
height: video.videoHeight,
width: video.videoWidth,
},
};
if (i == 0) this.setTexture(0);
};
}
};
reader.readAsDataURL(file);
} catch (e) {
throw new Error(e);
}
}
}
this.setTexture(0);
};
body.addEventListener('drop', this.dropListener);
this.dragover = (event) => {
event.preventDefault();
};
body.addEventListener('dragover', this.dragover, false);
}
/** Create a default texture and put in `this.defaultTexture` */
initDefaultTexture() {
const canvas = document.createElement('canvas');
canvas.height = 512;
canvas.width = 512;
const ctx = canvas.getContext('2d');
ctx.beginPath();
const colors = [
'red',
'orange',
'DarkOliveGreen',
'SpringGreen',
'cyan',
'MidnightBlue',
'MediumVioletRed',
];
for (let i = 0; i < colors.length; i++) {
ctx.beginPath();
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.width);
gradient.addColorStop(0, colors[i]);
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(
(canvas.width / colors.length) * i,
0,
(canvas.width / colors.length) * (i + 1),
canvas.height
);
}
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.font = '70px Arial';
const stringDefaultTexture = 'Default Texture';
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillText(stringDefaultTexture, 256, 256);
this.defaultTexture = new THREE.CanvasTexture(canvas);
}
/**
* It creates a new texture file object with the default texture and returns it
*
* @param {number} index - The index of the texture file.
* @returns {TextureFile} A new texture file object.
*/
createDefaultTextureFile(index) {
const newTextureFile = {
index: index,
name: 'DEFAULT',
texture: this.defaultTexture,
size: {
height: this.defaultTexture.image.height,
width: this.defaultTexture.image.width,
},
};
return newTextureFile;
}
/** Create all HTMLElements and fill `this.domElement`*/
initHtml() {
const domElement = document.createElement('div');
const coordinatesElement = this.createInputVector(
['X', 'Y', 'Z'],
'Coordinates',
100
);
domElement.appendChild(coordinatesElement.title);
this.coordinatesInputVector = coordinatesElement.inputVector;
domElement.appendChild(coordinatesElement.inputVector);
const rotationElement = this.createInputVector(
['X', 'Y', 'Z'],
'Rotation',
0.1
);
domElement.appendChild(rotationElement.title);
this.rotationInputVector = rotationElement.inputVector;
domElement.appendChild(rotationElement.inputVector);
const sizeElement = this.createInputVector(
['Height', 'Width'],
'Size',
100
);
domElement.appendChild(sizeElement.title);
this.sizeInputVector = sizeElement.inputVector;
domElement.appendChild(sizeElement.inputVector);
const matchExtentButton = document.createElement('button');
matchExtentButton.id = '_button_match_extent';
matchExtentButton.innerText = 'Match Extent';
this.callbacksHTMLEl.push({
event: 'click',
element: matchExtentButton,
cb: this.matchExtent,
});
domElement.appendChild(matchExtentButton);
const aspectRatioDiv = document.createElement('div');
domElement.appendChild(aspectRatioDiv);
const aspectRatioCheckbox = document.createElement('input');
aspectRatioCheckbox.id = 'aspectRatio';
aspectRatioCheckbox.type = 'checkbox';
this.callbacksHTMLEl.push({
event: 'change',
element: aspectRatioCheckbox,
cb: function (event) {
if (event.target.checked) {
const currentW = this.getSizeValues().width;
const w =
currentW != 0 ? currentW : this.currentTextureFile.size.width;
this.setSizeInputs(new THREE.Vector2(null, w));
}
},
});
this.aspectRatioCheckbox = aspectRatioCheckbox;
aspectRatioDiv.appendChild(aspectRatioCheckbox);
const labelAspectRatio = document.createElement('label');
labelAspectRatio.htmlFor = aspectRatioCheckbox.id;
labelAspectRatio.innerText = 'Aspect Ratio';
aspectRatioDiv.appendChild(labelAspectRatio);
const loopDiv = document.createElement('div');
domElement.appendChild(loopDiv);
const loopCheckbox = document.createElement('input');
loopCheckbox.id = 'loopSlideShow';
loopCheckbox.type = 'checkbox';
this.callbacksHTMLEl.push({
event: 'change',
element: loopCheckbox,
cb: function (event) {
if (this.intervalLoop) this.stopLoopSlideShow();
if (event.target.checked) {
this.loopSlideShow();
}
},
});
this.loopSlideShowCheckbox = loopCheckbox;
loopDiv.appendChild(loopCheckbox);
const labelLoopSlideShow = document.createElement('label');
labelLoopSlideShow.htmlFor = loopCheckbox.id;
labelLoopSlideShow.innerText = 'Loop SlideShow';
loopDiv.appendChild(labelLoopSlideShow);
const durationLoopInSecDiv = document.createElement('div');
domElement.appendChild(durationLoopInSecDiv);
const durationLoopInSecInput = document.createElement('input');
durationLoopInSecInput.id = 'durationLoopInputSlideShow';
durationLoopInSecInput.type = 'number';
durationLoopInSecInput.max = 100;
durationLoopInSecInput.min = 1;
durationLoopInSecInput.setAttribute('value', this.durationLoopInSec);
durationLoopInSecDiv.step = 0.5;
this.callbacksHTMLEl.push({
event: 'change',
element: durationLoopInSecInput,
cb: function (event) {
this.durationLoopInSec = parseFloat(event.target.value);
if (this.intervalLoop) {
this.restartLoopSlideShow();
}
},
});
this.durationLoopInputID = durationLoopInSecInput.id;
durationLoopInSecDiv.appendChild(durationLoopInSecInput);
const durationLoopInSecLabel = document.createElement('label');
durationLoopInSecLabel.htmlFor = durationLoopInSecInput.id;
durationLoopInSecLabel.innerText = 'Duration Loop (s)';
durationLoopInSecDiv.appendChild(durationLoopInSecLabel);
const counterLoopTimeDiv = document.createElement('div');
counterLoopTimeDiv.id = 'counterLoopTimeDivSlideShow';
counterLoopTimeDiv.innerText = this.durationLoopInSec;
this.counterLoopTimeDiv = counterLoopTimeDiv;
durationLoopInSecDiv.appendChild(counterLoopTimeDiv);
const slideSelect = document.createElement('select');
slideSelect.id = 'slideSelect';
domElement.appendChild(slideSelect);
const unsetOptionSlide = document.createElement('option');
unsetOptionSlide.value = 'null';
unsetOptionSlide.innerText = 'Select config slide';
slideSelect.appendChild(unsetOptionSlide);
this.slideSelect = slideSelect;
if (this.slides) {
for (let i = 0; i < this.slides.length; i++) {
const element = this.slides[i];
const option = document.createElement('option');
option.value = i;
option.innerText = element.name;
slideSelect.appendChild(option);
}
this.callbacksHTMLEl.push({
event: 'input',
element: slideSelect,
cb: function (event) {
this.setSlideshowInConfig(event.target.value);
},
});
}
this.domElement = domElement;
}
/**
* It sets the size, coordinates and rotation inputs to the values of the extent
*/
matchExtent() {
const extentCenter = this.extent.center();
this.setSizeInputs(
new THREE.Vector2(
Math.abs(this.extent.west - this.extent.east),
Math.abs(this.extent.north - this.extent.south)
)
);
let elevationZ = 0.1;
const itownsViewLayers = this.itownsView.getLayers();
for (let index = 0; index < itownsViewLayers.length; index++) {
const layer = itownsViewLayers[index];
if (layer.isElevationLayer) {
elevationZ = layer.colorTextureElevationMaxZ;
break;
}
}
this.setCoordinatesInputs(
new THREE.Vector3(extentCenter.x, extentCenter.y, elevationZ)
);
this.setRotationInputs(new THREE.Vector3(0, 0, 0));
}
/**
* Add event listeners to input
*/
initInputListener() {
this.hidePlaneListener = (event) => {
if (event.key.toLowerCase() != 'h') return;
if (!this.plane) return;
this.plane.visible = !this.plane.visible;
this.itownsView.notifyChange();
};
// Hide and show the geometryPlane
window.addEventListener('keydown', this.hidePlaneListener);
this.nextListener = (event) => {
if (event.key != 'ArrowRight') return;
this.nextSlide();
this.restartLoopSlideShow();
};
// Change the next slide
window.addEventListener('keydown', this.nextListener);
this.previousListener = (event) => {
if (event.key != 'ArrowLeft') return;
this.previousSlide();
this.restartLoopSlideShow();
};
// Change the previous slide
window.addEventListener('keydown', this.previousListener);
}
nextSlide() {
if (!this.plane) return;
const newIndexTextureFile =
(this.iCurrentTextureFile + 1) % this.textureFiles.length; // Loop
this.setTexture(newIndexTextureFile);
this.itownsView.notifyChange();
}
previousSlide() {
if (!this.plane) return;
const newIndexTextureFile =
this.iCurrentTextureFile - 1 < 0
? this.textureFiles.length - 1
: this.iCurrentTextureFile - 1;
this.setTexture(newIndexTextureFile);
this.itownsView.notifyChange();
}
/**
* @param {Array.String} labels List of labels name
* @param {string} vectorName Name of the vector
* @param {number} step The step of HTMLElement input (type number)
* @returns {{title:HTMLHeadingElement, inputVector:HTMLDivElement}} The `inputVector` 'div' contains labels and inputs HTMLElements
*/
createInputVector(labels, vectorName, step = 0.5) {
const titleVector = document.createElement('h3');
titleVector.innerText = vectorName;
const inputVector = document.createElement('div');
inputVector.id = vectorName + '_inputVector';
inputVector.style.display = 'grid';
for (let iInput = 0; iInput < labels.length; iInput++) {
const labelElement = document.createElement('label');
labelElement.innerText = labels[iInput];
const componentElement = document.createElement('input');
componentElement.id = vectorName + labelElement.innerText;
componentElement.type = 'number';
componentElement.setAttribute('value', '0');
componentElement.step = step;
labelElement.htmlFor = componentElement.id;
this.callbacksHTMLEl.push({
event: 'change',
element: componentElement,
cb: function (event) {
const value = event.target.value;
const element = event.target;
element.setAttribute('value', value);
if (this.aspectRatioCheckbox.checked)
if (vectorName.toLowerCase().includes('size'))
this.matchRatio(iInput, value);
this.updateVectors();
},
});
inputVector.appendChild(labelElement);
inputVector.appendChild(componentElement);
}
return {
title: titleVector,
inputVector: inputVector,
};
}
/**
* Function called when aspectRatio is checked
*
* @param {number} iInput The index of the input element
* @param {number} value The value of the input element
*/
matchRatio(iInput, value) {
const linkedSizeElement =
this.sizeInputVector.getElementsByTagName('input')[iInput == 0 ? 1 : 0];
const height = this.currentTextureFile.size.height;
const width = this.currentTextureFile.size.width;
const ratio = width / height;
const newValue = iInput == 0 ? value / ratio : value * ratio;
linkedSizeElement.value = newValue;
}
/** Update vectors variables with the values contained in inputs elements in DOM */
updateVectors() {
this.coordinatesVector =
this.inputVectorToVector(this.coordinatesInputVector) ||
new THREE.Vector3();
this.rotationVector =
this.inputVectorToVector(this.rotationInputVector) || new THREE.Vector3();
this.sizeVector =
this.inputVectorToVector(this.sizeInputVector) || new THREE.Vector2();
this.modifyPlane();
}
/**
* Convert inputVector HTMLElement to THREE.Vector
*
* @param {HTMLDivElement} inputVector HTMLElement 'div' contains labels and inputs HTMLElements
* @returns {THREE.Vector} vector
*/
inputVectorToVector(inputVector) {
const inputEls = inputVector.getElementsByTagName('input');
const countEls = inputEls.length;
switch (countEls) {
case 2:
return new THREE.Vector2(inputEls[0].value, inputEls[1].value);
case 3:
return new THREE.Vector3(
inputEls[0].value,
inputEls[1].value,
inputEls[2].value
);
case 4:
return new THREE.Vector4(
inputEls[0].value,
inputEls[1].value,
inputEls[2].value,
inputEls[3].value
);
}
return null;
}
/**
* Set `this.currentTexture`
*
* @param {number} iText The index of the texture to set
*/
setTexture(iText) {
if (this.currentTextureFile && this.currentTextureFile.video) {
this.currentTextureFile.video.pause();
this.currentTextureFile.video.currentTime = 0;
this.notifyValue = false;
}
this.textureFiles.forEach((tf) => {
if (tf.index == iText) {
this.currentTextureFile = tf;
if (tf.video) {
tf.video.play();
this.notifyValue = true;
}
}
});
this.currentTexture = this.currentTextureFile.texture;
this.modifyPlane();
this.itownsView.notifyChange();
this.aspectRatioCheckbox.dispatchEvent(new Event('change'));
}
/** Modify `this.plane` {THREE.Mesh} */
modifyPlane() {
if (!this.plane) {
this.createPlane();
}
this.plane.position.set(
this.coordinatesVector.x,
this.coordinatesVector.y,
this.coordinatesVector.z
);
this.plane.rotation.set(
this.rotationVector.x,
this.rotationVector.y,
this.rotationVector.z
);
this.plane.scale.set(this.sizeVector.x, this.sizeVector.y, 1);
this.plane.material.map = this.currentTexture || this.plane.material.map;
this.plane.updateMatrixWorld();
// this.itownsView.scene.add(this.plane);
this.itownsView.notifyChange();
}
/**
* It creates a PlaneGeometry and a MeshBasicMaterial, to create the Mesh `this.plane`
*/
createPlane() {
const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({
map: this.currentTextureFile.texture,
side: THREE.DoubleSide,
});
this.plane = new THREE.Mesh(geometry, material);
}
/**
* Loop through a slide show of textures
*
*/
loopSlideShow() {
if (!this.loopSlideShowCheckbox.checked) return;
const durationInMS = this.durationLoopInSec * 1000; // Loop event
this.counterLoopTimeDiv.innerText = this.durationLoopInSec;
this.intervalLoop = setInterval(() => {
this.nextSlide();
}, durationInMS);
this.counterIntervalLoop = setInterval(() => {
this.updateCounterLoop();
}, 100);
}
updateCounterLoop() {
const value = parseFloat(this.counterLoopTimeDiv.innerText);
const newValue = value - 0.1 <= 0 ? this.durationLoopInSec : value - 0.1;
this.counterLoopTimeDiv.innerText = newValue.toFixed(1);
}
stopLoopSlideShow() {
clearInterval(this.intervalLoop);
clearInterval(this.counterIntervalLoop);
}
restartLoopSlideShow() {
this.stopLoopSlideShow();
this.loopSlideShow();
}
// DOM GETTERS
get innerContentHtml() {
return this.domElement.outerHTML;
}
get iCurrentTextureFile() {
return this.currentTextureFile.index;
}
// INPUTS ELEMENTS SETTERS
/* Setting the values of the input fields in the DOM. */
setSizeInputs(vec2) {
const sizeInputEls = this.sizeInputVector.getElementsByTagName('input');
if (vec2.x !== null) {
const element0 = sizeInputEls[0];
element0.value = vec2.x;
element0.dispatchEvent(new Event('change'));
}
if (vec2.y !== null) {
const element1 = sizeInputEls[1];
element1.value = vec2.y;
element1.dispatchEvent(new Event('change'));
}
}
setCoordinatesInputs(vec3) {
const coordinatesInputEls =
this.coordinatesInputVector.getElementsByTagName('input');
if (vec3.x !== null) {
const element0 = coordinatesInputEls[0];
element0.value = vec3.x;
element0.dispatchEvent(new Event('change'));
}
if (vec3.y !== null) {
const element1 = coordinatesInputEls[1];
element1.value = vec3.y;
element1.dispatchEvent(new Event('change'));
}
if (vec3.z !== null) {
const element2 = coordinatesInputEls[2];
element2.value = vec3.z || this.coordinatesVector.z;
element2.dispatchEvent(new Event('change'));
}
}
setRotationInputs(vec3) {
const rotationInputEls =
this.rotationInputVector.getElementsByTagName('input');
if (vec3.x !== null) {
const element0 = rotationInputEls[0];
element0.value = vec3.x;
element0.dispatchEvent(new Event('change'));
}
if (vec3.y !== null) {
const element1 = rotationInputEls[1];
element1.value = vec3.y;
element1.dispatchEvent(new Event('change'));
}
if (vec3.z !== null) {
const element2 = rotationInputEls[2];
element2.value = vec3.z;
element2.dispatchEvent(new Event('change'));
}
}
/**
* Get size values contained in inputs elements in DOM
*
* @returns {{height:number,width:number}} sizevalues
*/
getSizeValues() {
const sizeInputEls = this.sizeInputVector.getElementsByTagName('input');
return {
height: parseInt(sizeInputEls[0].value),
width: parseInt(sizeInputEls[1].value),
};
}
}