diff --git a/public/index.html b/public/index.html index 096ad42..f2bea15 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ -
+
@@ -22,13 +22,21 @@ // load map let maps = []; let markerManager = null; + let playerManager = null; BlueMap.loadMaps("data/", bluemap.events).then(loadedMaps => { maps = loadedMaps; - bluemap.setMap(maps[0]); - markerManager = maps[0].createMarkerFileManager(bluemap.markerScene); + markerManager = new BlueMap.MarkerFileManager(bluemap.markerScene, "data/markers.json", maps[0].id, bluemap.events); markerManager.update(); markerManager.setAutoUpdateInterval(1000 * 10); + + playerManager = new BlueMap.PlayerMarkerManager(bluemap.markerScene, "live/players", "", bluemap.events); + playerManager.update(); + playerManager.setAutoUpdateInterval(1000); + + bluemap.setMap(maps[0]).then(() => { + playerManager.worldId = maps[0].world; + }); }); diff --git a/public/live/players b/public/live/players index a2c2260..6b701cd 100644 --- a/public/live/players +++ b/public/live/players @@ -1 +1 @@ -{"players":[{"uuid":"c61be8fb-0a66-4411-aae5-0c67bb4808d4","name":"TBlueF","world":"118a8ec2-ccb1-4e84-be88-1ff568e413ca","position":{"x":-130.62644738873905,"y":72.53260088333643,"z":138.27086962845027}}]} \ No newline at end of file +{"players":[{"uuid":"c61be8fb-0a66-4411-aae5-0c67bb4808d4","name":"TBlueF","world":"118a8ec2-ccb1-4e84-be88-1ff568e413ca","position":{"x":130.62644738873905,"y":82.53260088333643,"z":-138.27086962845027}}]} \ No newline at end of file diff --git a/src/BlueMap.js b/src/BlueMap.js index 0e5030a..7d09856 100644 --- a/src/BlueMap.js +++ b/src/BlueMap.js @@ -3,6 +3,8 @@ import {Map} from "./map/Map"; export { MapViewer } from "./MapViewer"; export * from "./util/Utils"; +export { MarkerFileManager } from "./markers/MarkerFileManager"; +export { PlayerMarkerManager } from "./markers/PlayerMarkerManager"; /** * Loads and returns a promise with an array of Maps loaded from that root-path.
diff --git a/src/MapViewer.js b/src/MapViewer.js index 3a7a374..bbac473 100644 --- a/src/MapViewer.js +++ b/src/MapViewer.js @@ -287,6 +287,7 @@ export class MapViewer { /** * Changes / Sets the map that will be loaded and displayed * @param map {Map} + * @returns Promise */ setMap(map = null) { if (this.map && this.map.isMap) this.map.unload(); diff --git a/src/PlayerMarkerManager.js b/src/PlayerMarkerManager.js deleted file mode 100644 index 42d68e6..0000000 --- a/src/PlayerMarkerManager.js +++ /dev/null @@ -1,116 +0,0 @@ -import {alert} from "../util/Utils"; -import {FileLoader} from "three"; - -export class PlayerMarkerManager { - - constructor(mapViewer, events = null, livePlayerUrl = "/live/players") { - this.mapViewer = mapViewer; - this.events = events ? events : mapViewer.events; - this.playerUrl = livePlayerUrl; - - this._playerData = {players:[]}; - - this.events.addEventListener("bluemapMapChanged", this.onMapChange); - } - - update(){ - return this.loadPlayerData() - .then(playerData => { - if (playerData && Array.isArray(playerData.players)) { - this._playerData = playerData; - this.updateMarkers(); - } - }) - .catch(reason => { - alert(this.events, reason, "warning"); - }); - } - - updateMarkers() { - if (!this.mapViewer.map || !this.mapViewer.map.isLoaded) return; - let markerset = this.getMarkerset(); - - // map of uuid to playerdata - let playerMap = {}; - this._playerData.players.forEach(player => { - if (!player.uuid) return; - - // fill defaults - player = { - name: player.uuid, - world: "", - position: {}, - rotation: {}, - ...player - }; - - player.position = { - x: 0, - y: 0, - z: 0, - ...player.position - }; - - player.rotation = { - yaw: 0, - pitch: 0, - roll: 0, - ...player.rotation - }; - - playerMap[player.uuid] = player; - }); - - //update existing markers - markerset.marker.forEach(marker => { - if (!marker.isPlayerMarker) return; - if (!playerMap[marker.playerUuid]) return; - let player = playerMap[marker.playerUuid]; - - - }); - } - - getMarkerset() { - //init markerset - let markerset = this.mapViewer.map.markerManager.markerSets["bm-live-players"]; - if (!markerset || !markerset.isMarkerSet){ - markerset = this.mapViewer.map.markerManager.createMarkerSet("bm-live-players"); - } - - return markerset; - } - - onMapChange = () => { - this.updateMarkers(); - } - - dispose() { - this.events.removeEventListener("bluemapMapChanged", this.onMapChange); - - this._playerData = {players:[]}; - this.updateMarkers(); - } - - /** - * Loads the playerdata - * @returns {Promise} - */ - loadPlayerData() { - return new Promise((resolve, reject) => { - //alert(this.events, `Loading players from '${this.playerUrl}'...`, "fine"); - - let loader = new FileLoader(); - loader.setResponseType("json"); - loader.load(this.playerUrl, - playerData => { - if (!playerData) reject(`Failed to parse '${this.playerUrl}'!`); - else resolve(playerData); - }, - () => {}, - () => reject(`Failed to load '${this.playerUrl}'!`) - ) - }); - } - -} \ No newline at end of file diff --git a/src/map/Map.js b/src/map/Map.js index 41f06bc..f279ba8 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -7,6 +7,7 @@ import {alert, dispatchEvent, hashTile, stringToImage} from "../util/Utils"; import {TileManager} from "./TileManager"; import {TileLoader} from "./TileLoader"; import {MarkerFileManager} from "../markers/MarkerFileManager"; +import {PlayerMarkerManager} from "../markers/PlayerMarkerManager"; export class Map { @@ -334,15 +335,6 @@ export class Map { } } - /** - * Creates a MarkerFileManager that is loading and updating the markers for this map. - * @param markerScene {Scene} - The scene to which all markers will be added - * @returns {MarkerFileManager} - */ - createMarkerFileManager(markerScene) { - return new MarkerFileManager(markerScene, this.dataUrl + "../markers.json", this.id, this.events); - } - dispose() { this.unload(); } diff --git a/src/markers/HtmlMarker.js b/src/markers/HtmlMarker.js index 6d943ee..db39dec 100644 --- a/src/markers/HtmlMarker.js +++ b/src/markers/HtmlMarker.js @@ -19,7 +19,7 @@ export class HtmlMarker extends Marker { this.fadeDistanceMax = Number.MAX_VALUE; this.addEventListener( 'removed', () => { - this.element.parentNode.removeChild(this.element); + if (this.element.parentNode) this.element.parentNode.removeChild(this.element); }); this.add(this.elementObject); diff --git a/src/markers/Marker.js b/src/markers/Marker.js index 34b8ac5..70fd74f 100644 --- a/src/markers/Marker.js +++ b/src/markers/Marker.js @@ -35,12 +35,7 @@ export class Marker extends Object3D { * @returns {number} - opacity between 0 and 1 */ static calculateDistanceOpacity(position, camera, fadeDistanceMin, fadeDistanceMax) { - //calculate "orthographic distance" to marker - Marker._posRelativeToCamera.subVectors(position, camera.position); - camera.getWorldDirection(Marker._cameraDirection); - let distance = Marker._posRelativeToCamera.dot(Marker._cameraDirection); - - //calculate opacity based on (min/max)distance + let distance = Marker.calculateDistanceToCameraPlane(position, camera); let minDelta = (distance - fadeDistanceMin) / fadeDistanceMin; let maxDelta = (distance - fadeDistanceMax) / (fadeDistanceMax * 0.5); return Math.min( @@ -49,4 +44,15 @@ export class Marker extends Object3D { ); } + /** + * @param position {Vector3} + * @param camera {THREE.Camera} + * @returns {number} + */ + static calculateDistanceToCameraPlane (position, camera) { + Marker._posRelativeToCamera.subVectors(position, camera.position); + camera.getWorldDirection(Marker._cameraDirection); + return Marker._posRelativeToCamera.dot(Marker._cameraDirection); + } + } \ No newline at end of file diff --git a/src/markers/MarkerFileManager.js b/src/markers/MarkerFileManager.js index 7071274..94d314b 100644 --- a/src/markers/MarkerFileManager.js +++ b/src/markers/MarkerFileManager.js @@ -1,4 +1,4 @@ -import {FileLoader, Scene} from "three"; +import {Scene} from "three"; import {MarkerSet} from "./MarkerSet"; import {ShapeMarker} from "./ShapeMarker"; import {alert} from "../util/Utils"; @@ -6,11 +6,12 @@ import {ExtrudeMarker} from "./ExtrudeMarker"; import {LineMarker} from "./LineMarker"; import {HtmlMarker} from "./HtmlMarker"; import {PoiMarker} from "./PoiMarker"; +import {MarkerManager} from "./MarkerManager"; /** * A manager for loading and updating markers from a markers.json file */ -export class MarkerFileManager { +export class MarkerFileManager extends MarkerManager { /** * @constructor @@ -20,120 +21,12 @@ export class MarkerFileManager { * @param events {EventTarget} */ constructor(markerScene, fileUrl, mapId, events = null) { + super(markerScene, fileUrl, events); Object.defineProperty(this, 'isMarkerFileManager', {value: true}); - this.markerScene = markerScene; - this.fileUrl = fileUrl; this.mapId = mapId; - this.events = events; - - /** @type {Map} */ - this.markerSets = new Map(); - /** @type {Map} */ - this.markers = new Map(); - - /** @type {NodeJS.Timeout} */ - this._updateInterval = null; } - /** - * Sets the automatic-update frequency, setting this to 0 or negative disables automatic updates (default). - * This is better than using setInterval() on update() because this will wait for the update to finish before requesting the next update. - * @param ms - interval in milliseconds - */ - setAutoUpdateInterval(ms) { - if (this._updateInterval) clearInterval(this._updateInterval); - if (ms > 0) { - let autoUpdate = () => { - this.update().finally(() => { - this._updateInterval = setTimeout(autoUpdate, ms); - }); - }; - - this._updateInterval = setTimeout(autoUpdate, ms); - } - } - - /** - * Loads the marker-file and updates all managed markers. - * @returns {Promise} - A promise completing when the markers finished updating - */ - update() { - return this.loadMarkerFile() - .then(markerFileData => this.updateFromData(markerFileData)) - .catch(error => { - alert(this.events, error, "error"); - }); - } - - /** - * Stops automatic-updates and disposes all markersets and markers managed by this manager - */ - dispose() { - this.setAutoUpdateInterval(0); - this.markerSets.forEach(markerSet => markerSet.dispose()); - } - - /** - * @private - * Adds a MarkerSet to this Manager, removing any existing markerSet with this id first. - * @param markerSet {MarkerSet} - */ - addMarkerSet(markerSet) { - this.removeMarkerSet(markerSet.markerSetId); - - this.markerSets.set(markerSet.markerSetId, markerSet); - this.markerScene.add(markerSet) - } - - /** - * @private - * Removes a MarkerSet from this Manager - * @param setId {string} - The id of the MarkerSet - */ - removeMarkerSet(setId) { - let markerSet = this.markerSets.get(setId); - - if (markerSet) { - this.markerScene.remove(markerSet); - this.markerSets.delete(setId); - markerSet.dispose(); - } - } - - /** - * @private - * Adds a marker to this manager - * @param markerSet {MarkerSet} - * @param marker {Marker} - */ - addMarker(markerSet, marker) { - this.removeMarker(marker.markerId); - - this.markers.set(marker.markerId, marker); - markerSet.add(marker); - } - - /** - * @private - * Removes a marker from this manager - * @param markerId {string} - */ - removeMarker(markerId) { - let marker = this.markers.get(markerId); - - if (marker) { - if (marker.parent) marker.parent.remove(marker); - this.markers.delete(markerId); - marker.dispose(); - } - } - - /** - * @private - * Updates all managed markers using the provided data. - * @param markerData {object} - The data object, usually parsed json from a markers.json - */ updateFromData(markerData) { if (!Array.isArray(markerData.markerSets)) return; let updatedMarkerSets = new Set(); @@ -144,7 +37,7 @@ export class MarkerFileManager { let markerSet = this.updateMarkerSetFromData(markerSetData); updatedMarkerSets.add(markerSet); } catch (err) { - alert(this.events, "Failed to parse markerset-data: " + err, "fine"); + alert(this.events, err, "fine"); } }); @@ -189,7 +82,7 @@ export class MarkerFileManager { let marker = this.updateMarkerFromData(markerSet, markerData); updatedMarkers.add(marker); } catch (err) { - alert(this.events, "Failed to parse marker-data: " + err, "fine"); + alert(this.events, err, "fine"); console.debug(err); } }); @@ -241,24 +134,4 @@ export class MarkerFileManager { return marker; } - /** - * @private - * Loads the marker file - * @returns {Promise} - A promise completing with the parsed json object from the loaded file - */ - loadMarkerFile() { - return new Promise((resolve, reject) => { - let loader = new FileLoader(); - loader.setResponseType("json"); - loader.load(this.fileUrl, - markerFileData => { - if (!markerFileData) reject(`Failed to parse '${this.fileUrl}'!`); - else resolve(markerFileData); - }, - () => {}, - () => reject(`Failed to load '${this.fileUrl}'!`) - ) - }); - } - } \ No newline at end of file diff --git a/src/markers/MarkerManager.js b/src/markers/MarkerManager.js new file mode 100644 index 0000000..de8eb86 --- /dev/null +++ b/src/markers/MarkerManager.js @@ -0,0 +1,158 @@ +import {FileLoader, Scene} from "three"; +import {MarkerSet} from "./MarkerSet"; +import {alert} from "../util/Utils"; + +/** + * A manager for loading and updating markers from a file + */ +export class MarkerManager { + + /** + * @constructor + * @param markerScene {Scene} - The scene to which all markers will be added + * @param fileUrl {string} - The marker file from which this manager updates its markers + * @param events {EventTarget} + */ + constructor(markerScene, fileUrl, events = null) { + Object.defineProperty(this, 'isMarkerManager', {value: true}); + + this.markerScene = markerScene; + this.fileUrl = fileUrl; + this.events = events; + + /** @type {Map} */ + this.markerSets = new Map(); + /** @type {Map} */ + this.markers = new Map(); + + /** @type {NodeJS.Timeout} */ + this._updateInterval = null; + } + + /** + * Sets the automatic-update frequency, setting this to 0 or negative disables automatic updates (default). + * This is better than using setInterval() on update() because this will wait for the update to finish before requesting the next update. + * @param ms - interval in milliseconds + */ + setAutoUpdateInterval(ms) { + if (this._updateInterval) clearInterval(this._updateInterval); + if (ms > 0) { + let autoUpdate = () => { + this.update().finally(() => { + this._updateInterval = setTimeout(autoUpdate, ms); + }); + }; + + this._updateInterval = setTimeout(autoUpdate, ms); + } + } + + /** + * Loads the marker-file and updates all managed markers. + * @returns {Promise} - A promise completing when the markers finished updating + */ + update() { + return this.loadMarkerFile() + .then(markerFileData => this.updateFromData(markerFileData)) + .catch(error => { + alert(this.events, error, "warning"); + }); + } + + /** + * Stops automatic-updates and disposes all markersets and markers managed by this manager + */ + dispose() { + this.setAutoUpdateInterval(0); + this.markerSets.forEach(markerSet => markerSet.dispose()); + } + + /** + * Removes all markers managed by this marker-manager + */ + clear() { + this.markerSets.forEach(markerSet => this.removeMarkerSet(markerSet.markerSetId)); + } + + /** + * @protected + * Adds a MarkerSet to this Manager, removing any existing markerSet with this id first. + * @param markerSet {MarkerSet} + */ + addMarkerSet(markerSet) { + this.removeMarkerSet(markerSet.markerSetId); + + this.markerSets.set(markerSet.markerSetId, markerSet); + this.markerScene.add(markerSet) + } + + /** + * @protected + * Removes a MarkerSet from this Manager + * @param setId {string} - The id of the MarkerSet + */ + removeMarkerSet(setId) { + let markerSet = this.markerSets.get(setId); + + if (markerSet) { + this.markerScene.remove(markerSet); + this.markerSets.delete(setId); + markerSet.dispose(); + } + } + + /** + * @protected + * Adds a marker to this manager + * @param markerSet {MarkerSet} + * @param marker {Marker} + */ + addMarker(markerSet, marker) { + this.removeMarker(marker.markerId); + + this.markers.set(marker.markerId, marker); + markerSet.add(marker); + } + + /** + * @protected + * Removes a marker from this manager + * @param markerId {string} + */ + removeMarker(markerId) { + let marker = this.markers.get(markerId); + + if (marker) { + if (marker.parent) marker.parent.remove(marker); + this.markers.delete(markerId); + marker.dispose(); + } + } + + /** + * Updates all managed markers using the provided data. + * @param markerData {object} - The data object, usually parsed json from a markers.json + */ + updateFromData(markerData) {} + + /** + * @private + * Loads the marker file + * @returns {Promise} - A promise completing with the parsed json object from the loaded file + */ + loadMarkerFile() { + return new Promise((resolve, reject) => { + let loader = new FileLoader(); + loader.setResponseType("json"); + loader.load(this.fileUrl, + markerFileData => { + if (!markerFileData) reject(`Failed to parse '${this.fileUrl}'!`); + else resolve(markerFileData); + }, + () => {}, + () => reject(`Failed to load '${this.fileUrl}'!`) + ) + }); + } + +} \ No newline at end of file diff --git a/src/markers/PlayerMarker.js b/src/markers/PlayerMarker.js new file mode 100644 index 0000000..c5e41bf --- /dev/null +++ b/src/markers/PlayerMarker.js @@ -0,0 +1,111 @@ +import {Marker} from "./Marker"; +import {CSS2DObject} from "../util/CSS2DRenderer"; +import {animate, EasingFunctions, htmlToElement} from "../util/Utils"; +import {Vector3} from "three"; + +export class PlayerMarker extends Marker { + + /** + * @param markerId {string} + * @param playerUuid {string} + */ + constructor(markerId, playerUuid) { + super(markerId); + Object.defineProperty(this, 'isPlayerMarker', {value: true}); + this.markerType = "player"; + + this.playerUuid = playerUuid; + + this.elementObject = new CSS2DObject(htmlToElement(` +
+ playerhead +
+
+ `)); + this.elementObject.onBeforeRender = (renderer, scene, camera) => this.onBeforeRender(renderer, scene, camera); + + this.playerHeadElement = this.element.getElementsByTagName("img")[0]; + this.playerNameElement = this.element.getElementsByTagName("div")[0]; + + this.addEventListener( 'removed', () => { + if (this.element.parentNode) this.element.parentNode.removeChild(this.element); + }); + + this.playerHeadElement.addEventListener('error', () => { + this.playerHeadElement.src = "assets/playerheads/steve.png"; + }, {once: true}); + + this.add(this.elementObject); + } + + /** + * @returns {Element} + */ + get element() { + return this.elementObject.element; + } + + onBeforeRender(renderer, scene, camera) { + this.element.setAttribute("distance-data", Marker.calculateDistanceToCameraPlane(this.position, camera).toString()); + } + + /** + * @typedef PlayerLike {{ + * uuid: string, + * name: string, + * world: string, + * position: {x: number, y: number, z: number}, + * rotation: {yaw: number, pitch: number, roll: number} + * }} + */ + + /** + * @param markerData {PlayerLike} + */ + updateFromData(markerData) { + + // animate position update + let pos = markerData.position || {}; + if (!this.position.x && !this.position.y && !this.position.z) { + this.position.set( + pos.x || 0, + pos.y || 0, + pos.z || 0 + ); + } else { + let startPos = { + x: this.position.x, + y: this.position.y, + z: this.position.z, + } + let deltaPos = { + x: (pos.x || 0) - startPos.x, + y: (pos.y || 0) - startPos.y, + z: (pos.z || 0) - startPos.z, + } + if (deltaPos.x || deltaPos.y || deltaPos.z) { + animate(progress => { + let ease = EasingFunctions.easeInOutCubic(progress); + this.position.set( + startPos.x + deltaPos.x * ease || 0, + startPos.y + deltaPos.y * ease || 0, + startPos.z + deltaPos.z * ease || 0 + ); + }, 500); + } + } + + // update name + let name = markerData.name || this.playerUuid; + if (this.playerNameElement.innerHTML !== name) + this.playerNameElement.innerHTML = name; + + } + + dispose() { + super.dispose(); + + if (this.element.parentNode) this.element.parentNode.removeChild(this.element); + } + +} \ No newline at end of file diff --git a/src/markers/PlayerMarkerManager.js b/src/markers/PlayerMarkerManager.js new file mode 100644 index 0000000..2d2428d --- /dev/null +++ b/src/markers/PlayerMarkerManager.js @@ -0,0 +1,101 @@ +import {MarkerManager} from "./MarkerManager"; +import {alert} from "../util/Utils"; +import {MarkerSet} from "./MarkerSet"; +import {PlayerMarker} from "./PlayerMarker"; + +export class PlayerMarkerManager extends MarkerManager { + + /** + * @constructor + * @param markerScene {THREE.Scene} - The scene to which all markers will be added + * @param playerDataUrl {string} - The marker file from which this manager updates its markers + * @param worldId {string} - The worldId of the world for which the markers should be loaded + * @param events {EventTarget} + */ + constructor(markerScene, playerDataUrl, worldId, events = null) { + super(markerScene, playerDataUrl, events); + Object.defineProperty(this, 'isPlayerMarkerManager', {value: true}); + + this.worldId = worldId; + + this.getPlayerMarkerSet(); + } + + /** + * @param markerData {{players: PlayerLike[]}} + */ + updateFromData(markerData) { + + /** @type Set */ + let updatedPlayerMarkers = new Set(); + + // update + if (Array.isArray(markerData.players)) { + markerData.players.forEach(playerData => { + try { + let playerMarker = this.updatePlayerMarkerFromData(playerData); + updatedPlayerMarkers.add(playerMarker); + } catch (err) { + alert(this.events, err, "fine"); + } + }); + } + + // remove + this.markers.forEach((playerMarker, markerId) => { + if (!updatedPlayerMarkers.has(playerMarker)) { + this.removeMarker(markerId); + } + }); + + } + + /** + * @private + * @param markerData {PlayerLike} + * @returns PlayerMarker + */ + updatePlayerMarkerFromData(markerData) { + let playerUuid = markerData.uuid; + if (!playerUuid) throw new Error("player-data has no uuid!"); + let markerId = "bm-player-" + playerUuid; + + /** @type PlayerMarker */ + let marker = this.markers.get(markerId); + + let markerSet = this.getPlayerMarkerSet(); + + // create new if not existent of wrong type + if (!marker || !marker.isPlayerMarker) { + marker = new PlayerMarker(markerId, playerUuid); + this.addMarker(markerSet, marker); + } + + // make sure marker is in the correct MarkerSet + if (marker.parent !== markerSet) markerSet.add(marker); + + // update + marker.updateFromData(markerData); + + // hide if wrong world + marker.visible = markerData.world === this.worldId; + + return marker; + } + + /** + * @private + * @returns {MarkerSet} + */ + getPlayerMarkerSet() { + let playerMarkerSet = this.markerSets.get("bm-players"); + + if (!playerMarkerSet) { + playerMarkerSet = new MarkerSet("bm-players"); + this.addMarkerSet(playerMarkerSet); + } + + return playerMarkerSet; + } + +} \ No newline at end of file diff --git a/src/util/CSS2DRenderer.js b/src/util/CSS2DRenderer.js index 734ea9b..38011cf 100644 --- a/src/util/CSS2DRenderer.js +++ b/src/util/CSS2DRenderer.js @@ -102,7 +102,7 @@ var CSS2DRenderer = function () { element.style.oTransform = style; element.style.transform = style; - element.style.display = ( parentVisible && object.visible && vector.z >= - 1 && vector.z <= 1 && element.style.opacity > 0 ) ? '' : 'none'; + element.style.display = ( parentVisible && object.visible && vector.z >= - 1 && vector.z <= 1 && element.style.opacity !== "0" ) ? '' : 'none'; var objectData = { distanceToCameraSquared: getDistanceToSquared( camera, object ) diff --git a/src/util/Utils.js b/src/util/Utils.js index bedc595..270e010 100644 --- a/src/util/Utils.js +++ b/src/util/Utils.js @@ -84,7 +84,7 @@ export const dispatchEvent = (element, event, detail = {}) => { * - warning * - error * @param element {EventTarget} the element on that the event is dispatched - * @param message {string} + * @param message {object} * @param level {string} */ export const alert = (element, message, level = "info") => { @@ -176,6 +176,53 @@ export const animate = function (animationFrame, durationMs = 1000, postAnimatio return animation; } +/** + * Source: https://gist.github.com/gre/1650294 + * @type {{ + * easeOutCubic: (function(number): number), + * linear: (function(number): number), + * easeOutQuint: (function(number): number), + * easeInQuart: (function(number): number), + * easeInOutQuint: (function(number): number), + * easeInQuad: (function(number): number), + * easeOutQuart: (function(number): number), + * easeInCubic: (function(number): number), + * easeInQuint: (function(number): number), + * easeOutQuad: (function(number): number), + * easeInOutQuad: (function(number): number), + * easeInOutCubic: (function(number): number), + * easeInOutQuart: (function(number): number) + * }} + */ +export const EasingFunctions = { + // no easing, no acceleration + linear: t => t, + // accelerating from zero velocity + easeInQuad: t => t*t, + // decelerating to zero velocity + easeOutQuad: t => t*(2-t), + // acceleration until halfway, then deceleration + easeInOutQuad: t => t<.5 ? 2*t*t : -1+(4-2*t)*t, + // accelerating from zero velocity + easeInCubic: t => t*t*t, + // decelerating to zero velocity + easeOutCubic: t => (--t)*t*t+1, + // acceleration until halfway, then deceleration + easeInOutCubic: t => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1, + // accelerating from zero velocity + easeInQuart: t => t*t*t*t, + // decelerating to zero velocity + easeOutQuart: t => 1-(--t)*t*t*t, + // acceleration until halfway, then deceleration + easeInOutQuart: t => t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t, + // accelerating from zero velocity + easeInQuint: t => t*t*t*t*t, + // decelerating to zero velocity + easeOutQuint: t => 1+(--t)*t*t*t*t, + // acceleration until halfway, then deceleration + easeInOutQuint: t => t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t +} + /** * Returns the offset position of an element *