57 Commits
Author SHA1 Message Date
Sebastian Kupke f87b19140b Removed header from test images. 2022-05-05 12:31:58 +02:00
Sebastian Kupke 63ebbba42b Added second resolution test. 2022-05-05 12:29:27 +02:00
Sebastian Kupke 72c6abf031 Resolution tests. 2022-05-04 15:18:11 +02:00
Sebastian Kupke 9db52a004a Added scatter resolution test. 2022-05-04 13:27:31 +02:00
Sebastian Kupke 527fcb993c Removed videos from resolution test. 2022-05-04 09:53:55 +02:00
Sebastian Kupke c41209d0fc Added description to resolution. 2022-05-04 09:10:01 +02:00
Sebastian Kupke 7e9f0159e8 Added resolution test. 2022-05-03 16:50:19 +02:00
Uwe Oestermeier 074fb67906 Fixed problem with outdated loader and error handler.s 2022-05-02 16:41:22 +02:00
Sebastian Kupke 51b0ef4b1b Added VSCode workspace file. 2022-05-02 11:13:03 +02:00
Sebastian Kupke 1efe94ef97 Bump version number. 2022-04-29 10:30:05 +02:00
Sebastian Kupke 7f068c5d94 Fixed resize fullscreen bug. 2022-04-29 10:28:59 +02:00
Uwe Oestermeier eff934e8b5 Moved the addVelocity method after the transform to allow a modification of the delta within the transform method. To make this destructive side effect more explicit transform could return a modified value. But that could effect several projects. 2021-04-28 17:02:54 +02:00
Sebastian Kupke 266a770c8e Merge branch 'master' into obersalzberg
# Conflicts:
#	dist/iwmlib.js
#	dist/iwmlib.pixi.js
2021-03-15 17:02:08 +01:00
Uwe Oestermeier a5f94e0a5f Reverted changes from commit fa0256d782 and moved addVelocity from _move back to old locations. 2021-03-15 15:39:07 +01:00
Uwe Oestermeier fff7a4f685 Fixed import order. 2021-03-01 08:58:44 +01:00
Sebastian Kupke d0ae8893cc Version 1.1.1. 2021-02-24 16:15:20 +01:00
Sebastian Kupke 1b4eee6498 Merge branch 'master' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib 2021-02-24 16:14:26 +01:00
Sebastian Kupke ac779af339 Popup close buttons can take a URL. 2021-02-24 16:13:50 +01:00
Severin Opel 1316a727f3 Fixed offset error. 2021-02-01 13:54:40 +01:00
Severin Opel be950ec8c1 Tried fix scatter. 2021-02-01 13:40:57 +01:00
Severin Opel ca5a39d661 Changed wrapper and events to allow other EventListeners. 2021-01-26 08:01:51 +01:00
Severin Opel fa0256d782 Moved addVelocity to _move. 2021-01-05 16:25:55 +01:00
Severin Opel 114c217ffa Improved logging and adjusted card's click handler
+ Logging now also works when ipcRenderer is appended to the window element.
+ Card's callback replacement method was changed from click to pointerdown.
2020-09-15 11:36:59 +02:00
Severin Opel 28cfdc180c Fixt highlight order on creation. 2020-09-01 13:38:22 +02:00
Uwe Oestermeier 116058ba77 Fixed a problem with clicking transformed rects in card wrappers. 2020-08-27 07:38:35 +02:00
Uwe Oestermeier 4211548b8b Removed console logs. 2020-08-04 18:13:24 +02:00
Uwe Oestermeier 12b17fd7af Fixed call of popup menu commands with parameters.s 2020-08-04 18:07:48 +02:00
Uwe Oestermeier 47b178fbc1 Added event parameter to PopupMenu perform. 2020-08-04 17:35:15 +02:00
Severin Opel 6ab63eb32c Now calling 'this' to allow overloading of methods. 2020-04-21 12:43:55 +02:00
Severin Opel 6445ab3f57 CardLoader.load now checks for ready state. 2020-04-19 13:30:35 +02:00
Severin Opel 129672eb0f Added options object to toggle function. 2020-04-14 12:14:40 +02:00
Uwe Oestermeier 70031d3bfa Removed comment since border case appeared in Edge. 2020-02-25 15:19:01 +01:00
Uwe Oestermeier 9db5723746 Updated dist 2020-02-17 16:20:16 +01:00
Uwe Oestermeier d4ab5c7e9c Merge branch 'master' of https://gitea.iwm-tuebingen.de/IWMBrowser/iwmlib
* 'master' of https://gitea.iwm-tuebingen.de/IWMBrowser/iwmlib:
  Changed scale
  Updated to PixiJS 5.2.1.
2020-02-17 16:19:33 +01:00
Uwe Oestermeier b068fd176f Added lineWidth support. 2020-02-17 16:18:55 +01:00
Uwe Oestermeier f70968fe01 Changed scale 2020-02-07 20:08:44 +01:00
Sebastian Kupke 1c70433b86 Updated to PixiJS 5.2.1. 2020-02-04 16:48:36 +01:00
Uwe Oestermeier 54b685ab42 Changed calling order 2020-02-03 10:06:27 +01:00
Uwe Oestermeier e6f6f6b185 Changed calling order 2020-02-03 10:06:01 +01:00
Uwe Oestermeier 7bb3b696f7 Created new build. 2020-01-31 10:26:41 +01:00
Sebastian Kupke 6cdf5310ba Added graphics alpha workaround. 2020-01-30 15:41:31 +01:00
Sebastian Kupke 1967b791e0 Updated dependencies. 2020-01-22 13:50:40 +01:00
Sebastian Kupke ebb4b9154e Updated pixi-compressed-textures. 2020-01-20 11:33:53 +01:00
Sebastian Kupke b32132c71f Removed stopEvents from RigidContainer. 2020-01-10 14:18:28 +01:00
Sebastian Kupke f398fe4824 Fixed reference bug. 2020-01-09 15:14:27 +01:00
Severin Opel d5b87f23cf Updated generated content (Docs & dist). 2019-12-12 19:04:11 +01:00
Severin Opel 7f2fc93fa3 Reworked overlay itself and doctest. 2019-12-12 19:02:58 +01:00
Severin Opel 32e913c629 Fixed wrongly assigned parameters 2019-12-12 17:23:39 +01:00
Severin Opel 87c966062c Map's cover state now updates every time cover is requested. 2019-12-12 17:01:51 +01:00
Severin Opel d7f867f1a6 Added maps graphics. 2019-12-11 17:30:11 +01:00
Severin Opel e188f3474f Reverted index.css to previous version. 2019-12-11 16:52:23 +01:00
Severin Opel 1e80845aa6 Fixed cover bug. Renamed mapdata to mapprojection. 2019-12-11 16:45:26 +01:00
Severin Opel a85569e54d commited for presentation. 2019-12-11 15:29:59 +01:00
Severin Opel 65fac2f406 Renamed 'MapView' to 'MapViewport'. Added documentation to the maps module. 2019-12-09 18:15:28 +01:00
Severin Opel 60e28f8fe5 Improved doctest css. 2019-12-09 14:29:26 +01:00
Severin Opel 792892cb82 Removed PIXIUtils from geographics. 2019-12-09 14:27:32 +01:00
Severin Opel 9159073483 Fixed GeoGraphics doctest. 2019-12-05 17:06:50 +01:00
181 changed files with 178002 additions and 49442 deletions
+5 -1
View File
@@ -77,6 +77,10 @@ typings/
.fusebox/ .fusebox/
# own # own
*.code-workspace
.history/ .history/
.vscode/ .vscode/
# ignore generated contents-
/doc/out/*
**/thumbnails
**/thumbnail.png
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

+128 -10
View File
@@ -1,15 +1,47 @@
:root {
--white: rgb(250, 250, 250);
--light-gray: rgb(219, 219, 219);
--gray: rgb(66, 66, 66);
--black: rgb(20, 20, 20);
--dark-background: rgb(50, 50, 50);
--border-radius: 5px;
}
html { html {
padding: 0; padding: 0;
font-size: 16px; font-size: 16px;
background: white; background: var(--white);
color: #000; color: var(--black);
font-family: 'Open Sans', Arial, sans-serif; font-family: 'Open Sans', Arial, sans-serif;
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
} }
.dark-mode nav{
border-color: var(--white);
}
nav {
margin-top: 50px;
min-width: 30%;
border-radius: var(--border-radius);
display: inline-block;
padding: 20px 30px;
/* background-color: var(--light-gray); */
border: 1px solid var(--black);
}
nav > h3 {
margin: 0;
}
nav > ol a {
font-weight: bold;
}
html.dark-mode { html.dark-mode {
background: rgb(51, 51, 51); background: var(--dark-background);
color: #ccc; color: #ccc;
} }
@@ -22,16 +54,102 @@ html.dark-mode {
color: white; color: white;
} }
.dark-mode a{ a {
color: #569CD6; color: #569cd6;
font-weight: bold; font-weight: bold;
} }
.dark-mode a:hover{ .dark-mode a:hover {
color: white; color: white;
} }
a{ .dark-mode button {
border: 1px solid rgb(211, 211, 211);
color: rgb(211, 211, 211);
background: linear-gradient(to top, rgb(53, 53, 53), rgb(73, 73, 73));
}
button {
background-color: var(--white);
border: 1px solid var(--gray);
color: var(--gray);
padding: 10px 20px;
border-radius: 5px;
transition: transform 0.2s;
}
label {
display: flex;
align-items: center;
user-select: none;
cursor: pointer;
}
input[type='checkbox'] {
display: none;
}
input[type='checkbox'] + .checkbox {
position: relative;
margin-right: 10px;
width: 32px;
height: 32px;
}
.controls {
display: flex;
}
.controls > * {
margin-right: 1em;
}
.dark-mode .checkbox {
background-color: var(--gray);
border: 1px solid var(--white);
}
.checkbox {
display: inline-block;
border-radius: 3px;
background-color: var(--white);
border: 1px solid var(--black);
}
.checkbox:after {
content: '';
position: absolute;
}
input[type='checkbox']:checked + .checkbox {
background-color: rgb(174, 238, 78);
}
input[type='checkbox']:checked + .checkbox:after {
left: 11px;
top: 6px;
width: 5px;
height: 12px;
border: solid var(--white);
border-width: 0 4px 4px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
button:active {
transform: scale(0.98);
}
button:hover {
filter: brightness(0.95);
}
button:focus {
box-shadow: 0 0 3px 1px #569cd6;
outline: none;
}
a {
text-decoration: none; text-decoration: none;
} }
@@ -53,8 +171,8 @@ a{
.doctest { .doctest {
margin: 0; margin: 0;
padding-top: 50px; padding-top: 20px;
padding-bottom: 50px; padding-bottom: 20px;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.5); box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.5);
} }
.collapsed .doctest { .collapsed .doctest {
@@ -71,7 +189,7 @@ a{
margin-bottom: 1em; margin-bottom: 1em;
} }
.doctest-collapsible-toggle { .doctest-titlebar {
display: flex; display: flex;
align-items: center; align-items: center;
color: whitesmoke; color: whitesmoke;
+30622 -33030
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+181 -170
View File
@@ -9,9 +9,9 @@
static implementationError(klass) { static implementationError(klass) {
let interfaceKeys = Reflect.ownKeys(this.prototype); let interfaceKeys = Reflect.ownKeys(this.prototype);
let classKeys = Reflect.ownKeys(klass.prototype); Reflect.ownKeys(klass.prototype);
for (let key of interfaceKeys) { for (let key of interfaceKeys) {
let interfaceDesc = this.prototype[key]; this.prototype[key];
let classDesc = klass.prototype[key]; let classDesc = klass.prototype[key];
if (typeof classDesc == 'undefined') return 'Missing ' + key if (typeof classDesc == 'undefined') return 'Missing ' + key
} }
@@ -239,43 +239,49 @@
let container = document.createElement('div'); let container = document.createElement('div');
container.className = 'doctest-wrapper'; container.className = 'doctest-wrapper';
let titleParent = container; if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
if (doctest.hasAttribute('data-collapsible')) { let titlebar = document.createElement('div');
let collapsibleToggle = document.createElement('div'); titlebar.className = 'doctest-titlebar';
titlebar.style = 'min-height: 10px;';
container.appendChild(titlebar);
let icon = document.createElement('i'); if (doctest.hasAttribute('data-title')) {
icon.className = 'material-icons'; let title = document.createElement('h6');
collapsibleToggle.appendChild(icon); title.innerText = doctest.getAttribute('data-title');
title.className = 'doctest-section-title';
titlebar.appendChild(title);
}
collapsibleToggle.className = 'doctest-collapsible-toggle'; if (doctest.hasAttribute('data-collapsible')) {
collapsibleToggle.style = 'min-height: 10px;'; let icon = document.createElement('i');
titleParent = collapsibleToggle; icon.className = 'material-icons';
container.appendChild(collapsibleToggle); titlebar.classList.add('doctest-collapsible-toggle');
const collapsedClass = 'collapsed'; if (titlebar.childNodes.length > 0) {
titlebar.insertBefore(icon, titlebar.childNodes[0]);
function setToggleMode(collapse) {
if (collapse) {
container.classList.add(collapsedClass);
icon.innerText = 'arrow_drop_down';
} else { } else {
container.classList.remove(collapsedClass); titlebar.appendChild(icon);
icon.innerText = 'arrow_drop_up';
} }
}
function determineToggleMode() { const collapsedClass = 'collapsed';
setToggleMode(!container.classList.contains(collapsedClass));
}
setToggleMode(doctest.hasAttribute('data-collapsed')); function setToggleMode(collapse) {
collapsibleToggle.addEventListener('click', determineToggleMode); if (collapse) {
} container.classList.add(collapsedClass);
if (doctest.hasAttribute('data-title')) { icon.innerText = 'arrow_drop_down';
let title = document.createElement('h6'); } else {
title.innerText = doctest.getAttribute('data-title'); container.classList.remove(collapsedClass);
title.className = 'doctest-section-title'; icon.innerText = 'arrow_drop_up';
titleParent.appendChild(title); }
}
function determineToggleMode() {
setToggleMode(!container.classList.contains(collapsedClass));
}
setToggleMode(doctest.hasAttribute('data-collapsed'));
titlebar.addEventListener('click', determineToggleMode);
}
} }
let pre = document.createElement('pre'); let pre = document.createElement('pre');
@@ -423,9 +429,12 @@
window.addEventListener( window.addEventListener(
'error', 'error',
event => { event => {
// if (typeof(event.error) == 'undefined') { if (typeof event.error == 'undefined') {
// console.info("Catched undefined error", event) // This sometimes happens in Edge. Since we have no error
// } // position, we cannot do much beside an info log.
console.info('Catched undefined error', event);
return
}
this.appendError(event.error, event.filename); this.appendError(event.error, event.filename);
}, },
true true
@@ -455,7 +464,10 @@
class Events { class Events {
static stop(event) { static stop(event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); // I removed this, as it destroys all the Hammer.js events.
// And also the click event.
// It seems to have no (apparent) negative impact. -SO
// event.stopPropagation()
} }
static extractPoint(event) { static extractPoint(event) {
@@ -608,19 +620,6 @@
static toLine(event) { static toLine(event) {
return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}` return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}`
let result = event.type;
let selector = this.selector(event.target);
result += ' selector: ' + selector;
if (event.target != document.querySelector(selector)) console.log('Cannot resolve', selector);
let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY'];
for (let key of keys) {
try {
result += ' ' + key + ':' + event[key];
} catch (e) {
console.log('Invalid key: ' + key);
}
}
return result
} }
static compareExtractedWithSimulated() { static compareExtractedWithSimulated() {
@@ -1086,9 +1085,18 @@
// Distance == 0.0 indicates an inside relation. // Distance == 0.0 indicates an inside relation.
static distanceToRect(p, r) { static distanceToRect(p, r) {
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x); let dx = 0;
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y); let dy = 0;
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy)) if (p.x < r.x) dx = p.x - r.x;
else if (p.x > r.x + r.width) dx = p.x - (r.x + r.width);
if (p.y < r.y) dy = p.y - r.y;
else if (p.y > r.y + r.height) dy = p.y - (r.y + r.height);
return Math.sqrt(dx * dx + dy * dy)
/* let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
let result = Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
console.log("distanceToRect", p, r, result)
return result */
} }
static fromPageToNode(element, p) { static fromPageToNode(element, p) {
@@ -1716,7 +1724,7 @@
}; };
try { try {
ipc = require('electron').ipcRenderer; ipc = window.ipcRenderer || require('electron').ipcRenderer;
logHandlers.log = message => ipc.send('log', message); logHandlers.log = message => ipc.send('log', message);
logHandlers.warn = message => ipc.send('warn', message); logHandlers.warn = message => ipc.send('warn', message);
logHandlers.error = message => ipc.send('error', message); logHandlers.error = message => ipc.send('error', message);
@@ -2614,8 +2622,8 @@
if (Events.isCaptured(event)) { if (Events.isCaptured(event)) {
return false return false
} }
let captured = this.target.capture(event);
return captured return this.target.capture ? this.target.capture(event) : false
} }
getPosition(event) { getPosition(event) {
@@ -2640,25 +2648,6 @@
result[id] = this.getPosition(event); result[id] = this.getPosition(event);
break break
} }
// case 'TouchEvent':
// // Needs to be observed: Perhaps changedTouches are all we need. If so
// // we can remove the touchEventKey default parameter
// if (touchEventKey == 'all') {
// for(let t of event.targetTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// else {
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// break
default:
break
} }
return result return result
} }
@@ -3159,8 +3148,7 @@
*/ */
class ScatterEvent extends BaseEvent { class ScatterEvent extends BaseEvent {
constructor( constructor(
target, target, { translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
{ translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
) { ) {
super('scatterTransformed', { target: target }); super('scatterTransformed', { target: target });
this.translate = translate; this.translate = translate;
@@ -3221,7 +3209,7 @@
this.velocity = null; this.velocity = null;
this.timestamp = null; this.timestamp = null;
this.onThrowFinished = onThrowFinished; this.onThrowFinished = onThrowFinished;
//console.log("onThrowFinished", onThrowFinished) //console.log("onThrowFinished", onThrowFinished)
} }
observeVelocity() { observeVelocity() {
@@ -3232,6 +3220,9 @@
let t = performance.now(); let t = performance.now();
let dt = t - this.lastframe; let dt = t - this.lastframe;
this.lastframe = t; this.lastframe = t;
// When number is not set or is zero. Use one instead.
let number = delta.number ? delta.number : 1;
if (dt > 0) { if (dt > 0) {
// Avoid division by zero errors later on // Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the // and consider the number of involved pointers sind addVelocity will be called by the
@@ -3239,13 +3230,14 @@
let velocity = { let velocity = {
t: t, t: t,
dt: dt, dt: dt,
dx: delta.x / delta.number, dx: delta.x / number,
dy: delta.y / delta.number dy: delta.y / number
}; };
this.velocities.push(velocity); this.velocities.push(velocity);
while (this.velocities.length > buffer) { while (this.velocities.length > buffer) {
this.velocities.shift(); this.velocities.shift();
} }
} }
} }
@@ -3295,7 +3287,7 @@
animateThrow(time) { animateThrow(time) {
if (this.velocity != null) { if (this.velocity != null) {
let dt = this._throwDeltaTime(); let dt = this._throwDeltaTime();
// console.log("animateThrow", dt) // console.log("animateThrow", dt)
let next = this.nextVelocity(this.velocity); let next = this.nextVelocity(this.velocity);
let prevLength = Points$1.length(this.velocity); let prevLength = Points$1.length(this.velocity);
let nextLength = Points$1.length(next); let nextLength = Points$1.length(next);
@@ -3493,7 +3485,7 @@
let delta = interaction.delta(); let delta = interaction.delta();
if (delta != null) { if (delta != null) {
this.addVelocity(delta);
let rotate = delta.rotate; let rotate = delta.rotate;
let zoom = delta.zoom; let zoom = delta.zoom;
if (this.maxRotation != null) { if (this.maxRotation != null) {
@@ -3514,6 +3506,8 @@
} }
this.transform(delta, zoom, rotate, delta.about); this.transform(delta, zoom, rotate, delta.about);
this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
if (zoom != 1) this.interactionAnchor = delta.about; if (zoom != 1) this.interactionAnchor = delta.about;
} }
} }
@@ -3578,10 +3572,10 @@
keepOnStage(velocity, collision = 0.5) { keepOnStage(velocity, collision = 0.5) {
let stagePolygon = this.containerPolygon; let stagePolygon = this.containerPolygon;
// UO: since keepOnStage is called in nextVelocity we need to // UO: since keepOnStage is called in nextVelocity we need to
// ensure a return value // ensure a return value
if (!stagePolygon) return { x: 0, y: 0 } if (!stagePolygon) return { x: 0, y: 0 }
let polygon = this.polygon; this.polygon;
let bounced = this.bouncing(); let bounced = this.bouncing();
if (bounced) { if (bounced) {
let stage = this.containerBounds; let stage = this.containerBounds;
@@ -3590,7 +3584,7 @@
let dx = this.movableX ? velocity.x : 0; let dx = this.movableX ? velocity.x : 0;
let dy = this.movableY ? velocity.y : 0; let dy = this.movableY ? velocity.y : 0;
let factor = this.throwDamping; let factor = this.throwDamping;
// if (recentered) { // if (recentered) {
if (x < 0) { if (x < 0) {
dx = -dx; dx = -dx;
factor = collision; factor = collision;
@@ -3626,10 +3620,10 @@
onComplete: this.close.bind(this) onComplete: this.close.bind(this)
}); });
} else if (this.scale < this.minScale + this.scaleCloseThreshold) { } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
this.zoom(this.minScale + this.scaleCloseThreshold, { this.zoom(this.minScale + this.scaleCloseThreshold, {
animate: 0.4 animate: 0.4
}); });
} }
} }
rotateDegrees(degrees, anchor) { rotateDegrees(degrees, anchor) {
@@ -3693,6 +3687,7 @@
} }
_move(delta) { _move(delta) {
// this.addVelocity(delta) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
this.x += this.movableX ? delta.x : 0; this.x += this.movableX ? delta.x : 0;
this.y += this.movableX ? delta.y : 0; this.y += this.movableX ? delta.y : 0;
} }
@@ -3730,14 +3725,14 @@
let newOrigin = Points$1.arc(anchor, beta + rotate, distance * thresholdedZoom); let newOrigin = Points$1.arc(anchor, beta + rotate, distance * thresholdedZoom);
let extra = Points$1.subtract(newOrigin, origin); let extra = Points$1.subtract(newOrigin, origin);
let offset = Points$1.subtract(anchor, origin); const anchorOffset = Points$1.subtract(anchor, origin);
this._move(offset); // this._move(offset)
this.scale = newScale; this.scale = newScale;
this.rotation += rotate; this.rotation += rotate;
offset = Points$1.negate(offset); let offset = Points$1.negate(anchorOffset);
offset = Points$1.add(offset, extra); offset = Points$1.add(offset, extra);
offset = Points$1.add(offset, translate); offset = Points$1.add(offset, translate);
this._move(offset); this._move(Points$1.add(anchorOffset, offset));
delta.x += extra.x; delta.x += extra.x;
delta.y += extra.y; delta.y += extra.y;
@@ -4021,8 +4016,7 @@
* @param {DOM node} debugCanvas - Shows debug infos about touches if not null * @param {DOM node} debugCanvas - Shows debug infos about touches if not null
*/ */
constructor( constructor(
element, element, {
{
stopEvents = 'auto', stopEvents = 'auto',
claimEvents = true, claimEvents = true,
useCapture = true, useCapture = true,
@@ -4186,8 +4180,7 @@
class DOMScatter$1 extends AbstractScatter { class DOMScatter$1 extends AbstractScatter {
constructor( constructor(
element, element,
container, container, {
{
startScale = 1.0, startScale = 1.0,
minScale = 0.1, minScale = 0.1,
maxScale = 1.0, maxScale = 1.0,
@@ -4384,10 +4377,10 @@
let r = this.bounds; let r = this.bounds;
let w2 = r.width / 2; let w2 = r.width / 2;
let h2 = r.height / 2; let h2 = r.height / 2;
// if (this.resizable) { // if (this.resizable) {
// w2 *= this.scale // w2 *= this.scale
// h2 *= this.scale // h2 *= this.scale
// } // }
var x = r.left + w2; var x = r.left + w2;
var y = r.top + h2; var y = r.top + h2;
return { x, y } return { x, y }
@@ -4507,7 +4500,8 @@
} }
this._x = x; this._x = x;
this._y = y; this._y = y;
TweenLite.set(this.element, { x: x, y: y }); // this.addVelocity({ x: delta.x, y: delta.y }) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
TweenLite.set(this.element, { x, y });
} }
resizeAfterTransform(zoom) { resizeAfterTransform(zoom) {
@@ -4714,16 +4708,24 @@
let clickRects = activeNodes.map(link => { let clickRects = activeNodes.map(link => {
let rect = link.getBoundingClientRect(); let rect = link.getBoundingClientRect();
// Since the getBoundingClientRect is untransformed we cannot rely on it's size
// We need a transformed bottom right to calculate local width and height
let bottomRight = Points$1.fromPageToNode(element, {
x: rect.x + rect.width,
y: rect.y + rect.height
});
let topLeft = Points$1.fromPageToNode(element, rect); let topLeft = Points$1.fromPageToNode(element, rect);
let width = Math.abs(bottomRight.x - topLeft.x);
let height = Math.abs(bottomRight.y - topLeft.y);
let center = Points$1.fromPageToNode(element, { let center = Points$1.fromPageToNode(element, {
x: rect.x + rect.width / 2, x: rect.x + width / 2,
y: rect.y + rect.height / 2 y: rect.y + height / 2
}); });
return { return {
x: topLeft.x, x: topLeft.x,
y: topLeft.y, y: topLeft.y,
width: rect.width, width,
height: rect.height, height,
center, center,
link link
} }
@@ -4751,14 +4753,19 @@
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a /* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page. proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
Therefore we use the original click event (see constructor). */ Therefore we use the original click event (see constructor). */
if (this.isSVGNode(node)) { // if (this.isSVGNode(node)) {
if (this.triggerSVGClicks) { // if (this.triggerSVGClicks) {
let click = new Event('click'); // let click = new Event('click')
node.dispatchEvent(click); // node.dispatchEvent(click)
} // }
return // return
} // }
node.click(); // node.click()
let click = new Event('click');
click.clientX = event.clientX;
click.clientY = event.clientY;
node.dispatchEvent(click);
} }
nodeTapped(node, event) { nodeTapped(node, event) {
@@ -4946,13 +4953,15 @@
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open('GET', this.src, false); xhr.open('GET', this.src, false);
xhr.onload = e => { xhr.onreadystatechange = e => {
domNode.innerHTML = this.prepare(xhr.response); if (xhr.readyState == 4) {
this.addedNode = domNode.firstElementChild; domNode.innerHTML = this.prepare(xhr.response);
let { width, height } = this.size(this.addedNode); this.addedNode = domNode.firstElementChild;
if (width) this.wantedWidth = width || this.wantedWidth; let { width, height } = this.size(this.addedNode);
if (height) this.wantedHeight = height || this.wantedHeight; if (width) this.wantedWidth = width || this.wantedWidth;
resolve(this); if (height) this.wantedHeight = height || this.wantedHeight;
resolve(this);
}
}; };
xhr.onerror = e => { xhr.onerror = e => {
reject(this); reject(this);
@@ -5753,8 +5762,6 @@
let bottom = parseFloat(this.element.style.bottom); let bottom = parseFloat(this.element.style.bottom);
this.element.style.bottom = bottom - delta.y + 'px'; this.element.style.bottom = bottom - delta.y + 'px';
break break
default:
break
} }
//console.log("onResize", this.onResize) //console.log("onResize", this.onResize)
if (this.onResize) { if (this.onResize) {
@@ -6177,8 +6184,6 @@
x = bbRight; x = bbRight;
if (!this.useEventPosWithBoundingBox) y = (bbTop + bbBottom) / 2; if (!this.useEventPosWithBoundingBox) y = (bbTop + bbBottom) / 2;
break break
default:
break
} }
} }
@@ -6234,8 +6239,6 @@
x += this.notchSize * 2; x += this.notchSize * 2;
x += this.posOffset; x += this.posOffset;
break break
default:
break
} }
this.placeOrigin(x, y); this.placeOrigin(x, y);
} }
@@ -6254,7 +6257,7 @@
/** Configuration object. Return default styles as CSS values. /** Configuration object. Return default styles as CSS values.
*/ */
defaultStyle() { defaultStyle() {
let padding = this.padding; this.padding;
let style = { let style = {
maxWidth: this.maxWidth + 'px', maxWidth: this.maxWidth + 'px',
zIndex: this.zIndex, zIndex: this.zIndex,
@@ -6533,7 +6536,7 @@
* @memberof Popup * @memberof Popup
*/ */
static targetCenter(event) { static targetCenter(event) {
let target = event.target; event.target;
let x = event.pageX; let x = event.pageX;
let y = event.pageY; let y = event.pageY;
let rect = Popup$1.targetRect(event); let rect = Popup$1.targetRect(event);
@@ -6626,10 +6629,10 @@
Elements$1.addClass(item, 'popupMenuItem'); Elements$1.addClass(item, 'popupMenuItem');
this.items[key] = item; this.items[key] = item;
item.onclick = event => { item.onclick = event => {
this.perform(key); this.perform(event, key);
}; };
item.ontap = event => { item.ontap = event => {
this.perform(key); this.perform(event, key);
}; };
item.onmouseover = event => { item.onmouseover = event => {
this.over(event, key); this.over(event, key);
@@ -6648,16 +6651,22 @@
} }
/** Execute a menu command. /** Execute a menu command.
* @param {object} event - The trigger event.
* @param {string} key - The selected key. * @param {string} key - The selected key.
*/ */
perform(key) { perform(event, key) {
let func = this.commands[key]; let func = this.commands[key];
if (this.autoClose) { if (this.autoClose) {
this.close(); this.close();
} }
setTimeout(() => { setTimeout(
func.call(); (event, key) => {
}, 20); func(event, key);
},
20,
event,
key
);
} }
/** Update the menu item denoted by key. /** Update the menu item denoted by key.
@@ -7573,7 +7582,7 @@
reorderArguments(params) { reorderArguments(params) {
// first parameter // first parameter
//-------------------- //--------------------
const element = params[0]; params[0];
// other parameter // other parameter
//-------------------- //--------------------
@@ -7983,10 +7992,13 @@
* @class Highlight * @class Highlight
* @extends {Object} * @extends {Object}
*/ */
const SCALE = 1.5;
class Highlight extends Object { class Highlight extends Object {
static disableAnimations() { static disableAnimations() {
_HighlightEnabled = false; _HighlightEnabled = false;
let expanded = document.querySelectorAll('.' + Highlight.expandedClass); let expanded = document.querySelectorAll('.' + this.expandedClass);
for (let obj of expanded) { for (let obj of expanded) {
this.shrink(obj); this.shrink(obj);
} }
@@ -7997,11 +8009,11 @@
} }
static removeAnimations(svgRoot) { static removeAnimations(svgRoot) {
let expanded = svgRoot.querySelectorAll('.' + Highlight.expandedClass); let expanded = svgRoot.querySelectorAll('.' + this.expandedClass);
for (let obj of expanded) { for (let obj of expanded) {
TweenLite.set(obj, { scale: 1 }); TweenLite.set(obj, { scale: 1 });
obj.classList.remove('zooming'); obj.classList.remove('zooming');
obj.classList.remove(Highlight.expandedClass); obj.classList.remove(this.expandedClass);
} }
let defs = svgRoot.querySelector('defs'); let defs = svgRoot.querySelector('defs');
while (defs.firstChild) { while (defs.firstChild) {
@@ -8031,12 +8043,12 @@
} }
} }
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) { static expand(obj, { scale = SCALE, duration = 3, stroke = 2, onComplete = null } = {}) {
if (obj == null) return if (obj == null) return
//console.log("expand") //console.log("expand")
obj.classList.add('zooming'); obj.classList.add('zooming');
TweenLite.to(obj, duration, { TweenLite.to(obj, duration, {
scale: scale, scale,
onUpdate: () => { onUpdate: () => {
let scale = obj._gsTransform.scaleX; let scale = obj._gsTransform.scaleX;
obj.setAttribute('stroke-width', stroke / scale); obj.setAttribute('stroke-width', stroke / scale);
@@ -8044,7 +8056,7 @@
onComplete: () => { onComplete: () => {
console.log('expand complete'); console.log('expand complete');
obj.classList.remove('zooming'); obj.classList.remove('zooming');
obj.classList.add(Highlight.expandedClass); obj.classList.add(this.expandedClass);
obj.setAttribute('stroke-width', stroke / scale); obj.setAttribute('stroke-width', stroke / scale);
if (onComplete) onComplete(); if (onComplete) onComplete();
} }
@@ -8064,7 +8076,7 @@
onComplete: () => { onComplete: () => {
//console.log("shrink complete") //console.log("shrink complete")
obj.classList.remove('zooming'); obj.classList.remove('zooming');
obj.classList.remove(Highlight.expandedClass); obj.classList.remove(this.expandedClass);
obj.setAttribute('stroke-width', stroke); obj.setAttribute('stroke-width', stroke);
} }
}); });
@@ -8083,9 +8095,9 @@
let id = circle.getAttribute('id'); let id = circle.getAttribute('id');
TweenLite.set(circle, { transformOrigin: '50% 50%' }); TweenLite.set(circle, { transformOrigin: '50% 50%' });
/*if (circle.classList.contains('zooming')) { /*if (circle.classList.contains('zooming')) {
console.log("already zooming") console.log("already zooming")
return return
}*/ }*/
let svgRoot = circle.closest('svg'); let svgRoot = circle.closest('svg');
let circleGroup = circle.parentNode; let circleGroup = circle.parentNode;
@@ -8103,7 +8115,7 @@
let maskImageId = 'maskImage' + id; let maskImageId = 'maskImage' + id;
let maskImage = svgRoot.getElementById(maskImageId); let maskImage = svgRoot.getElementById(maskImageId);
if (circle.classList.contains(Highlight.expandedClass)) { if (circle.classList.contains(this.expandedClass)) {
if (!circle.classList.contains('zooming')) { if (!circle.classList.contains('zooming')) {
this.shrink(circle, { stroke }); this.shrink(circle, { stroke });
this.shrink(maskImage, { stroke }); this.shrink(maskImage, { stroke });
@@ -8126,7 +8138,7 @@
this.shrink(m, { stroke }); this.shrink(m, { stroke });
} }
Highlight._createSVGMask(svgRoot, image, id); this._createSVGMask(svgRoot, image, id);
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` }) // TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
@@ -8136,8 +8148,8 @@
return false return false
} }
static openHighlight(target, { animation = 0.5, scale = 2, onExpanded = null } = {}) { static openHighlight(target, { animation = 0.5, scale = SCALE, onExpanded = null } = {}) {
if (Highlight._isExpanded(target)) { if (this._isExpanded(target)) {
console.log('Target is already expanded!'); console.log('Target is already expanded!');
return return
} else { } else {
@@ -8161,11 +8173,11 @@
let image = svgRoot.querySelector('image'); let image = svgRoot.querySelector('image');
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target, { let [mask, maskImage] = this._getSVGMask(target, {
svgRoot, svgRoot,
image image
}); });
let center = Highlight._calculateCenterRelativeTo(target, image); let center = this._calculateCenterRelativeTo(target, image);
console.log('_calculateCenterRelativeTo', center); console.log('_calculateCenterRelativeTo', center);
TweenLite.set(maskImage, { TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}` transformOrigin: `${center.x} ${center.y}`
@@ -8177,16 +8189,16 @@
onComplete: onExpanded onComplete: onExpanded
}); });
target.classList.add(Highlight.expandedClass); target.classList.add(this.expandedClass);
} }
} }
static toggleHighlight(node) { static toggleHighlight(node, options = {}) {
console.log('toggleHighlight', Highlight._isExpanded(node)); console.log('toggleHighlight', this._isExpanded(node));
if (Highlight._isExpanded(node)) { if (this._isExpanded(node)) {
Highlight.closeHighlight(node); this.closeHighlight(node, options);
} else { } else {
Highlight.openHighlight(node); this.openHighlight(node, options);
} }
} }
@@ -8209,7 +8221,7 @@
let maskImage = svgRoot.getElementById(maskImageId); let maskImage = svgRoot.getElementById(maskImageId);
if (!mask || !maskImage) if (!mask || !maskImage)
[mask, maskImage] = Highlight._createSVGMask(circle, { [mask, maskImage] = this._createSVGMask(circle, {
svgRoot, svgRoot,
image, image,
id id
@@ -8240,7 +8252,7 @@
let svg = 'http://www.w3.org/2000/svg'; let svg = 'http://www.w3.org/2000/svg';
let xlink = 'http://www.w3.org/1999/xlink'; let xlink = 'http://www.w3.org/1999/xlink';
let svgGroup = element.parentNode; element.parentNode;
let src = image.getAttributeNS(xlink, 'href'); let src = image.getAttributeNS(xlink, 'href');
@@ -8275,12 +8287,11 @@
maskImage.setAttribute('width', width); maskImage.setAttribute('width', width);
maskImage.setAttribute('height', height); maskImage.setAttribute('height', height);
maskImage.setAttribute('class', 'addedImage'); maskImage.setAttribute('class', 'addedImage');
svgGroup.insertBefore(maskImage, element); // image.nextSibling)
TweenLite.set(maskImage, { scale: 1 }); TweenLite.set(maskImage, { scale: 1 });
maskImage.style.mask = 'url(#' + maskId + ')'; maskImage.style.mask = 'url(#' + maskId + ')';
} }
svgGroup.appendChild(maskImage); element.insertAdjacentElement('beforebegin', maskImage); // image.nextSibling)
// svgGroup.appendChild(element) // svgGroup.appendChild(element)
return [mask, maskImage] return [mask, maskImage]
@@ -8298,14 +8309,14 @@
} }
static _isExpanded(target) { static _isExpanded(target) {
return target.classList.contains(Highlight.expandedClass) return target.classList.contains(this.expandedClass)
} }
static closeHighlight(target, { animation = 0.5 } = {}) { static closeHighlight(target, { animation = 0.5 } = {}) {
target.classList.remove(Highlight.expandedClass); target.classList.remove(this.expandedClass);
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target); let [mask, maskImage] = this._getSVGMask(target);
console.log('Close Highlight', maskImage); // console.log('Close Highlight', maskImage)
TweenLite.to([target, maskImage], animation, { TweenLite.to([target, maskImage], animation, {
scale: 1 scale: 1
}); });
@@ -8315,7 +8326,7 @@
if (!_HighlightEnabled) return if (!_HighlightEnabled) return
event.stopPropagation(); event.stopPropagation();
Highlight.animateCircle(event.target); this.animateCircle(event.target);
return false return false
} }
@@ -8481,7 +8492,7 @@
*/ */
static _replaceCallback(context, element, attributeVal) { static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') { if (element.tagName == 'A') {
element.addEventListener('click', event => { element.addEventListener('pointerdown', event => {
event.preventDefault(); event.preventDefault();
}); });
} }
@@ -9068,7 +9079,7 @@
let cx = target.getAttribute('cx'); let cx = target.getAttribute('cx');
let cy = target.getAttribute('cy'); let cy = target.getAttribute('cy');
let r = target.getAttribute('r'); let r = target.getAttribute('r');
let radius = r.endsWith('%') ? (parseFloat(r) / 100) * width : parseFloat(r); r.endsWith('%') ? (parseFloat(r) / 100) * width : parseFloat(r);
let x = cx.endsWith('%') ? (parseFloat(cx) / 100) * width : cx; let x = cx.endsWith('%') ? (parseFloat(cx) / 100) * width : cx;
let y = cy.endsWith('%') ? (parseFloat(cy) / 100) * height : cx; let y = cy.endsWith('%') ? (parseFloat(cy) / 100) * height : cx;
@@ -11291,4 +11302,4 @@
window.Highlight = Highlight; window.Highlight = Highlight;
window.Theme = Theme; window.Theme = Theme;
}()); })();
+1421 -739
View File
File diff suppressed because it is too large Load Diff
+20 -3
View File
@@ -36,7 +36,21 @@
"./lib/pixi/switch.js", "./lib/pixi/switch.js",
"./lib/pixi/theme.js", "./lib/pixi/theme.js",
"./lib/pixi/tooltip.js", "./lib/pixi/tooltip.js",
"./lib/pixi/volatile.js" "./lib/pixi/volatile.js",
"./lib/pixi/maps/mapapp.js",
"./lib/pixi/maps/mapviewport.js",
"./lib/pixi/maps/geographics.js",
"./lib/pixi/maps/geojson.js",
"./lib/pixi/maps/geolayer.js",
"./lib/pixi/maps/mapprojection.js",
"./lib/pixi/maps/maplist.js",
"./lib/pixi/maps/overlay.js",
"./lib/pixi/maps/scatter.js",
"./lib/pixi/maps/utils.js",
"./lib/pixi/maps/map.js",
"./lib/pixi/maps/projections/projection.js",
"./lib/pixi/maps/projections/mercator.js",
"./lib/pixi/maps/projections/robinson.js"
], ],
"exclude": [], "exclude": [],
"includePattern": ".+\\.js(doc|x)?$", "includePattern": ".+\\.js(doc|x)?$",
@@ -45,7 +59,10 @@
"sourceType": "module", "sourceType": "module",
"tags": { "tags": {
"allowUnknownTags": true, "allowUnknownTags": true,
"dictionaries": ["jsdoc", "closure"] "dictionaries": [
"jsdoc",
"closure"
]
}, },
"templates": { "templates": {
"applicationName": "iwmlib", "applicationName": "iwmlib",
@@ -70,4 +87,4 @@
"useLongnameInNav": false "useLongnameInNav": false
} }
} }
} }
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1830 -239
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1825 -244
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1825 -244
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1828 -237
View File
File diff suppressed because it is too large Load Diff
+1830 -246
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1826 -251
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1829 -236
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1829 -238
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+2583 -236
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1829 -240
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1841 -233
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
(function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return'<span id="line'+e+'"></span>'+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('.item[data-name*="'+n+'"]:eq(0)');if(s.length){s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}}); (function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return'<span id="line'+e+'"></span>'+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('a[href*="'+n+'"]:eq(0)').closest(".item");if(s.length){if(s.parents(".children").length){s.addClass("current");s.find("li.item").addClass("notCurrent");s=s.parents("ul.list>li.item")}s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}});
File diff suppressed because one or more lines are too long
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
+1 -1
View File
@@ -176,7 +176,7 @@ export default class Card {
*/ */
static _replaceCallback(context, element, attributeVal) { static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') { if (element.tagName == 'A') {
element.addEventListener('click', event => { element.addEventListener('pointerdown', event => {
event.preventDefault() event.preventDefault()
}) })
} }
+32 -30
View File
@@ -27,10 +27,13 @@ function round(value) {
* @class Highlight * @class Highlight
* @extends {Object} * @extends {Object}
*/ */
const SCALE = 1.5
export default class Highlight extends Object { export default class Highlight extends Object {
static disableAnimations() { static disableAnimations() {
_HighlightEnabled = false _HighlightEnabled = false
let expanded = document.querySelectorAll('.' + Highlight.expandedClass) let expanded = document.querySelectorAll('.' + this.expandedClass)
for (let obj of expanded) { for (let obj of expanded) {
this.shrink(obj) this.shrink(obj)
} }
@@ -41,11 +44,11 @@ export default class Highlight extends Object {
} }
static removeAnimations(svgRoot) { static removeAnimations(svgRoot) {
let expanded = svgRoot.querySelectorAll('.' + Highlight.expandedClass) let expanded = svgRoot.querySelectorAll('.' + this.expandedClass)
for (let obj of expanded) { for (let obj of expanded) {
TweenLite.set(obj, { scale: 1 }) TweenLite.set(obj, { scale: 1 })
obj.classList.remove('zooming') obj.classList.remove('zooming')
obj.classList.remove(Highlight.expandedClass) obj.classList.remove(this.expandedClass)
} }
let defs = svgRoot.querySelector('defs') let defs = svgRoot.querySelector('defs')
while (defs.firstChild) { while (defs.firstChild) {
@@ -75,12 +78,12 @@ export default class Highlight extends Object {
} }
} }
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) { static expand(obj, { scale = SCALE, duration = 3, stroke = 2, onComplete = null } = {}) {
if (obj == null) return if (obj == null) return
//console.log("expand") //console.log("expand")
obj.classList.add('zooming') obj.classList.add('zooming')
TweenLite.to(obj, duration, { TweenLite.to(obj, duration, {
scale: scale, scale,
onUpdate: () => { onUpdate: () => {
let scale = obj._gsTransform.scaleX let scale = obj._gsTransform.scaleX
obj.setAttribute('stroke-width', stroke / scale) obj.setAttribute('stroke-width', stroke / scale)
@@ -88,7 +91,7 @@ export default class Highlight extends Object {
onComplete: () => { onComplete: () => {
console.log('expand complete') console.log('expand complete')
obj.classList.remove('zooming') obj.classList.remove('zooming')
obj.classList.add(Highlight.expandedClass) obj.classList.add(this.expandedClass)
obj.setAttribute('stroke-width', stroke / scale) obj.setAttribute('stroke-width', stroke / scale)
if (onComplete) onComplete() if (onComplete) onComplete()
} }
@@ -108,7 +111,7 @@ export default class Highlight extends Object {
onComplete: () => { onComplete: () => {
//console.log("shrink complete") //console.log("shrink complete")
obj.classList.remove('zooming') obj.classList.remove('zooming')
obj.classList.remove(Highlight.expandedClass) obj.classList.remove(this.expandedClass)
obj.setAttribute('stroke-width', stroke) obj.setAttribute('stroke-width', stroke)
} }
}) })
@@ -127,9 +130,9 @@ export default class Highlight extends Object {
let id = circle.getAttribute('id') let id = circle.getAttribute('id')
TweenLite.set(circle, { transformOrigin: '50% 50%' }) TweenLite.set(circle, { transformOrigin: '50% 50%' })
/*if (circle.classList.contains('zooming')) { /*if (circle.classList.contains('zooming')) {
console.log("already zooming") console.log("already zooming")
return return
}*/ }*/
let svgRoot = circle.closest('svg') let svgRoot = circle.closest('svg')
let circleGroup = circle.parentNode let circleGroup = circle.parentNode
@@ -147,7 +150,7 @@ export default class Highlight extends Object {
let maskImageId = 'maskImage' + id let maskImageId = 'maskImage' + id
let maskImage = svgRoot.getElementById(maskImageId) let maskImage = svgRoot.getElementById(maskImageId)
if (circle.classList.contains(Highlight.expandedClass)) { if (circle.classList.contains(this.expandedClass)) {
if (!circle.classList.contains('zooming')) { if (!circle.classList.contains('zooming')) {
this.shrink(circle, { stroke }) this.shrink(circle, { stroke })
this.shrink(maskImage, { stroke }) this.shrink(maskImage, { stroke })
@@ -170,7 +173,7 @@ export default class Highlight extends Object {
this.shrink(m, { stroke }) this.shrink(m, { stroke })
} }
Highlight._createSVGMask(svgRoot, image, id) this._createSVGMask(svgRoot, image, id)
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` }) // TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
@@ -180,8 +183,8 @@ export default class Highlight extends Object {
return false return false
} }
static openHighlight(target, { animation = 0.5, scale = 2, onExpanded = null } = {}) { static openHighlight(target, { animation = 0.5, scale = SCALE, onExpanded = null } = {}) {
if (Highlight._isExpanded(target)) { if (this._isExpanded(target)) {
console.log('Target is already expanded!') console.log('Target is already expanded!')
return return
} else { } else {
@@ -205,11 +208,11 @@ export default class Highlight extends Object {
let image = svgRoot.querySelector('image') let image = svgRoot.querySelector('image')
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target, { let [mask, maskImage] = this._getSVGMask(target, {
svgRoot, svgRoot,
image image
}) })
let center = Highlight._calculateCenterRelativeTo(target, image) let center = this._calculateCenterRelativeTo(target, image)
console.log('_calculateCenterRelativeTo', center) console.log('_calculateCenterRelativeTo', center)
TweenLite.set(maskImage, { TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}` transformOrigin: `${center.x} ${center.y}`
@@ -221,16 +224,16 @@ export default class Highlight extends Object {
onComplete: onExpanded onComplete: onExpanded
}) })
target.classList.add(Highlight.expandedClass) target.classList.add(this.expandedClass)
} }
} }
static toggleHighlight(node) { static toggleHighlight(node, options = {}) {
console.log('toggleHighlight', Highlight._isExpanded(node)) console.log('toggleHighlight', this._isExpanded(node))
if (Highlight._isExpanded(node)) { if (this._isExpanded(node)) {
Highlight.closeHighlight(node) this.closeHighlight(node, options)
} else { } else {
Highlight.openHighlight(node) this.openHighlight(node, options)
} }
} }
@@ -253,7 +256,7 @@ export default class Highlight extends Object {
let maskImage = svgRoot.getElementById(maskImageId) let maskImage = svgRoot.getElementById(maskImageId)
if (!mask || !maskImage) if (!mask || !maskImage)
[mask, maskImage] = Highlight._createSVGMask(circle, { [mask, maskImage] = this._createSVGMask(circle, {
svgRoot, svgRoot,
image, image,
id id
@@ -319,12 +322,11 @@ export default class Highlight extends Object {
maskImage.setAttribute('width', width) maskImage.setAttribute('width', width)
maskImage.setAttribute('height', height) maskImage.setAttribute('height', height)
maskImage.setAttribute('class', 'addedImage') maskImage.setAttribute('class', 'addedImage')
svgGroup.insertBefore(maskImage, element) // image.nextSibling)
TweenLite.set(maskImage, { scale: 1 }) TweenLite.set(maskImage, { scale: 1 })
maskImage.style.mask = 'url(#' + maskId + ')' maskImage.style.mask = 'url(#' + maskId + ')'
} }
svgGroup.appendChild(maskImage) element.insertAdjacentElement('beforebegin', maskImage) // image.nextSibling)
// svgGroup.appendChild(element) // svgGroup.appendChild(element)
return [mask, maskImage] return [mask, maskImage]
@@ -342,14 +344,14 @@ export default class Highlight extends Object {
} }
static _isExpanded(target) { static _isExpanded(target) {
return target.classList.contains(Highlight.expandedClass) return target.classList.contains(this.expandedClass)
} }
static closeHighlight(target, { animation = 0.5 } = {}) { static closeHighlight(target, { animation = 0.5 } = {}) {
target.classList.remove(Highlight.expandedClass) target.classList.remove(this.expandedClass)
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target) let [mask, maskImage] = this._getSVGMask(target)
console.log('Close Highlight', maskImage) // console.log('Close Highlight', maskImage)
TweenLite.to([target, maskImage], animation, { TweenLite.to([target, maskImage], animation, {
scale: 1 scale: 1
}) })
@@ -359,7 +361,7 @@ export default class Highlight extends Object {
if (!_HighlightEnabled) return if (!_HighlightEnabled) return
event.stopPropagation() event.stopPropagation()
Highlight.animateCircle(event.target) this.animateCircle(event.target)
return false return false
} }
+1 -1
View File
@@ -9,8 +9,8 @@
<link rel="stylesheet" href="../../css/highlight.css"> <link rel="stylesheet" href="../../css/highlight.css">
<link rel="stylesheet" href="../../css/popup.css"> <link rel="stylesheet" href="../../css/popup.css">
<script src="../3rdparty/highlight/highlight.pack.js"></script> <script src="../3rdparty/highlight/highlight.pack.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.3rdparty.js"></script> <script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
</head> </head>
<body onload="Doctest.run();"> <body onload="Doctest.run();">
+25 -12
View File
@@ -101,16 +101,24 @@ export default class CardWrapper extends Object {
let clickRects = activeNodes.map(link => { let clickRects = activeNodes.map(link => {
let rect = link.getBoundingClientRect() let rect = link.getBoundingClientRect()
// Since the getBoundingClientRect is untransformed we cannot rely on it's size
// We need a transformed bottom right to calculate local width and height
let bottomRight = Points.fromPageToNode(element, {
x: rect.x + rect.width,
y: rect.y + rect.height
})
let topLeft = Points.fromPageToNode(element, rect) let topLeft = Points.fromPageToNode(element, rect)
let width = Math.abs(bottomRight.x - topLeft.x)
let height = Math.abs(bottomRight.y - topLeft.y)
let center = Points.fromPageToNode(element, { let center = Points.fromPageToNode(element, {
x: rect.x + rect.width / 2, x: rect.x + width / 2,
y: rect.y + rect.height / 2 y: rect.y + height / 2
}) })
return { return {
x: topLeft.x, x: topLeft.x,
y: topLeft.y, y: topLeft.y,
width: rect.width, width,
height: rect.height, height,
center, center,
link link
} }
@@ -138,14 +146,19 @@ export default class CardWrapper extends Object {
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a /* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page. proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
Therefore we use the original click event (see constructor). */ Therefore we use the original click event (see constructor). */
if (this.isSVGNode(node)) { // if (this.isSVGNode(node)) {
if (this.triggerSVGClicks) { // if (this.triggerSVGClicks) {
let click = new Event('click') // let click = new Event('click')
node.dispatchEvent(click) // node.dispatchEvent(click)
} // }
return // return
} // }
node.click() // node.click()
let click = new Event('click')
click.clientX = event.clientX
click.clientY = event.clientY
node.dispatchEvent(click)
} }
nodeTapped(node, event) { nodeTapped(node, event) {
+36 -30
View File
@@ -145,43 +145,49 @@ export default class Doctest {
let container = document.createElement('div') let container = document.createElement('div')
container.className = 'doctest-wrapper' container.className = 'doctest-wrapper'
let titleParent = container if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
if (doctest.hasAttribute('data-collapsible')) { let titlebar = document.createElement('div')
let collapsibleToggle = document.createElement('div') titlebar.className = 'doctest-titlebar'
titlebar.style = 'min-height: 10px;'
container.appendChild(titlebar)
let icon = document.createElement('i') if (doctest.hasAttribute('data-title')) {
icon.className = 'material-icons' let title = document.createElement('h6')
collapsibleToggle.appendChild(icon) title.innerText = doctest.getAttribute('data-title')
title.className = 'doctest-section-title'
titlebar.appendChild(title)
}
collapsibleToggle.className = 'doctest-collapsible-toggle' if (doctest.hasAttribute('data-collapsible')) {
collapsibleToggle.style = 'min-height: 10px;' let icon = document.createElement('i')
titleParent = collapsibleToggle icon.className = 'material-icons'
container.appendChild(collapsibleToggle) titlebar.classList.add('doctest-collapsible-toggle')
const collapsedClass = 'collapsed' if (titlebar.childNodes.length > 0) {
titlebar.insertBefore(icon, titlebar.childNodes[0])
function setToggleMode(collapse) {
if (collapse) {
container.classList.add(collapsedClass)
icon.innerText = 'arrow_drop_down'
} else { } else {
container.classList.remove(collapsedClass) titlebar.appendChild(icon)
icon.innerText = 'arrow_drop_up'
} }
}
function determineToggleMode() { const collapsedClass = 'collapsed'
setToggleMode(!container.classList.contains(collapsedClass))
}
setToggleMode(doctest.hasAttribute('data-collapsed')) function setToggleMode(collapse) {
collapsibleToggle.addEventListener('click', determineToggleMode) if (collapse) {
} container.classList.add(collapsedClass)
if (doctest.hasAttribute('data-title')) { icon.innerText = 'arrow_drop_down'
let title = document.createElement('h6') } else {
title.innerText = doctest.getAttribute('data-title') container.classList.remove(collapsedClass)
title.className = 'doctest-section-title' icon.innerText = 'arrow_drop_up'
titleParent.appendChild(title) }
}
function determineToggleMode() {
setToggleMode(!container.classList.contains(collapsedClass))
}
setToggleMode(doctest.hasAttribute('data-collapsed'))
titlebar.addEventListener('click', determineToggleMode)
}
} }
let pre = document.createElement('pre') let pre = document.createElement('pre')
+6 -3
View File
@@ -117,9 +117,12 @@ export default class Errors {
window.addEventListener( window.addEventListener(
'error', 'error',
event => { event => {
// if (typeof(event.error) == 'undefined') { if (typeof event.error == 'undefined') {
// console.info("Catched undefined error", event) // This sometimes happens in Edge. Since we have no error
// } // position, we cannot do much beside an info log.
console.info('Catched undefined error', event)
return
}
this.appendError(event.error, event.filename) this.appendError(event.error, event.filename)
}, },
true true
+4 -1
View File
@@ -1,7 +1,10 @@
export default class Events { export default class Events {
static stop(event) { static stop(event) {
event.preventDefault() event.preventDefault()
event.stopPropagation() // I removed this, as it destroys all the Hammer.js events.
// And also the click event.
// It seems to have no (apparent) negative impact. -SO
// event.stopPropagation()
} }
static extractPoint(event) { static extractPoint(event) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 755 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.
+9 -7
View File
@@ -141,13 +141,15 @@ export class HTMLLoader extends CardLoader {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest() let xhr = new XMLHttpRequest()
xhr.open('GET', this.src, false) xhr.open('GET', this.src, false)
xhr.onload = e => { xhr.onreadystatechange = e => {
domNode.innerHTML = this.prepare(xhr.response) if (xhr.readyState == 4) {
this.addedNode = domNode.firstElementChild domNode.innerHTML = this.prepare(xhr.response)
let { width, height } = this.size(this.addedNode) this.addedNode = domNode.firstElementChild
if (width) this.wantedWidth = width || this.wantedWidth let { width, height } = this.size(this.addedNode)
if (height) this.wantedHeight = height || this.wantedHeight if (width) this.wantedWidth = width || this.wantedWidth
resolve(this) if (height) this.wantedHeight = height || this.wantedHeight
resolve(this)
}
} }
xhr.onerror = e => { xhr.onerror = e => {
reject(this) reject(this)
+2 -2
View File
@@ -849,8 +849,8 @@ export class InteractionDelegate {
if (Events.isCaptured(event)) { if (Events.isCaptured(event)) {
return false return false
} }
let captured = this.target.capture(event)
return captured return this.target.capture ? this.target.capture(event) : false
} }
getPosition(event) { getPosition(event) {
+1 -1
View File
@@ -10,7 +10,7 @@ let logHandlers = {
} }
try { try {
ipc = require('electron').ipcRenderer ipc = window.ipcRenderer || require('electron').ipcRenderer
logHandlers.log = message => ipc.send('log', message) logHandlers.log = message => ipc.send('log', message)
logHandlers.warn = message => ipc.send('warn', message) logHandlers.warn = message => ipc.send('warn', message)
logHandlers.error = message => ipc.send('error', message) logHandlers.error = message => ipc.send('error', message)
+36
View File
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Fullscreen Application Doctest</title>
<link rel="stylesheet" href=".././3rdparty/highlight/styles/default.css" />
<link rel="stylesheet" href="../../css/doctest.css" />
<script src=".././3rdparty/highlight/highlight.pack.js"></script>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
</head>
<body style="margin: 0; padding: 0;">
<canvas id="canvas" style="width: 100%; height: 100%;"></canvas>
<script class="doctest">
const app = new PIXIApp({
view: canvas,
fpsLogging: true,
transparent: false
})
app.setup()
app.run()
let highlightBtn = new PIXI.Graphics()
highlightBtn.lineStyle(4, 0xff6900)
highlightBtn.drawRoundedRect(150, 40, 30, 30, 4)
highlightBtn.endFill()
app.stage.addChild(highlightBtn)
</script>
</body>
</html>
+14 -1
View File
@@ -137,7 +137,7 @@ export default class PIXIApp extends PIXI.Application {
this.graphql = graphql this.graphql = graphql
if (fullScreen || autoResize) { if (fullScreen || autoResize) {
console.log('App is in fullScreen mode or autoResize mode') console.log('App is in fullScreen mode or autoResize mode')
const resizeDebounced = debounce(event => this.resize(event), 50) const resizeDebounced = debounce(event => this.resizeApp(event), 50)
window.addEventListener('resize', resizeDebounced) window.addEventListener('resize', resizeDebounced)
document.body.addEventListener('orientationchange', this.checkOrientation.bind(this)) document.body.addEventListener('orientationchange', this.checkOrientation.bind(this))
} }
@@ -312,6 +312,19 @@ export default class PIXIApp extends PIXI.Application {
* @return {PIXIApp} - Returns the PIXIApp for chaining. * @return {PIXIApp} - Returns the PIXIApp for chaining.
*/ */
resize(event, { width = window.innerWidth, height = window.innerHeight } = {}) { resize(event, { width = window.innerWidth, height = window.innerHeight } = {}) {
return this.resizeApp(event, { width, height })
}
/**
* Resizes the renderer to fit into the window or given width and height.
*
* @param {object} [event] - The event.
* @param {object=} [opts={}] - The event.
* @param {number} [opts.width=window.innerWidth] - The width of the app to resize to.
* @param {number} [opts.height=window.innerHeight] - The height of the app to resize to.
* @return {PIXIApp} - Returns the PIXIApp for chaining.
*/
resizeApp(event, { width = window.innerWidth, height = window.innerHeight } = {}) {
this.width = width this.width = width
this.height = height this.height = height
this.expandRenderer() this.expandRenderer()
+8 -6
View File
@@ -62,12 +62,11 @@ window.FontInfo = FontInfo
window.Text = Text window.Text = Text
//Maps //Maps
import MapView from './maps/mapview.js'
import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js' import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js'
import { MapData, DeepZoomMapData } from './maps/mapdata.js' import { MapProjection, DeepZoomMapProjection } from './maps/mapprojection'
window.MapData = MapData window.MapProjection = MapProjection
window.DeepZoomMapData = DeepZoomMapData window.DeepZoomMapProjection = DeepZoomMapProjection
window.GeoMap = GeoMap window.GeoMap = GeoMap
window.ImageMap = ImageMap window.ImageMap = ImageMap
@@ -80,7 +79,8 @@ window.Projection = {
Robinson Robinson
} }
window.MapView = MapView import MapViewport from './maps/mapviewport.js'
window.MapViewport = MapViewport
import MapApp from './maps/mapapp.js' import MapApp from './maps/mapapp.js'
@@ -100,7 +100,6 @@ window.CompactScatter = CompactScatter
window.CoverScatter = CoverScatter window.CoverScatter = CoverScatter
window.MapObjectScatter = MapObjectScatter window.MapObjectScatter = MapObjectScatter
import { GeoLayer, MapLayer } from './maps/geolayer.js' import { GeoLayer, MapLayer } from './maps/geolayer.js'
window.GeoLayer = GeoLayer window.GeoLayer = GeoLayer
window.MapLayer = MapLayer window.MapLayer = MapLayer
@@ -118,5 +117,8 @@ import Overlay from './maps/overlay.js'
window.Overlay = Overlay window.Overlay = Overlay
import { MapList } from './maps/maplist.js' import { MapList } from './maps/maplist.js'
import GeoJson, { GeoUtils } from './maps/geojson.js'
window.MapList = MapList window.MapList = MapList
window.GeoJson = GeoJson
window.GeoUtils = GeoUtils
+4 -1
View File
@@ -248,7 +248,10 @@ const button20 = new Button({
y: 310, y: 310,
label: 'Link Button', label: 'Link Button',
type: 'checkbox', type: 'checkbox',
style: 'link' style: 'link',
action: event => {
console.log('Link button clicked')
}
}) })
const button21 = new Button({ const button21 = new Button({
+12 -10
View File
@@ -113,6 +113,8 @@ export default class Button extends PIXI.Container {
const theme = Theme.fromString(opts.theme) const theme = Theme.fromString(opts.theme)
this.theme = theme this.theme = theme
this.HIDDEN_ALPHA = 0.000000001 // WORKAROUND: See https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide#graphics-interaction
this.opts = Object.assign( this.opts = Object.assign(
{}, {},
{ {
@@ -172,10 +174,10 @@ export default class Button extends PIXI.Container {
if (this.opts.style === 'link') { if (this.opts.style === 'link') {
Object.assign(this.opts, { Object.assign(this.opts, {
strokeAlpha: 0, strokeAlpha: this.HIDDEN_ALPHA,
strokeActiveAlpha: 0, strokeActiveAlpha: this.HIDDEN_ALPHA,
fillAlpha: 0, fillAlpha: this.HIDDEN_ALPHA,
fillActiveAlpha: 0 fillActiveAlpha: this.HIDDEN_ALPHA
}) })
} }
@@ -286,10 +288,6 @@ export default class Button extends PIXI.Container {
this.opts.beforeAction.call(this, e, this) this.opts.beforeAction.call(this, e, this)
} }
if (this.opts.action) {
this.opts.action.call(this, e, this)
}
TweenLite.to([this.button, this.content], this.theme.fast, { TweenLite.to([this.button, this.content], this.theme.fast, {
alpha: 0.83, alpha: 0.83,
overwrite: 'none' overwrite: 'none'
@@ -299,6 +297,10 @@ export default class Button extends PIXI.Container {
this.active = !this.active this.active = !this.active
} }
if (this.opts.action) {
this.opts.action.call(this, e, this)
}
if (this.opts.afterAction) { if (this.opts.afterAction) {
this.opts.afterAction.call(this, e, this) this.opts.afterAction.call(this, e, this)
} }
@@ -640,8 +642,8 @@ export default class Button extends PIXI.Container {
hide() { hide() {
this.opts.strokeAlpha = 0.0 this.opts.strokeAlpha = 0.0
this.opts.strokeActiveAlpha = 0.0 this.opts.strokeActiveAlpha = 0.0
this.opts.fillAlpha = 0.0000000001 // WORKAROUND: See https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide#graphics-interaction this.opts.fillAlpha = this.HIDDEN_ALPHA
this.opts.fillActiveAlpha = 0.0000000001 this.opts.fillActiveAlpha = this.HIDDEN_ALPHA
this.layout() this.layout()
+2 -2
View File
@@ -85,8 +85,8 @@ export class PIXITileLoader extends TileLoader {
super(tiles) super(tiles)
this.destroyed = false this.destroyed = false
this.loader = new PIXI.Loader() this.loader = new PIXI.Loader()
this.loader.on('load', this._onLoaded.bind(this)) this.loader.onLoad.add(this._onLoaded.bind(this))
this.loader.on('error', this._onError.bind(this)) this.loader.onError.add(this._onError.bind(this))
if (compression) { if (compression) {
this.loader.use(PIXI.compressedTextures.ImageParser.use) this.loader.use(PIXI.compressedTextures.ImageParser.use)
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 44 KiB

+1
View File
@@ -37,6 +37,7 @@ const index = new Index(itemTemplate, [
['Flippable', 'flippable.html'], ['Flippable', 'flippable.html'],
['LabeledGraphics', 'labeledgraphics.html'], ['LabeledGraphics', 'labeledgraphics.html'],
['List', 'list.html'], ['List', 'list.html'],
['Maps', 'maps/index.html'],
['Message', 'message.html'], ['Message', 'message.html'],
['Modal', 'modal.html'], ['Modal', 'modal.html'],
['Tooltip', 'tooltip.html'], ['Tooltip', 'tooltip.html'],
File diff suppressed because one or more lines are too long
+75 -42
View File
@@ -2,9 +2,10 @@ import { Points } from '../../utils.js'
import { EventHandler } from './utils.js' import { EventHandler } from './utils.js'
import { FlagPolygon } from '../graphics/label.js' import { FlagPolygon } from '../graphics/label.js'
import { DeepZoomMap } from './map.js' import { DeepZoomMap } from './map.js'
import { PIXIUtils } from './utils.js'
/** /**
* GeoGraphics are graphical objects, that does not store the graphics information * * GeoGraphics are graphical objects, that does not store the graphics information
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be * in screen space, but in geographical coordinates. Therefore GeoGraphics must be
* placed on GeoLayers to work properly. * placed on GeoLayers to work properly.
* *
@@ -14,11 +15,12 @@ import { DeepZoomMap } from './map.js'
* The geolayers forward this 'adaptTo' to all children that are GeoGraphics. * The geolayers forward this 'adaptTo' to all children that are GeoGraphics.
* Which adjust their so called 'point' data to the new map. * Which adjust their so called 'point' data to the new map.
* *
* @abstract * @export
* @class GeoGraphics
*/ */
export class GeoGraphics { export class GeoGraphics {
constructor(coordinates, { scale = 1, onDraw = null, onDrawEnd = null, debug = false } = {}) { constructor(coordinates, { scale = 1, onDraw = null, onDrawEnd = null, debug = false } = {}) {
this.coordinates = coordinates this._coordinates = coordinates
this.debug = debug this.debug = debug
this.graphics = new PIXI.Graphics() this.graphics = new PIXI.Graphics()
this.scale = scale this.scale = scale
@@ -28,6 +30,17 @@ export class GeoGraphics {
this._position = null this._position = null
} }
/**
* The coordinates of the geographics.
*
* @member {array}
* @readonly
* @memberof GeoGraphics
*/
get coordinates() {
return this._coordinates
}
clone() { clone() {
console.error(`Call of abstract method clone(). Overwrite in subclass.`, this) console.error(`Call of abstract method clone(). Overwrite in subclass.`, this)
} }
@@ -133,6 +146,12 @@ export class GeoGraphics {
this._layer = layer this._layer = layer
} }
/**
* Map of the containing layer. Null if on no layer.
*
* @readonly
* @memberof GeoGraphics
*/
get map() { get map() {
let map = null let map = null
if (this.mapLayer) { if (this.mapLayer) {
@@ -141,6 +160,13 @@ export class GeoGraphics {
return map return map
} }
/**
* MapLayer of the containing layer. Null if on no layer.
*
* @member {MapLayer}
* @readonly
* @memberof GeoGraphics
*/
get mapLayer() { get mapLayer() {
let mapLayer = null let mapLayer = null
if (this.layer) { if (this.layer) {
@@ -199,6 +225,10 @@ export class GeoGraphics {
* *
* This GeoGraphics does not provide any visual representation. * This GeoGraphics does not provide any visual representation.
* Draw the desired shape in the onDraw callback. * Draw the desired shape in the onDraw callback.
*
* @export
* @class GeoPoint
* @extends {GeoGraphics}
*/ */
export class GeoPoint extends GeoGraphics { export class GeoPoint extends GeoGraphics {
clone() { clone() {
@@ -228,6 +258,13 @@ export class GeoPoint extends GeoGraphics {
_draw() {} _draw() {}
} }
/**
* Represensts a line between two locations.
*
* @export
* @class GeoLine
* @extends {GeoGraphics}
*/
export class GeoLine extends GeoGraphics { export class GeoLine extends GeoGraphics {
/** /**
* @param {object} opts - Optional values * @param {object} opts - Optional values
@@ -330,6 +367,13 @@ export class GeoLine extends GeoGraphics {
} }
} }
/**
* Represents a shape on a map.
*
* @export
* @class GeoShape
* @extends {GeoGraphics}
*/
export class GeoShape extends GeoGraphics { export class GeoShape extends GeoGraphics {
clone() { clone() {
return new GeoShape(this.coordinates, this._cloneOptions) return new GeoShape(this.coordinates, this._cloneOptions)
@@ -441,15 +485,12 @@ export class GeoShape extends GeoGraphics {
*/ */
_drawShape(polygon, hole = []) { _drawShape(polygon, hole = []) {
// We save the fill specified in the onDraw event handler. // We save the fill specified in the onDraw event handler.
//
// Consider: Maybe it would be a good idea to add a 'onHoleDraw'
// callback to make the hole customizable. Maybe you want
// to fill it with a different color or an mediocre alpha value.
// then feel free to implement it.
let { fill, alpha } = PIXIUtils.saveFill(this.graphics) let { fill, alpha } = PIXIUtils.saveFill(this.graphics)
/** /**
* This may seem redundant, but it's required * a) Draw the hole with a polygon.
*
* This may seem redundant to (c), but it's required (in this order(!))
* to make the hole clickable. * to make the hole clickable.
* *
* It was a bit confusing, so I made a CodePen * It was a bit confusing, so I made a CodePen
@@ -460,9 +501,15 @@ export class GeoShape extends GeoGraphics {
this.graphics.drawPolygon(hole) this.graphics.drawPolygon(hole)
} }
/**
* b) Draw the shape.
*/
this.graphics.beginFill(fill, alpha) this.graphics.beginFill(fill, alpha)
this.graphics.drawPolygon(polygon) this.graphics.drawPolygon(polygon)
/**
* c) Add the hole.
*/
if (hole.length > 0) { if (hole.length > 0) {
this.graphics.beginHole() this.graphics.beginHole()
this.graphics.drawPolygon(hole) this.graphics.drawPolygon(hole)
@@ -475,6 +522,9 @@ export class GeoShape extends GeoGraphics {
* The MultiGraphics makes use of the other GeoGraphics to * The MultiGraphics makes use of the other GeoGraphics to
* create a single graphics element out of multiple different * create a single graphics element out of multiple different
* GeoGraphics. * GeoGraphics.
*
* @class GeoMultiGraphics
* @extends {GeoGraphics}
*/ */
class GeoMultiGraphics extends GeoGraphics { class GeoMultiGraphics extends GeoGraphics {
/** /**
@@ -524,6 +574,13 @@ class GeoMultiGraphics extends GeoGraphics {
} }
} }
/**
* Text that is attatched to a GeoPoint.
*
* @export
* @class GeoText
* @extends {GeoPoint}
*/
export class GeoText extends GeoPoint { export class GeoText extends GeoPoint {
constructor(coordinates, text, opts) { constructor(coordinates, text, opts) {
super(coordinates, opts) super(coordinates, opts)
@@ -531,14 +588,6 @@ export class GeoText extends GeoPoint {
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle) this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
this._text = new PIXI.Text(text, this.textStyle) this._text = new PIXI.Text(text, this.textStyle)
//TODO: Make this more generic:
// We have 8 layers (12-20) for each map so this temporarily works fine.
// Outsource it to the map class.
//let textScale = Math.pow(2, 7)
// let textScale = 5
// this.text.scale.set(1 / textScale, 1 / textScale)
switch (this.align) { switch (this.align) {
case 'left': case 'left':
break break
@@ -570,6 +619,8 @@ export class GeoText extends GeoPoint {
/** /**
* A geotext with a notch at a certain Position. * A geotext with a notch at a certain Position.
*
* TODO: This is a specialization for the Tüsch. This should be inside the Tüsch project.
*/ */
export class GeoFlagLabel extends GeoText { export class GeoFlagLabel extends GeoText {
constructor(coordinates, text, opts) { constructor(coordinates, text, opts) {
@@ -675,6 +726,13 @@ export class GeoFlagLabel extends GeoText {
} }
} }
/**
* The GeoMultiShape displays multiple forms.
*
* @export
* @class GeoMultiShape
* @extends {GeoShape}
*/
export class GeoMultiShape extends GeoShape { export class GeoMultiShape extends GeoShape {
static _manipulatePoints(points, func) { static _manipulatePoints(points, func) {
points.forEach(shape => { points.forEach(shape => {
@@ -714,28 +772,3 @@ export class GeoMultiShape extends GeoShape {
}) })
} }
} }
class PIXIUtils {
/*
* Transform a pixi text to it's actual screensize,
* ignoring it's local transforms
*/
static toScreenFontSize(pixiText, fontSize = null) {
pixiText._recursivePostUpdateTransform()
let normalizedScale = {
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
y: pixiText.scale.x / pixiText.transform.worldTransform.d
}
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y }
if (fontSize) pixiText.style.fontSize = fontSize
}
static saveFill(graphics) {
return {
fill: graphics.fill.color,
alpha: graphics.fill.alpha
}
}
}
+2 -2
View File
@@ -156,7 +156,7 @@
// As map an image of europe is used. // As map an image of europe is used.
let europe = "../../../../var/examples/maps/europe/europe.jpg" let europe = "../../../../var/examples/maps/europe/europe.jpg"
let europeData = new MapData(MERCATOR, { let europeMapProjection = new MapProjection(MERCATOR, {
clip: { clip: {
min: { x: 32.863294, y: -18.58 }, min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 } max: { x: 57.467973, y: 44.277158 }
@@ -182,7 +182,7 @@
window.readyHandler = new EventHandler("onReady") window.readyHandler = new EventHandler("onReady")
function ready(textures) { function ready(textures) {
let europeMap = new ImageMap(new PIXI.Sprite(textures.get(europe)), europeData) let europeMap = new ImageMap(new PIXI.Sprite(textures.get(europe)), europeMapProjection)
app.setMap("germany", europeMap) app.setMap("germany", europeMap)
app.setup().run() app.setup().run()
+26 -7
View File
@@ -6,8 +6,7 @@ import { GeoGraphics, GeoShape, GeoMultiShape, GeoLine, GeoPoint } from './geogr
* *
* @static * @static
* @export * @export
* @class GeoJsonGraphics * @class
* @extends {GeoGraphics}
*/ */
export default class GeoJson { export default class GeoJson {
static isLineType(type) { static isLineType(type) {
@@ -40,6 +39,15 @@ export default class GeoJson {
return ['Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon'] return ['Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon']
} }
/**
* Takes a JSON object that contains a FeatureCollection and returns an array
* of GeoJson objects.
*
* @static
* @param {array} featureCollection - Array of GeoJSON objects that were contained in the feature collection.
* @returns {array} Returns an array of geo json objects.
* @memberof GeoJson
*/
static unwrapFeatureCollection(featureCollection) { static unwrapFeatureCollection(featureCollection) {
if (featureCollection.features == null) { if (featureCollection.features == null) {
console.error( console.error(
@@ -59,18 +67,27 @@ export default class GeoJson {
} }
list.push({ type, coordinates }) list.push({ type, coordinates })
// console.log({type, coordinates})
}) })
return list return list
} }
/**
* Validates and converts one set of coordinates of a specific type.
*
* @static
* @param {string} type - Type of the GeoJson.
* @param {array} coordinates - array of points.
* @returns {array}
* @memberof GeoJson
*/
static validateAndConvert(type, coordinates) { static validateAndConvert(type, coordinates) {
let converted = null
if (!GeoJson.validateType(type)) throw new GeoJson.InvalidTypeError(type) if (!GeoJson.validateType(type)) throw new GeoJson.InvalidTypeError(type)
else { else {
if (GeoJson.validateCoordinates(type, coordinates)) { if (GeoJson.validateCoordinates(type, coordinates)) {
let converted = GeoJson.convert(type, coordinates) converted = GeoJson.convert(type, coordinates)
return converted
} else { } else {
console.error( console.error(
`Coordinates are invalid. They must be in format of type '${type} - ${GeoJson._getFormatStringOfType( `Coordinates are invalid. They must be in format of type '${type} - ${GeoJson._getFormatStringOfType(
@@ -79,6 +96,8 @@ export default class GeoJson {
) )
} }
} }
return converted
} }
static validateType(type) { static validateType(type) {
@@ -123,7 +142,7 @@ export default class GeoJson {
* considered valid. A complete list is provided in the GeoUtils. * considered valid. A complete list is provided in the GeoUtils.
* *
* @param {object} point - The point that is tested for validity. * @param {object} point - The point that is tested for validity.
* @returns * @returns {boolean}
* @memberof GeoJson * @memberof GeoJson
*/ */
static validateAndConvertPoint(point) { static validateAndConvertPoint(point) {
@@ -302,7 +321,7 @@ export class GeoUtils {
* {latitude: lat, longitude: lng} * {latitude: lat, longitude: lng}
* *
* @static * @static
* @param {object / array} coordinate - Coordinate to be tested, if it is an valid coordinate. * @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null. * @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
* @memberof GeoGraphics * @memberof GeoGraphics
*/ */
+88 -21
View File
@@ -1,14 +1,17 @@
import { GeoMap } from './map.js' import { GeoMap } from './map.js'
import MapView from './mapview.js'
import { EventHandler } from './utils.js' import { EventHandler } from './utils.js'
import { GeoGraphics } from './geographics.js' import { GeoGraphics } from './geographics.js'
import { MapList } from './maplist.js' import { MapList } from './maplist.js'
//import { GeoGraphics } from "../pixi/geographics.js" import MapViewport from './mapviewport.js'
import { ScatterContainer } from '../scatter.js'
/** /**
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and * The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
* GeoGraphics. The layer can be adapted to a map and notifies all Geo-Children * GeoGraphics. The layer can be adapted to a map and notifies all Geo-Children
* of the Adaption. * of the Adaption.
*
* @export
* @class GeoLayer
*/ */
export class GeoLayer { export class GeoLayer {
constructor(displayObject, opts = {}) { constructor(displayObject, opts = {}) {
@@ -93,6 +96,13 @@ export class GeoLayer {
return this._visibility return this._visibility
} }
/**
* Alias for geoLayer.displayObject.addChild.
*
* @public
* @param {GeoGraphics | PIXI.DisplayObject} element - Element to add to the displayObject.
* @memberof GeoLayer
*/
addChild(element) { addChild(element) {
this.displayObject.addChild(element) this.displayObject.addChild(element)
} }
@@ -117,15 +127,6 @@ export class GeoLayer {
} else console.error('There was no map specified.', this) } else console.error('There was no map specified.', this)
} }
// place(geographic) {
// if (geographic.constructor.name.startsWith('Geo') && geographic.graphics) {
// // Fix to remove the rollupjs circular dependency
// //if (geographic instanceof GeoGraphics) {
// this.geographics.push(geographic)
// super.place(geographic.graphics)
// } else super.place(geographic)
// }
removeFromParent() { removeFromParent() {
if (this.parent) { if (this.parent) {
this.parent.removeLayer(this) this.parent.removeLayer(this)
@@ -165,8 +166,15 @@ export class GeoLayer {
return this._parent return this._parent
} }
/**
* Adds a GeoLayer as child to the GeoLayer.
*
* @public
* @param {GeoLayer} layer - GeoLayer to add.
* @memberof GeoLayer
*/
addLayer(layer) { addLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) { if (layer instanceof GeoLayer) {
layer.removeFromParent() layer.removeFromParent()
this.layers.push(layer) this.layers.push(layer)
@@ -218,6 +226,18 @@ export class GeoLayer {
// } // }
} }
/**
* The map layer is responsible for showing certain maps, at a specific position It contains
* a list of available maps and can switch between them seamlessly. GeoGraphics placed on the MapLayer itself
* or child Geolayers will be adapted to maps and adjusted on map change automatically.
*
* The map layer is the 'king' of the geo layers. Every geolayer
* needs a map layer at it's root. Otherwise they won't work-
*
* @export
* @class MapLayer
* @extends {GeoLayer}
*/
export class MapLayer extends GeoLayer { export class MapLayer extends GeoLayer {
constructor( constructor(
mapList, mapList,
@@ -247,7 +267,7 @@ export class MapLayer extends GeoLayer {
listeners: onChange listeners: onChange
}) })
this.mapview = new MapView({ this._mapview = new MapViewport({
zoom, zoom,
focus, focus,
viewport viewport
@@ -267,6 +287,18 @@ export class MapLayer extends GeoLayer {
this.changeMap(mapList.active) this.changeMap(mapList.active)
} }
//Todo: rename to mapviewport.
/**
* Returns the MapViewport of this map layer.
*
* @readonly
* @member {MapViewport}
* @memberof MapLayer
*/
get mapview() {
return this._mapview
}
get mapChangeLocked() { get mapChangeLocked() {
return this._mapChangeLocked return this._mapChangeLocked
} }
@@ -279,22 +311,34 @@ export class MapLayer extends GeoLayer {
this._mapChangeLocked = false this._mapChangeLocked = false
} }
/**
* Adapts all child layers and their GeoGraphics.
*
* This is called primarily on a map change.
*
* @private
* @memberof MapLayer
*/
adapt() { adapt() {
this.layers.forEach(layer => { this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(this.map) if (layer.adapt) layer.adapt(this.map)
}) })
} }
focus(coordinates, zoom) {
this.mapview.updateFocusPoint(this.map)
}
transformed(e) { transformed(e) {
this.mapview.transformed(this.map) this.mapview.update(this.map)
this.layers.forEach(layer => layer.parentMapLayerTransformed(this)) this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
this.transformHandler.call(this) this.transformHandler.call(this)
} }
/**
* Clones the map layer-
*
* @param {ScatterContainer} scatterContainer - ScatterContainer of the app.
* @param {PIXI.DisplayObject} [container=null] - Container of the newly created MapLayer. If null, an empty PIXI.Container will be created.
* @returns {MapLayer} - Returns the cloned MapLayer.
* @memberof MapLayer
*/
clone(scatterContainer, container = null) { clone(scatterContainer, container = null) {
let mapList = this.mapList.clone() let mapList = this.mapList.clone()
container = container == null ? new PIXI.Container() : container container = container == null ? new PIXI.Container() : container
@@ -369,23 +413,46 @@ export class MapLayer extends GeoLayer {
} }
} }
/**
* Applies the mapviews focus to the map.
* This may be useful, if the container was modified.
*
* @memberof MapLayer
*/
refocus() { refocus() {
this.mapview.apply(this.map) this.mapview.apply(this.map)
} }
/**
* @public
* @returns {GeoMap} - Returns the active map.
* @readonly
* @memberof MapLayer
*/
get map() { get map() {
return this.mapList.map return this.mapList.map
} }
/** /**
* This is required for the consistency of georelated layers. *
* The request traverses up to the mapLayer where it then returns * This is required for the geo layers.
* the responsible map layer. * MapLayer requests from the geoLayers traverse up to the next MapLayer.
*
* @public
* @returns {MapLayer} - Returns this MapLayer.
* @readonly
* @memberof MapLayer
*/ */
get mapLayer() { get mapLayer() {
return this return this
} }
/**
* Cleans up the MapLayer.
*
* @public
* @memberof MapLayer
*/
cleanup() { cleanup() {
this.mapList.cleanup() this.mapList.cleanup()
} }
+112
View File
@@ -0,0 +1,112 @@
<html>
<head>
<title>PIXI Maps Doctests</title>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0" />
<link rel="stylesheet" href="../../../css/index.css">
<script src="../../../dist/iwmlib.js"></script>
<template id="itemTemplate">
<a class="wrapper" href="">
<div class="preview">
<div class="thumbnail-container">
<div class="thumbnail">
<img class="icon" src="thumbnails/notfound.png">
<!-- <iframe src="" frameborder="0"></iframe> -->
</div>
</div>
<div class="title"></div>
</div>
</a>
</template>
<style>
body {
font-size: 20px;
display: flex;
flex-direction: column;
}
#logo {
left: 0;
position: absolute;
top: 30px;
}
#logo>img {
width: 80px;
}
header>h1 {
text-transform: uppercase;
letter-spacing: 0.05em;
word-spacing: 0.25em;
margin-top: 0;
}
header>p {
max-width: 720px;
line-height: 1.5em;
color: rgb(207, 207, 207);
}
header {
font-family: "Open Sans", sans-serif;
background-color: #4c4f4f;
color: whitesmoke;
padding: 68px 50px 10px 150px;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}
.container {
justify-content: center;
flex: 1;
height: auto;
min-height: auto;
width: auto;
min-width: auto;
margin: 0 60px;
}
</style>
</head>
<body>
<header>
<a id="logo" target="_blank" href="http://www.iwm-tuebingen.de">
<img src="../../../assets/logos/iwm_logo_2015_twitter.png">
</a>
<h1>Maps Module</h1>
<p>The maps module provides a handy toolkit to easily integrate maps in an application. Create a full screen map
application by using the mapapp. Utilize the GeoLayer-system to integrate maps in an existing application.
Draw graphics onto the map using geographical positions instead of pixel positions with the GeoGraphics.
Or just use an Overlay to quickly draw icons for each point on a map.
</p>
</header>
<div id="container" class="container">
</div>
<script>
const index = new Index(itemTemplate, [
["GeoGraphics", "geographics.html"],
["GeoJson", "geojson.html"],
["GeoMap", "map.html"],
["MapApp", "mapapp.html"],
["MapProjection", "mapprojection.html"],
["MapViewport", "mapviewport.html"],
["Overlay", "overlay.html"],
["Scatter", "scatter.html"]
],
null)
index.load()
</script>
</body>
</html>
+4 -4
View File
@@ -71,10 +71,10 @@
const projection = new Projection.Mercator() const projection = new Projection.Mercator()
// Create a map data object. // Create a map data object.
const osmDeepZoomMapData = new DeepZoomMapData(projection, tilesConfig, { app }) const osmDeepZoomMapProjection = new DeepZoomMapProjection(projection, tilesConfig, { app })
// Create the map // Create the map
const osmMap = new DeepZoomMap(osmDeepZoomMapData, tilesConfig) const osmMap = new DeepZoomMap(osmDeepZoomMapProjection, tilesConfig)
// Add the map to the app. // Add the map to the app.
app.addMap("osm", osmMap) app.addMap("osm", osmMap)
@@ -122,10 +122,10 @@
const projection = new Projection.Robinson(10) const projection = new Projection.Robinson(10)
// Create a map data object. // Create a map data object.
let mapData = new MapData(projection) let mapProjection = new MapProjection(projection)
// Create the map // Create the map
let imageMap = new ImageMap(sprites.get(mapTexture), mapData) let imageMap = new ImageMap(sprites.get(mapTexture), mapProjection)
// Add the map to the app. // Add the map to the app.
+149 -68
View File
@@ -1,12 +1,12 @@
import { MapObjectScatter } from './scatter.js' import { MapObjectScatter } from './scatter.js'
import { DeepZoomImage, DeepZoomInfo } from '../deepzoom/image.js' import { DeepZoomImage, DeepZoomInfo } from '../deepzoom/image.js'
import { MapData, DeepZoomMapData } from './mapdata.js' import { MapProjection, DeepZoomMapProjection } from './mapprojection.js'
import { Points } from '../../utils.js' import { Points } from '../../utils.js'
import { EventHandler } from './utils.js' import { EventHandler } from './utils.js'
import Mercator from './projections/mercator.js' import Mercator from './projections/mercator.js'
/** /**
* The GeoMap class displays a map, that it gets from MapData object. * The GeoMap class displays a map, that it gets from MapProjection object.
* It handles the current location on the map, the zoom factor, interactions and * It handles the current location on the map, the zoom factor, interactions and
* the viewport, the area of the map, the user can see and navigate to. * the viewport, the area of the map, the user can see and navigate to.
* *
@@ -26,12 +26,12 @@ export class GeoMap {
* Creates instance of GeoMap * Creates instance of GeoMap
* *
* @constructor * @constructor
* @param {MapData}[mapdata={}] - The mapdata describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map? * @param {MapProjection}[mapProjection={}] - The map projection describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map?
* @param {object}[opts={}] - With the opts, the created MapObjectScatter can be adjusted. * @param {object}[opts={}] - With the opts, the created MapObjectScatter can be adjusted.
* @param {boolean}[opts.cover=false] - Enables covering behaviour of a map object. Normally maps should cover the whole app. * @param {boolean}[opts.cover=false] - Enables covering behaviour of a map object. Normally maps should cover the whole app.
*/ */
constructor( constructor(
mapdata = {}, mapProjection = {},
{ {
debug = true, debug = true,
cover = true, cover = true,
@@ -43,7 +43,7 @@ export class GeoMap {
translatable = true, translatable = true,
scalable = true, scalable = true,
rotatable = false, // Many functionalities are not supported when rotating the map. Mainly the cover mechanism. rotatable = false, // Many functionalities are not supported when rotating the map. Mainly the cover mechanism.
viewport = mapdata.maxViewport, viewport = mapProjection.maxViewport,
// Events // Events
onLoad = null, onLoad = null,
onTransform = null onTransform = null
@@ -54,7 +54,7 @@ export class GeoMap {
this.onTransform = new EventHandler('transform', { listeners: onTransform }) this.onTransform = new EventHandler('transform', { listeners: onTransform })
this._alpha = alpha this._alpha = alpha
this.cover = cover this._cover = cover
this.debug = debug this.debug = debug
//TODO discuss if this is required here. //TODO discuss if this is required here.
@@ -68,15 +68,15 @@ export class GeoMap {
this.scalable = scalable this.scalable = scalable
this.viewport = viewport this.viewport = viewport
this.mapdata = mapdata this._mapProjection = mapProjection
this.overlays = {} this.overlays = {}
/** /**
* Adjust the viewport depending on the mapdata clipping. * Adjust the viewport depending on the mapProjection clipping.
*/ */
if (this.mapdata.clip) { if (this.mapProjection.clip) {
const vp = this.viewport const vp = this.viewport
const cp = this.mapdata.clip const cp = this.mapProjection.clip
let bounds = { let bounds = {
min: { min: {
x: vp.min.x > cp.min.x ? vp.min.x : cp.min.x, x: vp.min.x > cp.min.x ? vp.min.x : cp.min.x,
@@ -92,29 +92,64 @@ export class GeoMap {
} }
} }
/**
* Determines if the scatter covers the container.
* @member {boolean}
* @readonly
* @memberof GeoMap
*/
get cover() {
// For cloning we want to store the cover inside the map.
// But the scatter is responsible for the cover state.
// So we update the app's cover state every time
// we get the value.
if (this.image && this.image.scatter && this.image.scatter.cover != null) {
this._cover = this.image.scatter.cover
}
return this._cover
}
/**
* Returns the image object used by the GeoMap.
*
* @readonly
* @memberof GeoMap
*/
get image() {
return this._image
}
/**
* The mapProjection of the map.
*
* @member {MapProjection}
* @readonly
* @memberof GeoMap
*/
get mapProjection() {
return this._mapProjection
}
/**
* Clears all EventHandlers.
*
* @memberof GeoMap
*/
flushHandlers() { flushHandlers() {
// this.onLoaded this.onLoad.empty()
this.onTransform.empty() this.onTransform.empty()
} }
/**
* Locks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) { lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable
this.image.scatter.movableX = translatable
this.image.scatter.movableY = movableX
this.image.scatter.rotatable = movableY
this.image.scatter.scalable = scalable
}
// Issue #001: This causes the map to not be displayed at the correct position on
// map change.
// // Rotation does not yet work with the cover mechanism.
// //this.rotatable = false
// this.translatable = false
// this.scalable = false
}
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
if (this.image && this.image.scatter) { if (this.image && this.image.scatter) {
this.image.scatter.translatable = translatable this.image.scatter.translatable = translatable
this.image.scatter.movableX = movableX this.image.scatter.movableX = movableX
@@ -122,13 +157,26 @@ export class GeoMap {
this.image.scatter.rotatable = rotatable this.image.scatter.rotatable = rotatable
this.image.scatter.scalable = scalable this.image.scatter.scalable = scalable
} }
// Issue #001
// // Rotation does not yet work with the cover mechanism.
// //this.rotatable = true
// this.translatable = true
// this.scalable = true
} }
/**
* Unlocks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
this.lock({ rotatable, translatable, movableX, movableY, scalable })
}
/**
* Unloads the image of the map.
*
* @public
* @memberof GeoMap
*/
unload() { unload() {
if (this.image) { if (this.image) {
if (this.image.parent) { if (this.image.parent) {
@@ -142,6 +190,12 @@ export class GeoMap {
} }
} }
/**
* Removes the map, freeing all memory ba flushing handlers and removing the image.
*
* @public
* @memberof GeoMap
*/
remove() { remove() {
if (this.image) this.image.mask = null if (this.image) this.image.mask = null
@@ -164,6 +218,7 @@ export class GeoMap {
/** /**
* Wrapps the display object around a scatter object. * Wrapps the display object around a scatter object.
* creates the image.
* *
* @private * @private
* @param {DisplayObject} displayObject - Defines the display object that will be wrapped inside the scatter object. * @param {DisplayObject} displayObject - Defines the display object that will be wrapped inside the scatter object.
@@ -172,11 +227,9 @@ export class GeoMap {
load(image, renderer, frame = null, scatter = null) { load(image, renderer, frame = null, scatter = null) {
if (this.debug) console.log('Load image: ', image, frame) if (this.debug) console.log('Load image: ', image, frame)
this.image = image this._image = image
if (frame) this.setFrame(frame) if (frame) this.setFrame(frame)
let boundaries = this.mapdata.getBoundaries()
let scatterOpts = Object.assign({ let scatterOpts = Object.assign({
cover: this.cover, cover: this.cover,
scaleable: this.scaleable, scaleable: this.scaleable,
@@ -186,7 +239,6 @@ export class GeoMap {
startScale: this.startScale, startScale: this.startScale,
minScale: this.minScale, minScale: this.minScale,
maxScale: this.maxScale, maxScale: this.maxScale,
boundaries,
onTransform: this.transformed.bind(this) onTransform: this.transformed.bind(this)
}) })
@@ -242,11 +294,11 @@ export class GeoMap {
* to a coordinate with latitude and longitude. * to a coordinate with latitude and longitude.
* *
* *
* @param {object} point - Point in form of {x: x_val, y: y_val}. * @param {object} point - Point in form of {x, y}.
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}. * @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
*/ */
coordinatesFromPoint(point) { coordinatesFromPoint(point) {
let coords = this.mapdata.toCoordinates(this.toRelativePosition(point)) let coords = this.mapProjection.toCoordinates(this.toRelativePosition(point))
return coords return coords
} }
@@ -254,10 +306,10 @@ export class GeoMap {
* Transform coordinates in the map into pixel positions on the deep zoom image. * Transform coordinates in the map into pixel positions on the deep zoom image.
* *
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}. * @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
* @return - Returns a image position in form of {x: x_val, y: y_val}. * @return {Point} - Returns a image position in form of {x: x, y: y}.
*/ */
coordinatesToPoint(coordinates) { coordinatesToPoint(coordinates) {
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates)) return this.toAbsolutePixelCoordinates(this.mapProjection.toPixel(coordinates))
} }
toRelativePosition(point) { toRelativePosition(point) {
@@ -324,15 +376,41 @@ export class GeoMap {
this.frame = null this.frame = null
} }
/**
* The complete Triforce, or one or more components of the Triforce.
* @typedef {Object} Frame
* @property {number} x - X position of the frame.
* @property {number} y - Y position of the frame.
* @property {number} width - Width of the frame.
* @property {number} height - Height od the frame.
* @property {Point} localCenter - Local center of the map.
* @property {Point} center - Global center of the map.
*/
/**
* Sets the frame if the map.
*
* Frame is the display in which the map is shown.
* Normally it's the app, but it can be another element,
* for example when in a submap.
*
* @param {Frame} frame
* @memberof GeoMap
*/
setFrame(frame) { setFrame(frame) {
if (this.debug) console.log('Set Frame: ', frame)
this.frame = frame this.frame = frame
} }
/** /**
* Gets the frame if the map.
*
* Frame is the display in which the map is shown. * Frame is the display in which the map is shown.
* Normally it's the app, but it can be another element, * Normally it's the app, but it can be another element,
* for example when in a submap. * for example when in a submap.
*
* @returns {Frame} - Returns the frame of the map.
* @memberof GeoMap
*/ */
getFrame() { getFrame() {
let frame = { let frame = {
@@ -418,6 +496,7 @@ export class GeoMap {
* for creating the maps. * for creating the maps.
* *
* @static * @static
* @private
* @param {object} json - The object containing multiple map data sets. * @param {object} json - The object containing multiple map data sets.
* @param {error-object} error - An object that contains an parameter message: {message = ""}. This is faking a call by reference. * @param {error-object} error - An object that contains an parameter message: {message = ""}. This is faking a call by reference.
* @returns {boolean} - True if all sets were valid. False otherwise. * @returns {boolean} - True if all sets were valid. False otherwise.
@@ -453,6 +532,7 @@ export class GeoMap {
*Validates of a single data set contains the valid data for creating a map. *Validates of a single data set contains the valid data for creating a map.
* *
* @static * @static
* @private
* @param {object} json - The object containing a single set of map data. * @param {object} json - The object containing a single set of map data.
* @returns {boolean} - True if valid, otherwise false. * @returns {boolean} - True if valid, otherwise false.
* @memberof GeoMap * @memberof GeoMap
@@ -488,8 +568,8 @@ export class GeoMap {
const tilesConfig = data.tiles const tilesConfig = data.tiles
const options = data.options const options = data.options
const mapdata = new DeepZoomMapData(projection, tilesConfig) const mapProjection = new DeepZoomMapProjection(projection, tilesConfig)
return new DeepZoomMap(mapdata, tilesConfig, options) return new DeepZoomMap(mapProjection, tilesConfig, options)
} }
static _getProjectionByName(projection) { static _getProjectionByName(projection) {
@@ -506,43 +586,42 @@ export class GeoMap {
GeoMap.counter = 0 GeoMap.counter = 0
/** /**
* The DeepZoomMap class extends the GeoMap to create * The DeepZoomMap class extends the GeoMap to create
* maps as deepzoom images from maptiles. * maps as deepzoom images from maptiles.
* *
* @extends GeoMap * @export
* @class * @class DeepZoomMap
* @see {@link maps.html} * @extends {GeoMap}
*/ */
export class DeepZoomMap extends GeoMap { export class DeepZoomMap extends GeoMap {
/** /**
* @constructor * @constructor
* @param {object} tilesConfig - The tiles config object, that defines at what path and in which format the tiles are. * @param {object} tilesConfig - The tiles config object, that defines at what path and in which format the tiles are.
* @param {MapData} mapdata - A MapData object, that contains informations of how the given map has to be interpreted. * @param {MapProjection} mapProjection - A MapProjection object, that contains informations of how the given map has to be interpreted.
* @param {object} opts - Additional options to specify the behaviour of the deep zoom image. * @param {object} opts - Additional options to specify the behaviour of the deep zoom image.
*/ */
constructor(mapdata, tilesConfig, opts = {}) { constructor(mapProjection, tilesConfig, opts = {}) {
opts = Object.assign( opts = Object.assign(
{ {
maxScale: Math.min(tilesConfig.width, tilesConfig.height) / tilesConfig.tileSize, maxScale: Math.min(tilesConfig.width, tilesConfig.height) / tilesConfig.tileSize,
minScale: mapdata.getMinScale, minScale: mapProjection.getMinScale,
highResolution: true, highResolution: true,
debug: false debug: false
}, },
opts opts
) )
super(mapdata, opts) super(mapProjection, opts)
this.tilesConfig = tilesConfig this.tilesConfig = tilesConfig
this._verifyMapdata() this._verifyMapProjection()
} }
_verifyMapdata() { _verifyMapProjection() {
if (!(this.mapdata instanceof MapData)) { if (!(this.mapProjection instanceof MapProjection)) {
console.error('Use the MapData object for creating maps!') console.error('Use the MapProjection object for creating maps!')
} else { } else {
if (!(this.mapdata instanceof DeepZoomMapData)) { if (!(this.mapProjection instanceof DeepZoomMapProjection)) {
console.error('Use the DeepZoomMapData object.') console.error('Use the DeepZoomMapProjection object.')
} }
} }
} }
@@ -553,10 +632,10 @@ export class DeepZoomMap extends GeoMap {
* @private * @private
*/ */
load(container = null, scatter = null) { load(container = null, scatter = null) {
if (!this.mapdata.app) console.error('App was not set in the mapdata.') if (!this.mapProjection.app) console.error('App was not set in the mapProjection.')
this.info = new DeepZoomInfo(this.tilesConfig) this.info = new DeepZoomInfo(this.tilesConfig)
let image = new DeepZoomImage(this.info, { let image = new DeepZoomImage(this.info, {
app: this.mapdata.app, app: this.mapProjection.app,
alpha: this.alpha, alpha: this.alpha,
debug: this.debug, debug: this.debug,
world: scatter == null ? scatter : scatter.getWorldScatter() world: scatter == null ? scatter : scatter.getWorldScatter()
@@ -674,7 +753,7 @@ export class DeepZoomMap extends GeoMap {
} }
clone(container, scatter = null) { clone(container, scatter = null) {
const map = new DeepZoomMap(this.mapdata, this.tilesConfig, { const map = new DeepZoomMap(this.mapProjection, this.tilesConfig, {
alpha: this.alpha, alpha: this.alpha,
cover: this.cover, cover: this.cover,
debug: this.debug, debug: this.debug,
@@ -734,15 +813,17 @@ DeepZoomMap.tintcolors = [0xff0000, 0xff00ff, 0xffff00, 0x00ff00, 0x00ffff, 0x00
DeepZoomMap.tintcolor = 0 DeepZoomMap.tintcolor = 0
/** /**
*
* ImageMap extends GeoMap to display simple images * ImageMap extends GeoMap to display simple images
* as maps. * as maps.
*
* @export
* @class ImageMap
* @extends {GeoMap}
*/ */
export class ImageMap extends GeoMap { export class ImageMap extends GeoMap {
constructor(sprite, mapdata, opts = {}) { constructor(sprite, mapProjection, opts = {}) {
super(mapdata, opts) super(mapProjection, opts)
if (this.debug) console.log('Construct Image Map', sprite, mapdata, opts) if (this.debug) console.log('Construct Image Map', sprite, mapProjection, opts)
this.sprite = sprite this.sprite = sprite
@@ -756,7 +837,7 @@ export class ImageMap extends GeoMap {
} }
clone(container = null, scatter = null) { clone(container = null, scatter = null) {
const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapdata, { const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapProjection, {
alpha: this.alpha, alpha: this.alpha,
cover: this.cover, cover: this.cover,
debug: this.debug, debug: this.debug,
+6 -6
View File
@@ -98,11 +98,11 @@
resolutionDependent: false resolutionDependent: false
}) })
// The mapdata object contains informations, // The map projection object contains informations,
// how the displayed map has to be interpreted. // how the displayed map has to be interpreted.
// e.g. which projection is used or how the // e.g. which projection is used or how the
// image is clipped. // image is clipped.
let europeData = new MapData(new Projection.Mercator(), { let europeData = new MapProjection(new Projection.Mercator(), {
clip: { clip: {
min: { x: 32.863294, y: -18.58 }, min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 } max: { x: 57.467973, y: 44.277158 }
@@ -120,16 +120,16 @@
cover cover
}) })
let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, { let testMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), testConfig.tiles, {
app app
}) })
let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover }) let testMap = new DeepZoomMap(testMapProjection, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap) app.addMap("test", testMap)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, { let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app app
}) })
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover }) let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap) app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp // Finally apply the map to the MapApp
+163 -51
View File
@@ -7,12 +7,35 @@ import { Points } from '../../utils.js'
import Logging from '../../logging.js' import Logging from '../../logging.js'
import { MapList } from './maplist.js' import { MapList } from './maplist.js'
/**
* A PIXI.Point or object in form {x,y}.
*
* @typedef {Object} Point
* @property {number} x - Indicates whether the Courage component is present.
* @property {number} y - Indicates whether the Power component is present.
*/
/**
* A coordinate point is a PIXI.Point or object in form {x,y} that contains map coordinates
* instead of pixel values, where x represents the latitude and y the longitude.
* @typedef {Object} CoordinatePoint
* @property {number} x - Indicates whether the Courage component is present.
* @property {number} y - Indicates whether the Power component is present.
*/
/** /**
* MapApp is responsible for showing fullscreen * MapApp is responsible for showing fullscreen
* map applications. * map applications.
* *
* @export
* @class MapApp
* @extends {PIXIApp}
*/ */
export default class MapApp extends PIXIApp { export default class MapApp extends PIXIApp {
/**
*Creates an instance of MapApp.
* @memberof MapApp
*/
constructor(opts = {}) { constructor(opts = {}) {
super(opts) super(opts)
@@ -27,7 +50,6 @@ export default class MapApp extends PIXIApp {
coordsLogging: false, coordsLogging: false,
overlays: {}, overlays: {},
keycodes: {}, keycodes: {},
showHotkeys: false,
imageMapZoomHeight: 256, //Defines the zoomvalue 1 for all image maps inside the mapapp. imageMapZoomHeight: 256, //Defines the zoomvalue 1 for all image maps inside the mapapp.
focus: null, focus: null,
zoom: 1, zoom: 1,
@@ -44,7 +66,6 @@ export default class MapApp extends PIXIApp {
this.overlayElements = new Map() this.overlayElements = new Map()
this.debug = opts.debug this.debug = opts.debug
this.fpsLogging = opts.fpsLogging this.fpsLogging = opts.fpsLogging
this.showHotkeys = opts.showHotkeys
this.keycodes = this._extractKeyCodes(opts.keycodes) this.keycodes = this._extractKeyCodes(opts.keycodes)
this.coordsLogging = opts.coordsLogging this.coordsLogging = opts.coordsLogging
this.overlays = opts.overlays this.overlays = opts.overlays
@@ -106,9 +127,15 @@ export default class MapApp extends PIXIApp {
console.log(JSON.stringify(boundaries)) console.log(JSON.stringify(boundaries))
} }
/**
* Creates the MapLayer.
*
* @private
* @memberof MapApp
*/
_setupMapLayer() { _setupMapLayer() {
this.mapContainer = new PIXI.Container() this.mapContainer = new PIXI.Container()
this.mapLayer = new MapLayer(this.mapList, this.scene, this.mapContainer, { this._mapLayer = new MapLayer(this.mapList, this.scene, this.mapContainer, {
name: 'Root Map Layer', name: 'Root Map Layer',
focus: this.focus, focus: this.focus,
zoom: this.zoom, zoom: this.zoom,
@@ -174,26 +201,49 @@ export default class MapApp extends PIXIApp {
} }
} }
/**
* Relayouts the app. E.g. called when the window is resized.
*
* @param {number} width - Desired width of the app.
* @param {number} height - Desired height of the app.
* @memberof MapApp
*/
layout(width, height) { layout(width, height) {
this.scene.resize(width, height) this.scene.resize(width, height)
this.mapLayer.mapview.update() this.mapLayer.mapview.update()
this.onSizeChanged.call(this) this.onSizeChanged.call(this)
} }
/**
* Overrides the sceneFactory of the PIXIApp to create a RigidScatterContainer instead of
* a regular PIXI.Container()
*
* @private
* @returns {RigidScatterContainer} - Returns the newly created RigidScatterContainer.
* @memberof MapApp
*/
sceneFactory() { sceneFactory() {
return new RigidScatterContainer(this.width, this.height, this.renderer, { return new RigidScatterContainer(this.width, this.height, this.renderer, {
app: this, app: this,
showBounds: true, showBounds: false,
showTouches: true, showTouches: false,
showPolygon: true, showPolygon: false,
container: this container: this,
stopEvents: false,
claimEvents: false
}) })
} }
/**
* Changes the map to the given key.
*
* @param {string} key - Identifier of the map to change to.
* @memberof MapApp
*/
selectMap(key) { selectMap(key) {
if (this.debug) console.log('Select map', key, result)
let result = this.mapList.select(key) let result = this.mapList.select(key)
console.log('Select map', key, result)
if (result && this.mapLayer) { if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map) this.mapLayer.changeMap(this.mapList.map)
} }
@@ -211,14 +261,30 @@ export default class MapApp extends PIXIApp {
this.selectMap(key) this.selectMap(key)
} }
/**
* Adds a map to the maplist.
* If no map is set, the added map will be set as default.
*
* @param {string} key - Identifier for the map.
* @param {GeoMap} map - Map object to add.
* @memberof MapApp
*/
addMap(key, map) { addMap(key, map) {
if (this.mapList) this.mapList.add(key, map) if (this.mapList) this.mapList.add(key, map)
else console.error('Cannot access mapLayer. It was not initialized yet.') else console.error('Cannot access mapLayer. It was not initialized yet.')
} }
/**
* Adds multiple maps at once.
*
* @param {object} mapObject
* @memberof MapApp
*/
addMaps(mapObject) { addMaps(mapObject) {
for (let [key, val] of Object.entries(mapObject)) { for (let [key, val] of Object.entries(mapObject)) {
this.addMap(key, val) if (val instanceof GeoMap) {
this.addMap(key, val)
} else console.warn('Tried adding maps that are not og Type GeoMap.')
} }
} }
@@ -226,6 +292,14 @@ export default class MapApp extends PIXIApp {
this.onTransform.call(this, event) this.onTransform.call(this, event)
} }
/**
*
* Called when the mapLayer changed the map.
*
* @private
* @param {*} lastMap
* @memberof MapApp
*/
_mapChanged(lastMap) { _mapChanged(lastMap) {
if (lastMap) { if (lastMap) {
lastMap.flushHandlers() lastMap.flushHandlers()
@@ -243,6 +317,16 @@ export default class MapApp extends PIXIApp {
} }
} }
/**
* Returns the mapLayer of the map.
*
* @member {MapLayer}
* @memberof MapApp
*/
get mapLayer() {
return this._mapLayer
}
_doesOverlayElementExist(layer, type, name) { _doesOverlayElementExist(layer, type, name) {
let layerElements = this.overlayElements.get(layer) let layerElements = this.overlayElements.get(layer)
return layerElements != undefined && layerElements[type] != null && layerElements[type][name] != null return layerElements != undefined && layerElements[type] != null && layerElements[type][name] != null
@@ -265,7 +349,9 @@ export default class MapApp extends PIXIApp {
} }
/** /**
* Copies the current coordinates to the clipboard. * Copies the current location to the clipboard.
*
* @memberof MapApp
*/ */
locationToClipboard() { locationToClipboard() {
let hidden = document.createElement('input') let hidden = document.createElement('input')
@@ -278,6 +364,15 @@ export default class MapApp extends PIXIApp {
document.body.removeChild(hidden) document.body.removeChild(hidden)
} }
/**
* Can be used to copy polygons to the clipboard.
*
* Useful for debugging or to roughly trace a shape in the map.
* The generated pointarray can be used as geometry of a geographic
* or inside an overlay to draw that shape onto the map.
*
* @memberof MapApp
*/
pathToClipboard() { pathToClipboard() {
let hidden = document.createElement('input') let hidden = document.createElement('input')
document.body.appendChild(hidden) document.body.appendChild(hidden)
@@ -301,25 +396,27 @@ export default class MapApp extends PIXIApp {
document.body.removeChild(hidden) document.body.removeChild(hidden)
} }
/**
* Returns the active map.
*
* @readonly
* @memberof MapApp
*/
get map() { get map() {
return this.mapList.map return this.mapList.map
} }
get activeMapKey() {
return this.mapLayer.active
}
getRelativePosition(x, y) {
return {
x: x * app.width,
y: y * app.height
}
}
clearDrawData() { clearDrawData() {
this.drawData = [] this.drawData = []
} }
/**
* Logs a text field on the map.
* The text element is a DOMElement.
*
* @param {string} msg - Message to log.
* @memberof MapApp
*/
showNotification(msg) { showNotification(msg) {
let notification = document.createElement('div') let notification = document.createElement('div')
notification.classList.add('notification') notification.classList.add('notification')
@@ -362,8 +459,25 @@ export default class MapApp extends PIXIApp {
}) })
} }
_currentLocationToString() {} /**
* @typedef KeyCode
* @type {object}
* @property {string} key - an ID.
* @property {boolean} altKey - Defines if KeyCode requires the alt key to be pressed.
* @property {boolean} shiftKey - Defines if KeyCode requires the shift key to be pressed.
* @property {boolean} ctrlKey - Defines if KeyCode requires the ctrl key to be pressed.
*
*/
/**
* Check's if a key event matches a defined KeyCode.
*
* @private
* @param {KeyboardEvent} event - Event that is fired on keydown, -pressed or -up.
* @param {KeyCode} keyCode - KeyCode is an object in the form of : {key:number, altKey:boolean,shiftKey:boolean,ctrlKey:boolean }
* @returns {boolean} - True, when the event matches the keycode, false otherwise.
* @memberof MapApp
*/
_matchKeyCode(event, keyCode) { _matchKeyCode(event, keyCode) {
// If keycode does not exist or is invalid - return. // If keycode does not exist or is invalid - return.
if (!keyCode || keyCode.key == null) return false if (!keyCode || keyCode.key == null) return false
@@ -385,6 +499,13 @@ export default class MapApp extends PIXIApp {
}) })
} }
/**
* Checks on every key down if it matches a keycode.
*
* @private
* @param {KeyboardEvent} event
* @memberof MapApp
*/
_checkForKeyCode(event) { _checkForKeyCode(event) {
if (this._matchKeyCode(event, this.keycodes.copyCoordinate)) { if (this._matchKeyCode(event, this.keycodes.copyCoordinate)) {
event.preventDefault() event.preventDefault()
@@ -443,10 +564,28 @@ export default class MapApp extends PIXIApp {
this.drawMode = this.DRAW_MODES.PIXI_POINT this.drawMode = this.DRAW_MODES.PIXI_POINT
} }
_extractKeyCodes(keycodeText) { /**
* @typedef KeyCodePairs
* @type {object}
* @property {string} name - Name of the KeyCode.
* @property {KeyCode} keyCode - KeyCode
*
*/
/**
* Extracts keycodes from a string.
*
* KeycodeStrings may look like this 'ctrl+shift+b'
*
* @private
* @param {KeyCodePairs} nameKeyCodePairs
* @returns {array} - Returns an array of KeyCode objects.
* @memberof MapApp
*/
_extractKeyCodes(nameKeyCodePairs) {
let out = {} let out = {}
for (let [name, combinationString] of Object.entries(keycodeText)) { for (let [name, combinationString] of Object.entries(nameKeyCodePairs)) {
let keys = combinationString.split('+') let keys = combinationString.split('+')
out[name] = { out[name] = {
key: null, key: null,
@@ -517,34 +656,7 @@ export default class MapApp extends PIXIApp {
const center = submap.center const center = submap.center
const radius = submap.container.width / 2 const radius = submap.container.width / 2
const distance = Points.distance(center, event.data.global) / submap.scatter.scale const distance = Points.distance(center, event.data.global) / submap.scatter.scale
if (distance > radius) {
//submap.resize((distance) * 2, .2)
//submap.centerAt(myevent.center)
}
} }
// for (const submap of this.submaps) {
// const center = submap.center
// const radius = submap.container.width / 2
// const distance = Points.distance(center, event.data.global) / submap.scatter.scale
// const inside = distance < radius + 10
// console.log(distance, radius)
// if (inside) {
// // (this.width + 80) / 2 * this.scatter.scale
// //const width = (submap.width + 80) / 2 * submap.scatter.scale
// //console.log(width)
// if (distance > radius) {
// submap.resize((distance) * 2, .2)
// }
// } else {
// if (distance < radius + 20) {
// //submap.resize((distance - 30) * 2, .2)
// }
// }
// }
} }
} }
+73 -5
View File
@@ -1,7 +1,14 @@
/**
* MapList is a list of maps with one active index.
* It contains some utility functions to change the map.
*
* @export
* @class MapList
*/
export class MapList { export class MapList {
constructor(active = null, maps = {}) { constructor(active = null, maps = {}) {
this.maps = maps this._maps = maps
this.active = active this._active = active
if (Object.keys(maps).length > 0) this.select(active) if (Object.keys(maps).length > 0) this.select(active)
} }
@@ -9,8 +16,9 @@ export class MapList {
/** /**
* Selects a map from the map list. * Selects a map from the map list.
* *
* @public
* @param {string} active - Name of the map to select. * @param {string} active - Name of the map to select.
* @returns * @returns {Map} - Returns the active map. Returns null if no map was added to the MapList.
* @memberof MapList * @memberof MapList
*/ */
select(active) { select(active) {
@@ -29,7 +37,7 @@ export class MapList {
} }
if (this.active !== active) { if (this.active !== active) {
this.active = active this._active = active
map = this.maps[active] map = this.maps[active]
} }
} else { } else {
@@ -40,6 +48,13 @@ export class MapList {
return map return map
} }
/**
* Clones the entire maplist.
*
* @public
* @returns {MapList} - Returns a cloned instance of this map list.
* @memberof MapList
*/
clone() { clone() {
let maps = {} let maps = {}
@@ -50,17 +65,63 @@ export class MapList {
return new MapList(this.active, maps) return new MapList(this.active, maps)
} }
/**
* Adds a new map to the map list.
*
* @public
* @param {string} key - Key to identify the map.
* @param {GeoMap} map - The GeoMap to add.
* @memberof MapList
*/
add(key, map) { add(key, map) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.') if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
if (this.active == null) this.active = key if (this.active == null) this._active = key
map.name = key map.name = key
this.maps[key] = map this.maps[key] = map
} }
/**
* Returns the the active map.
* If none is set, it returns null.
*
*@public
* @readonly
* @member {GeoMap}
* @memberof MapList
*/
get map() { get map() {
return this.maps && this.maps[this.active] ? this.maps[this.active] : null return this.maps && this.maps[this.active] ? this.maps[this.active] : null
} }
/**
* Returns the list of addedd cards.
*
* @member {object}
* @readonly
* @memberof MapList
*/
get maps() {
return this._maps
}
/**
* Returns the active key.
*
* @member {string}
* @readonly
* @memberof MapList
*/
get active() {
return this._active
}
/**
* Selects the next map in the map array.
*
* @public
* @returns {GeoMap} - Returns the next map in the list.
* @memberof MapList
*/
next() { next() {
let keys = Object.keys(this.maps) let keys = Object.keys(this.maps)
let idx = keys.indexOf(this.active) let idx = keys.indexOf(this.active)
@@ -69,6 +130,13 @@ export class MapList {
return next return next
} }
/**
* Removes all maps from the maplist.
* And cleans up all maps.
*
* @public
* @memberof MapList
*/
cleanup() { cleanup() {
for (let key in this.maps) { for (let key in this.maps) {
let map = this.maps[key] let map = this.maps[key]
@@ -3,7 +3,7 @@
<head> <head>
<meta charset='UTF-8'> <meta charset='UTF-8'>
<title>MapData</title> <title>MapProjection</title>
<link rel='stylesheet' href='../../3rdparty/highlight/styles/default.css'> <link rel='stylesheet' href='../../3rdparty/highlight/styles/default.css'>
@@ -37,8 +37,8 @@
</head> </head>
<body onload='Doctest.run()'> <body onload='Doctest.run()'>
<h1>MapData</h1> <h1>MapProjection</h1>
<p>Mapdata calculates is responsible for transforming map coordinates to pixel coordinates and backwards.</p> <p>The map projection calculates is responsible for transforming map coordinates to pixel coordinates and backwards.</p>
<h2>Static Squared World Map</h2> <h2>Static Squared World Map</h2>
<p>The most simple example is a squared world map, thats projected with mercator transformation. Ranging from <p>The most simple example is a squared world map, thats projected with mercator transformation. Ranging from
@@ -73,9 +73,9 @@
//Helper function to print the coordinates to a dom element. //Helper function to print the coordinates to a dom element.
function printCoordinates(element, mapdata, out_dom) { function printCoordinates(element, mapProjection, out_dom) {
element.addEventListener("mousemove", (event) => { element.addEventListener("mousemove", (event) => {
let coords = mapdata.toCoordinates({ x: event.offsetX / event.target.width, y: event.offsetY / event.target.height }) let coords = mapProjection.toCoordinates({ x: event.offsetX / event.target.width, y: event.offsetY / event.target.height })
out_dom.innerHTML = "Lat: " + coords.x.toFixed(4) + " Lng: " + coords.y.toFixed(4) out_dom.innerHTML = "Lat: " + coords.x.toFixed(4) + " Lng: " + coords.y.toFixed(4)
}) })
} }
@@ -99,8 +99,8 @@
</script> </script>
<script class="doctest"> <script class="doctest">
// Instantiate our mapdata. // Instantiate our map projection.
let squared_world = new MapData(new Projection.Mercator()); let squared_world = new MapProjection(new Projection.Mercator());
// Define a position, we are interested in in {x: latitude, y: longitude} // Define a position, we are interested in in {x: latitude, y: longitude}
let iceland = { x: 64.514979, y: -19.020796 } let iceland = { x: 64.514979, y: -19.020796 }
@@ -116,7 +116,7 @@
printCoordinates(map_image_0, squared_world, map_coords_0) printCoordinates(map_image_0, squared_world, map_coords_0)
</script> </script>
<h2 id="germany">Clipped Map</h2> <h2 id="germany">Clipped Map</h2>
<p>Often we don't use the whole map, or our map is a subsection of the world. MapData can clip those cases, using <p>Often we don't use the whole map, or our map is a subsection of the world. MapProjection can clip those cases, using
the a the a
bounding box of min and max coordinates.</p> bounding box of min and max coordinates.</p>
<p>Coordinates: <p>Coordinates:
@@ -127,8 +127,8 @@
</div> </div>
<script class="doctest"> <script class="doctest">
//Same procedure as above, just add a clipping parameter to the MapData object. //Same procedure as above, just add a clipping parameter to the MapProjection object.
let europe = new MapData(new Projection.Mercator(), { let europe = new MapProjection(new Projection.Mercator(), {
clip: { clip: {
min: { x: 32.863294, y: -18.58 }, min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 } max: { x: 57.467973, y: 44.277158 }
@@ -159,7 +159,7 @@
const size = 256 const size = 256
// Fake offset by using the old mapdata. // Fake offset by using the old map projection.
let translation = squared_world.toPixel({ x: 90-10, y: -140 }) let translation = squared_world.toPixel({ x: 90-10, y: -140 })
console.log(translation) console.log(translation)
@@ -167,7 +167,7 @@
//This map is clipped at the bottom //This map is clipped at the bottom
// And also translated in hoizontal direction. // And also translated in hoizontal direction.
// The translate option corrects that. // The translate option corrects that.
let translated_world = new MapData(new Projection.Mercator(), { let translated_world = new MapProjection(new Projection.Mercator(), {
translate: { x: -10, y: 40 }, translate: { x: -10, y: 40 },
}) })
@@ -1,20 +1,20 @@
/** /**
* MapData contains the informations about how * MapProjection contains the informations about how
* a Map has to be interpreted. What are the bounds of the * a Map has to be interpreted. What are the bounds of the
* map and how to translate coordinates into * map and how to translate coordinates into
* image positions. * image positions.
* *
* @class * @class
* @see {@link mapdata.html} * @see {@link mapprojection.html}
*/ */
export class MapData { export class MapProjection {
/** /**
* Creates instance of MapData * Creates instance of MapProjection
* *
* @constructor * @constructor
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection). * @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
* @param {object}[opts] - Addiditonal options. * @param {object}[opts] - Addiditonal options.
* @param {[[minLat, minLng],[maxLat, maxLng]]}[opts.bounds] - Describes the minimum and maximum coordinates on the map * @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.) * @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
*/ */
constructor(projection, opts = {}) { constructor(projection, opts = {}) {
@@ -25,7 +25,7 @@ export class MapData {
opts opts
) )
this.projection = projection this._projection = projection
if (this.opts.clip) { if (this.opts.clip) {
let _cmin = this.projection.forward(this.opts.clip.min) let _cmin = this.projection.forward(this.opts.clip.min)
@@ -49,12 +49,24 @@ export class MapData {
} }
} }
/**
* The projection used by the map projection.
*
* @member {Projection}
* @readonly
* @memberof MapProjection
*/
get projection() {
return this._projection
}
/** /**
* Transforms a pixel point on the map to a geographical coordinate. * Transforms a pixel point on the map to a geographical coordinate.
* *
* @param {{x,y} | PIXI.Point} point - A pixel position on the map. * @public
* @returns {{x,y} | PIXI.Point} - A geographical coordinate. * @param {Point} point - A pixel position on the map.
* @memberof MapData * @returns {CoordinatePoint} A geographical coordinate.
* @memberof MapProjection
*/ */
toCoordinates(point) { toCoordinates(point) {
if (this.opts.clip) { if (this.opts.clip) {
@@ -84,9 +96,10 @@ export class MapData {
/** /**
* Transform a geographical coordinate to a pixel point on the map. * Transform a geographical coordinate to a pixel point on the map.
* *
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}. * @public
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map. * @param {CoordinatePoint} coordinates - A point in the form of {x:lat,y:lng}.
* @memberof MapData * @returns {Point} A pixel position on the map.
* @memberof MapProjection
*/ */
toPixel(coordinates) { toPixel(coordinates) {
let coords = { x: coordinates.x, y: coordinates.y } let coords = { x: coordinates.x, y: coordinates.y }
@@ -116,6 +129,19 @@ export class MapData {
return point return point
} }
/**
* Clipping describes the
* piece of the map that is shown. The returned object contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
*
* E.g. if we just show a map of
* europe, then we have to set the clipping properly, otherwise
* the preojection would produce the wrong results when transforming
* from a point to coordinates or the other way around.
*
* @readonly
* @member {object}
* @memberof MapProjection
*/
get clip() { get clip() {
let unclipped = { let unclipped = {
min: { x: 0, y: 0 }, min: { x: 0, y: 0 },
@@ -126,30 +152,28 @@ export class MapData {
} }
/** /**
* Bounds to pixel transforms some bounds in form of {min:{x:minLat, y:minLng},max:{x:maxLat, y:maxLng}} * Returns the biggest viewport the map projection allows.
* to pixel coordinates. * This is determined by the projecton or the clipping on the mapapp.
* *
* @readonly
* @memberof MapProjection
*/ */
getBoundaries() {
// let min = this.toPixel(bounds.min)
// let max = this.toPixel(bounds.max)
// Y values needs to be swapped, as PIXI has it's origin
// in the top-left corner and a regular map in the bottom-left corner.
let boundaries = {
min: { x: 0, y: 0 },
max: { x: 1, y: 1 }
}
return boundaries
}
get maxViewport() { get maxViewport() {
return this.opts.clip ? this.opts.clip : this.projection.maxViewport return this.opts.clip ? this.opts.clip : this.projection.maxViewport
} }
} }
export class DeepZoomMapData extends MapData { /**
* Special map projection for DeepZoomMap objects.
*
* Note: It just transform the clipping parameter of the tiles config
* to the clipping of the mapapp.
*
* @export
* @class DeepZoomMapProjection
* @extends {MapProjection}
*/
export class DeepZoomMapProjection extends MapProjection {
constructor(projection, tilesConfig, opts = {}) { constructor(projection, tilesConfig, opts = {}) {
if (tilesConfig.clip) { if (tilesConfig.clip) {
opts.clip = { opts.clip = {
+9 -9
View File
@@ -81,11 +81,11 @@
}) })
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, { let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app: app app: app
}) })
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app: app }), { cover: true }) let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app: app }), { cover: true })
app.setMap("deepzoom", deepZoomMap) app.setMap("deepzoom", deepZoomMap)
@@ -127,11 +127,11 @@
const app = new MapApp(opts) const app = new MapApp(opts)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, { let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app: app app: app
}) })
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app: app }), Object.assign({ cover: false }, mapOptipns)) let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app: app }), Object.assign({ cover: false }, mapOptipns))
app.setMap("deepzoom", deepZoomMap) app.setMap("deepzoom", deepZoomMap)
@@ -216,8 +216,8 @@
// Called when all textures are loaded. // Called when all textures are loaded.
let ready = (sprites) => { let ready = (sprites) => {
// Define the mapdata for the map. // Define the map projection for the map.
let europeData = new MapData(new Projection.Mercator(), { let europeData = new MapProjection(new Projection.Mercator(), {
clip: { clip: {
min: { x: 32.863294, y: -18.58 }, min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 } max: { x: 57.467973, y: 44.277158 }
@@ -225,7 +225,7 @@
}) })
// Create the map using the texture and the mapdata. // Create the map using the texture and the map projection.
// Optionally customize the map by supplying secific options. // Optionally customize the map by supplying secific options.
let imageMap = new ImageMap(sprites.get(europe), europeData, { let imageMap = new ImageMap(sprites.get(europe), europeData, {
cover: false cover: false
@@ -270,12 +270,12 @@
}) })
// Create the deepzoomdata. // Create the deepzoomdata.
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), mapConfig.tiles, { let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), mapConfig.tiles, {
app app
}) })
// Create the app. // Create the app.
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, mapConfig.tiles, { app }), { cover: false }) let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, mapConfig.tiles, { app }), { cover: false })
// Set the map in the app. // Set the map in the app.
app.setMap("deepzoom", deepZoomMap) app.setMap("deepzoom", deepZoomMap)
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

+163
View File
@@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en" class="dark-mode">
<head>
<meta charset="UTF-8" />
<title>MapViewport</title>
<script src="../../3rdparty/highlight/highlight.pack.js"></script>
<link rel="stylesheet" href="../../../fonts/material-icon-font/material-icons.css">
<link rel='stylesheet' href='../../3rdparty/highlight/styles/vs2015.css'>
<link rel='stylesheet' href='../../../css/doctest.css'>
<script src="../../../dist/iwmlib.3rdparty.js"></script>
<script src="../../../dist/iwmlib.js"></script>
<script src="../../../dist/iwmlib.pixi.js"></script>
<style>
.controls {
display: flex;
}
</style>
</head>
<body onload="Doctest.run()">
<h1>MapViewport</h1>
<p>
The MapViewport works under the hood of a map layer to track the informations about the current focus point and
zoom position.
This is important to maintain the same view when maps are changed.
</p>
<canvas id="canvas"></canvas>
<div id="mapControl"></div>
<div id="cityControl" class="controls"></div>
<p><strong>WHAT TO SEE:</strong> The map should focus Paris.</p>
<script>
let osmConfig = {
"projection": "mercator",
"type": "deepzoom",
"tiles": {
"tileSize": 256,
"format": "png",
"overlap": 0,
"type": "map",
"height": 1024,
"width": 1024,
"path": "../assets/maps/osm",
"urlTileTemplate": "{path}/{level}/{row}/{column}.{format}"
}
}
let testConfig = {
"projection": "mercator",
"type": "deepzoom",
"tiles": {
"tileSize": 128,
"format": "jpg",
"overlap": 0,
"type": "map",
"height": 4096,
"width": 4096,
"path": "../assets/maps/test",
"urlTileTemplate": "{path}/{level}/{row}/{column}.{format}"
}
}
</script>
<script class="doctest">
let capitals = {
london: { x: 51.5, y: -0.083333 },
rome: { x: 41.9, y: 12.483333 },
madrid: { x: 40.4, y: -3.683333 },
paris: { x: 48.833986, y: 2.346989 }
}
// You may define a focus point ...
let focus = capitals.paris
// ... and a zoom level.
let zoom = 1
// Name has to be app (like all other PIXIApps).
const app = (window.app = new MapApp({
focus,
zoom,
view: canvas,
coordsLogging: true,
width: 512,
height: 512
}))
// As map an image of europe is used.
let europe = '../assets/maps/pixabay/europe.jpg'
//Preload all required sprites for the image map.
app.loadSprites([europe], sprites => ready(sprites), {
resolutionDependent: false
})
// The map projection object contains informations,
// how the displayed map has to be interpreted.
// e.g. which projection is used or how the
// image is clipped.
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
}
})
function ready(sprites) {
const cover = true
// When resources are loaded, the ImageMap can be instantiated.
let imageMap = new ImageMap(sprites.get(europe), europeData, {
coordsLogging: true,
maxScale: 1,
cover
})
let testMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), testConfig.tiles, {
app
})
let testMap = new DeepZoomMap(testMapProjection, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap)
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app
})
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp
app.setMap('europe', imageMap)
// The app requires a map before beeing able to run.
// So start the app here.
app.setup().run()
for (let [key, val] of Object.entries(app.mapList.maps)) {
let mapBtn = document.createElement("button")
mapBtn.innerText = key
mapBtn.addEventListener("click", () => {
app.mapLayer.changeMap(val)
})
mapControl.appendChild(mapBtn)
}
}
for (let [key, val] of Object.entries(capitals)) {
let cityBtn = document.createElement("button")
cityBtn.innerText = key
cityBtn.addEventListener("click", () => {
app.mapLayer.map.moveTo(val)
})
cityControl.appendChild(cityBtn)
}
</script>
</body>
</html>
@@ -1,17 +1,17 @@
import { DeepZoomMap } from './map.js' import { DeepZoomMap } from './map.js'
/** /**
* The MapView class is responsible for a consistent map view. * The MapViewport class is responsible for a consistent map view.
* It is aware of the current viewposition, the scale and viewport. * It is aware of the current viewposition, the scale and viewport.
* It ensures, that maps can be changed, without the user noticing it. * It ensures, that maps can be changed, without the user noticing it.
* *
*/ */
export default class MapView { export default class MapViewport {
/** /**
* *
* @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app. * @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app.
* @param {number} [zoom = 0] - Defines the startup zoom of the app. Note that this is just a request. * @param {number} [zoom = 0] - Defines the startup zoom of the app. Note that this is just a request.
* The MapView will prioritize a full scale app, than displaying the demanded zoom factor * The MapViewport will prioritize a full scale app, than displaying the demanded zoom factor
*/ */
constructor({ focus = null, zoom = null, viewport = { min: { x: -85, y: -180 }, max: { x: 85, y: 180 } } } = {}) { constructor({ focus = null, zoom = null, viewport = { min: { x: -85, y: -180 }, max: { x: 85, y: 180 } } } = {}) {
this.viewport = viewport this.viewport = viewport
@@ -20,32 +20,71 @@ export default class MapView {
this.referenceHeight = 256 this.referenceHeight = 256
} }
/**
* The current focus point as map coordinates.
*
* @member {CoordinatePoint}
* @readonly
* @memberof MapViewport
*/
get focus() { get focus() {
return this._focus return this._focus
} }
/**
* The current zoom distance.
* On DeepZoomMaps this is equal to the zoom level.
*
* @member {number}
* @readonly
* @memberof MapViewport
*/
get zoom() { get zoom() {
return this._zoom return this._zoom
} }
/**
* Applies the current view to a map.
*
*
* @param {GeoMap} map - Map the viewport should be applied to.
* @memberof MapViewport
*/
apply(map) { apply(map) {
map.moveTo(this._focus, this._zoom) map.moveTo(this._focus, this._zoom)
} }
transformed(map) { /**
* Update the focus point and zoom according to the map.
* This is commonly called when the map is transformed.
*
* @param {GeoMap} map - Map to update.
* @memberof MapViewport
*/
update(map) {
this.updateZoom(map) this.updateZoom(map)
this.updateFocusPoint(map) this.updateFocusPoint(map)
} }
updatePosition(map) { /**
this.updateFocusPoint(map) * Updates the focus point.
this.updateZoom(map) * This is automatically called when calling update.
} *
* @param {GeoMap} map
* @memberof MapViewport
*/
updateFocusPoint(map) { updateFocusPoint(map) {
const frame = map.getFrame() const frame = map.getFrame()
this._focus = this.coordinatesFromWindowPoint(map, frame.localCenter) this._focus = this.coordinatesFromWindowPoint(map, frame.localCenter)
} }
/**
* Updates the zoom.
* This is automatically called when calling update.
*
* @param {GeoMap} map
* @memberof MapViewport
*/
updateZoom(map) { updateZoom(map) {
/** /**
* TODO: This relies on the fact, that all maps have the same tileSize, * TODO: This relies on the fact, that all maps have the same tileSize,
@@ -57,6 +96,16 @@ export default class MapView {
} }
} }
/**
* Transforms a position on the map to a position in the window.
*
* Inverse of windowPointToMapPoint.
*
* @param {GeoMap} map - Map to calculate the position on.
* @param {Point} point - Position on the map in pixels.
* @returns {Point} - Pixel position in the window.
* @memberof MapViewport
*/
mapPointToWindowPoint(map, point) { mapPointToWindowPoint(map, point) {
let windowPoint = { x: 0, y: 0 } let windowPoint = { x: 0, y: 0 }
@@ -76,6 +125,16 @@ export default class MapView {
return windowPoint return windowPoint
} }
/**
* Transforms in the window to a position on the map.
*
* Inverse of mapPointToWindowPoint(map, point) {.
*
* @param {GeoMap} map - Map to calculate the position on.
* @param {Point} point -Pixel position in the window.
* @returns {Point} - Position on the map in pixels.
* @memberof MapViewport
*/
windowPointToMapPoint(map, point) { windowPointToMapPoint(map, point) {
let pointOnMap = { x: 0, y: 0 } let pointOnMap = { x: 0, y: 0 }
if (map['image'] && map.image['parent']) { if (map['image'] && map.image['parent']) {
@@ -111,7 +170,7 @@ export default class MapView {
y: position.y / (map.height * map.scatter.scale) y: position.y / (map.height * map.scatter.scale)
} }
let coordinates = map.mapdata.toCoordinates(normalized) let coordinates = map.mapProjection.toCoordinates(normalized)
return coordinates return coordinates
} }
+45 -12
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" class="dark-mode">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@@ -17,6 +17,8 @@
<script src="../../../dist/iwmlib.js"></script> <script src="../../../dist/iwmlib.js"></script>
<script src="../../../dist/iwmlib.pixi.js"></script> <script src="../../../dist/iwmlib.pixi.js"></script>
<link rel="shortcut icon" type="image/x-icon" href="../../../assets/icons/map.png">
<style> <style>
.inline-showcase { .inline-showcase {
@@ -33,6 +35,7 @@
<body onload="Doctest.run()"> <body onload="Doctest.run()">
<h1 class="title">Overlay</h1> <h1 class="title">Overlay</h1>
<!-- <a href="../../../" class="Documentation"></a> -->
<p class="description"> <p class="description">
The overlayclass creates a convenient way to create and design The overlayclass creates a convenient way to create and design
complex map overlays. complex map overlays.
@@ -46,7 +49,7 @@
coordsLogging: true coordsLogging: true
}) })
var osmworld = '../assets/maps/osm/0/0/0.png' var osmworld = '../assets/maps/osm/0/0/0.png'
let worlOSMData = new MapData(new Projection.Mercator()) let worlOSMData = new MapProjection(new Projection.Mercator())
function setupMap(textures) { function setupMap(textures) {
@@ -108,17 +111,22 @@
let exampleOverlayJSON = { let exampleOverlayJSON = {
icon: '../../../assets/icons/place.png', icon: '../../../assets/icons/place.png',
iconColor: '0x000000', iconColor: "0x35aaea",
iconAnchor: { x: 0.5, y: 1 }, iconAnchor: { x: 0.5, y: 1 },
size: 5,
scale: 0.2, scale: 0.2,
color: '0xFF00FF', disabledColor: 0x000000,
fillAlpha: 0, disabledIconColor: 0xCCCCCC,
disabledScale: 0.5,
color: '0x3FA7EE',
items: [ items: [
{ {
name: 'Abidjan', name: 'Custom Icon',
fontWeight: "bold", fontWeight: "bold",
icon: '../../../assets/icons//beenhere.png', icon: '../../../assets/icons/beenhere.png',
iconColor: 0x00ff00, iconColor: 0x00ff00,
iconAlpha: 0.5,
size: 0,
labelVerticalAlignment: "underneath", labelVerticalAlignment: "underneath",
label: 'Abidjan', label: 'Abidjan',
location: { location: {
@@ -126,17 +134,19 @@
y: -4.006472 y: -4.006472
}, },
information: information:
'... der größte städtische Ballungsraum der Elfenbeinküste.' 'Here a custom icon is used. It overrides the icon setting in the global section.'
}, },
{ {
name: 'Berlin', name: 'Berlin',
label: "enabled",
disabledLabel: "disabled",
location: { location: {
x: 52.52543, x: 52.52543,
y: 13.385291 y: 13.385291
}, },
information: information:
'... ist die Bundeshauptstadt der Bundesrepublik Deutschland.' '... ist die Bundeshauptstadt der Bundesrepublik Deutschland.',
enabled: false
}, },
{ {
name: 'Canberra', name: 'Canberra',
@@ -156,7 +166,7 @@
y: 18.416962 y: 18.416962
}, },
information: information:
'... ist eine Hafenstadt an der Südwestküste Südafrikas auf einer Halbinsel, die vom beeindruckenden Tafelberg dominiert wird.' `This item adjusts it's size according to the map.`
}, },
{ {
name: 'Moskau', name: 'Moskau',
@@ -190,7 +200,8 @@
}, },
{ {
name: 'Tokio', name: 'Tokio',
type: "factory",
label: "factory",
location: { location: {
x: 35.696278, x: 35.696278,
y: 139.731366 y: 139.731366
@@ -220,6 +231,7 @@
function texturesLoaded(textures) { function texturesLoaded(textures) {
/** When all textures are loaded .... */ /** When all textures are loaded .... */
setupMap(textures) setupMap(textures)
@@ -231,6 +243,27 @@
let cleaner = null let cleaner = null
const vanishingTime = 1000 const vanishingTime = 1000
// Factories must return a geographics object.
Overlay.createFactory("factory", (item) => {
// This is applied to every item in the overlay that has
// the type factory'
let geographics = new GeoPoint(item.location)
geographics.drawHandler.add((graphics) => {
graphics.beginFill(item.color, item.fillAlpha)
graphics.drawRect(0, 0, 10, 10)
})
let text = new PIXI.Text(item.name, {fontSize: 5})
geographics.graphics.addChild(text)
return geographics
})
/** /**
* *
* The actual PIXI elements are created when the overlay.create() is called. * The actual PIXI elements are created when the overlay.create() is called.
+385 -192
View File
@@ -1,5 +1,5 @@
import { GeoLayer } from './geolayer.js' import { GeoLayer } from './geolayer.js'
import { GeoPoint, GeoMultiShape } from './geographics.js' import { GeoPoint, GeoMultiShape, GeoGraphics } from './geographics.js'
import GeoJson from './geojson.js' import GeoJson from './geojson.js'
import Popup from '../popup.js' import Popup from '../popup.js'
@@ -11,118 +11,216 @@ import Popup from '../popup.js'
* *
* It's highly configurable and adaptable to a variety of scenarios. * It's highly configurable and adaptable to a variety of scenarios.
* *
* @export
* @class Overlay
*
*
* @param {object} options
* @param {object} options.name Name of the item.
* @param {object} options.borderColor
* @param {object} options.borderThickness
* @param {object} options.enabled Determines if enabled, disabled when false.
* @param {object} options.labelScale
* @param {object} options.location
* @param {object} options.label
* @param {object} options.labelType
* @param {object} options.labelLocation
* @param {object} options.labelAlignment
* @param {object} options.labelVerticalAlignment
* @param {object} options.labelSpacing
* @param {object} options.labelTarget
* @param {object} options.geometry
* @param {object} options.information
* @param {object} options.informationPath
* @param {object} options.informationType
* @param {object} options.icon
* @param {object} options.iconAlpha
* @param {object} options.iconAnchor
* @param {object} options.iconColor
* @param {object} options.iconScale
* @param {object} options.iconOffset
* @param {object} options.items
* @param {object} options.color Color of te point.
* @param {object} options.fillAlpha FillAlpha of the point.
* @param {object} options.size Size of the point.
* @param {object} options.scale Determines the scale of the graphics.
* @param {object} options.rescale Rescales the graphics relative to the map, that they keep their original scale. Only works for the entire overlay atm. Therefore must be set in the global param eters and not inside the children.
* @param {object} options.popoverOffset
* @param {object} options.zoomVisibility
* @param {object} options.labelMultiLineAlign
* @param {object} options.labelBreakWords
* @param {object} options.labelDropShadow
* @param {object} options.labelDropShadowAlpha
* @param {object} options.labelDropShadowAngle
* @param {object} options.labelDropShadowBlur
* @param {object} options.labelDropShadowColor
* @param {object} options.labelDropShadowDistance
* @param {object} options.fontColor
* @param {object} options.fontFamily
* @param {object} options.fontSize
* @param {object} options.fontStyle
* @param {object} options.fontWeight
* @param {object} options.labelStroke
* @param {object} options.labelStrokeThickness
* @param {object} options.wordWrap
* @param {object} options.wordWrapWidth
* @param {object} options.labelFill
* @param {object} options.labelFillGradientType
* @param {object} options.labelFillGradientStops
* @param {object} options.fontVariant
* @param {object} options.labelLeading
* @param {object} options.letterSpacing
* @param {object} options.labelLineHeight
* @param {object} options.labelLineJoin
* @param {object} options.labelMiterLimit
* @param {object} options.labelPadding
* @param {object} options.labelTrim
* @param {object} options.textBaseline
* @param {object} options.labelWhiteSpace
*/ */
export default class Overlay { export default class Overlay {
/** constructor(options = {}) {
*Creates an instance of Overlay.
* @param {*} [{
* name = "unnamed", {string}
* connector = false,
* connectorType = "line",
* location = null,
* label = false,
* labelType = null,
* labelLocation = null,
* labelAlignment = "center",
* labelVerticalAlign = "underneath",
* labelTarget = null,
* shape = "geometry", {string} - Defines the shape to be used: geometry, circle, rectangle
* geometry = null,
* geometryType = "point",
* geometryFallback = "circle",
* informationPopup = "infocard",
*
* }={}]
* @memberof Overlay
*/
constructor(opts = {}) {
let defaultTextStyle = new PIXI.TextStyle() let defaultTextStyle = new PIXI.TextStyle()
Object.assign( let defaultOptions = {
this, type: 'auto',
{ name: 'unnamed',
type: 'auto',
name: 'unnamed',
borderColor: 0x000000, borderColor: 0x000000,
borderThickness: 5, borderThickness: 5,
enabled: true,
labelScale: 1,
location: null,
label: false,
labelType: null,
labelLocation: null,
labelAlignment: 'center',
labelVerticalAlignment: 'underneath',
labelSpacing: 10,
labelTarget: null,
geometry: null,
information: '',
informationPath: null,
informationType: 'popup',
icon: null,
iconAlpha: 1,
iconAnchor: { x: 0.5, y: 0.5 },
iconColor: 0xffffff,
iconScale: 1,
iconOffset: { x: 0, y: 0 },
items: [],
color: 0xff00ff,
fillAlpha: 1,
size: 50,
scale: 1,
rescale: false,
popoverOffset: { x: 0, y: 0 },
zoomVisibility: { min: 0, max: Number.MAX_VALUE },
disabledAlpha: 1, /**
disabledColor: 0xaa1111, * The following Attributes are taken from the TextStyle class
disabledBorderColor: 0x000000, * of PIXI. All can be overwritten in the overlay's JSON file.
enabled: true, */
labelScale: 1, labelMultiLineAlign: defaultTextStyle.align,
location: null, labelBreakWords: defaultTextStyle.breakWords,
label: false, labelDropShadow: defaultTextStyle.dropShadow,
labelType: null, labelDropShadowAlpha: defaultTextStyle.dropShadowAlpha,
labelLocation: null, labelDropShadowAngle: defaultTextStyle.dropShadowAngle,
labelAlignment: 'center', labelDropShadowBlur: defaultTextStyle.dropShadowBlur,
labelVerticalAlign: 'underneath', labelDropShadowColor: defaultTextStyle.dropShadowColor,
labelSpacing: 10, labelDropShadowDistance: defaultTextStyle.dropShadowDistance,
labelTarget: null, fontColor: 0x000000,
geometry: null, fontFamily: defaultTextStyle.fontFamily,
information: '', fontSize: defaultTextStyle.fontSize,
informationPath: null, fontStyle: defaultTextStyle.fontStyle,
informationType: 'popup', fontWeight: defaultTextStyle.fontWeight,
icon: null, labelStroke: defaultTextStyle.stroke,
iconAlpha: 1, labelStrokeThickness: defaultTextStyle.strokeThickness,
iconAnchor: { x: 0.5, y: 0.5 }, wordWrap: defaultTextStyle.wordWrap,
iconColor: 0xffffff, wordWrapWidth: defaultTextStyle.wordWrapWidth,
iconScale: 1, labelFill: defaultTextStyle.fill,
iconOffset: { x: 0, y: 0 }, labelFillGradientType: defaultTextStyle.fillGradientType,
items: [], labelFillGradientStops: defaultTextStyle.fillGradientStops,
color: 0xff00ff, fontVariant: defaultTextStyle.fontVariant,
fillAlpha: 1, labelLeading: defaultTextStyle.leading,
size: 50, letterSpacing: defaultTextStyle.letterSpacing,
scale: 1, labelLineHeight: defaultTextStyle.lineHeight,
rescale: false, labelLineJoin: defaultTextStyle.lineJoin,
popoverOffset: { x: 0, y: 0 }, labelMiterLimit: defaultTextStyle.miterLimit,
zoomVisibility: { min: 0, max: Number.MAX_VALUE }, labelPadding: defaultTextStyle.padding,
labelTrim: defaultTextStyle.trim,
textBaseline: defaultTextStyle.textBaseline,
labelWhiteSpace: defaultTextStyle.whiteSpace
}
/** defaultOptions = this.addDisabled(defaultOptions)
* The following Attributes are taken from the TextStyle class
* of PIXI. All can be overwritten in the overlay's JSON file. console.log('DISABLED VERTICALA.', defaultOptions.disabledLabelVerticalAlignment)
*/
labelMultiLineAlign: defaultTextStyle.align, options = this.addDisabled(options)
labelBreakWords: defaultTextStyle.breakWords,
labelDropShadow: defaultTextStyle.dropShadow, options = Object.assign({}, defaultOptions, options)
labelDropShadowAlpha: defaultTextStyle.dropShadowAlpha,
labelDropShadowAngle: defaultTextStyle.dropShadowAngle, Object.assign(this, options)
labelDropShadowBlur: defaultTextStyle.dropShadowBlur,
labelDropShadowColor: defaultTextStyle.dropShadowColor,
labelDropShadowDistance: defaultTextStyle.dropShadowDistance,
fontColor: 0x000000,
fontFamily: defaultTextStyle.fontFamily,
fontSize: defaultTextStyle.fontSize,
fontStyle: defaultTextStyle.fontStyle,
fontWeight: defaultTextStyle.fontWeight,
labelStroke: defaultTextStyle.stroke,
labelStrokeThickness: defaultTextStyle.strokeThickness,
wordWrap: defaultTextStyle.wordWrap,
wordWrapWidth: defaultTextStyle.wordWrapWidth,
labelFill: defaultTextStyle.fill,
labelFillGradientType: defaultTextStyle.fillGradientType,
labelFillGradientStops: defaultTextStyle.fillGradientStops,
fontVariant: defaultTextStyle.fontVariant,
labelLeading: defaultTextStyle.leading,
letterSpacing: defaultTextStyle.letterSpacing,
labelLineHeight: defaultTextStyle.lineHeight,
labelLineJoin: defaultTextStyle.lineJoin,
labelMiterLimit: defaultTextStyle.miterLimit,
labelPadding: defaultTextStyle.padding,
labelTrim: defaultTextStyle.trim,
textBaseline: defaultTextStyle.textBaseline,
labelWhiteSpace: defaultTextStyle.whiteSpace
},
opts
)
} }
/**
* Some parameters shall not have a disabled option.
* These are defined here.
*
* @readonly
* @memberof Overlay
*/
get excludedDisableParameters() {
return ['items', 'rescale', 'name', 'type', 'enabled']
}
/**
* Copies the normal properties to the disabled version of the property,
* while conserving already set disabled properties.
*
* @param {*} options
* @returns {object} - Returns the modified options object.
* @memberof Overlay
*/
addDisabled(options) {
for (let [key, value] of Object.entries(options)) {
if (this.excludedDisableParameters.indexOf(key) == -1) {
let disabledProperty = this.toDisabledPropertyString(key)
if (options[disabledProperty] == undefined) {
options[disabledProperty] = value
}
}
}
return options
}
/**
* Textures need to be loaded by the app. Texture parameters return all parameters that need to be evaluated for textures.
* @readonly
* @member {array}
* @memberof Overlay
*/
get textureParameters() { get textureParameters() {
return ['icon'] const textureParameters = ['icon']
textureParameters.forEach(textureParameter => {
textureParameters.push(this.toDisabledPropertyString(textureParameter))
})
return textureParameters
} }
/**
* The required textures are saved inside the single items.
* Their texture string gets replaced by a texture object
* containing the path and the texture.
*
* @param {*} parameter
* @param {*} key
* @param {*} path
* @memberof Overlay
*/
setTexture(parameter, key, path) { setTexture(parameter, key, path) {
let obj = (this[parameter] = {}) let obj = (this[parameter] = {})
obj._key = key obj._key = key
@@ -150,6 +248,14 @@ export default class Overlay {
}) })
} }
/**
* Recursively calls a function on child items.
*
* @param {*} parameter
* @param {*} func
* @param {*} [obj=null]
* @memberof Overlay
*/
apply(parameter, func, obj = null) { apply(parameter, func, obj = null) {
if (obj == null) return if (obj == null) return
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
@@ -187,13 +293,21 @@ export default class Overlay {
adjustItems = null, adjustItems = null,
cleanupItems = null cleanupItems = null
} = {}) { } = {}) {
const name = this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay' console.log(this)
const name = this.name
? this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay'
: 'Unnamed Overlay'
let geoLayer = new GeoLayer(new PIXI.Container(), { name }) let geoLayer = new GeoLayer(new PIXI.Container(), { name })
geoLayer.visibility = this.zoomVisibility geoLayer.visibility = this.zoomVisibility
if (this.rescale) geoLayer.rescale = this.rescale if (this.rescale) geoLayer.rescale = this.rescale
this.items.forEach(item => { this.items.forEach(item => {
if (!excludeItems(item)) { if (!excludeItems(item)) {
//Copies all values to a disabled state.
item = this.addDisabled(item)
if (adjustItems) { if (adjustItems) {
adjustItems(item) adjustItems(item)
} }
@@ -208,11 +322,60 @@ export default class Overlay {
}) })
return geoLayer return geoLayer
} }
/**
* Used to pick a property or disabled property using an enabled parameter.
*
* @param {*} item
* @param {*} property
* @param {boolean} [enabled=true]
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
pickItemProperty(item, property, enabled = true) {
return enabled ? this.getItemProperty(item, property) : this.getDisabledItemProperty(item, property)
}
toDisabledPropertyString(propertyName) {
const prefix = 'disabled'
return prefix + propertyName[0].toUpperCase() + propertyName.slice(1)
}
/**
* Get's the disabled version of the property as long as it is not excluded.
*
* @param {*} item
* @param {*} property
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
getDisabledItemProperty(item, property) {
if (this.excludedDisableParameters.indexOf(property) === -1) {
property = this.toDisabledPropertyString(property)
}
const propertyValue = this.getItemProperty(item, property)
return propertyValue
}
/**
* Tries to get the infromation from an overlay item.
* If the item does not contain the property, the overlay definition will be used.
* When the overlay definition is not set, the default value is used.
*
* @param {OverlayItem} item - Item to get the informations from.
* @param {string} property - Name of the property.
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
getItemProperty(item, property) { getItemProperty(item, property) {
let propertyValue = null let propertyValue = null
const propertyExistsOnItem = item[property] !== undefined const propertyExistsOnItem = item[property] !== undefined
const propertyExistsOnOverlay = this[property] !== undefined const propertyExistsOnOverlay = this[property] !== undefined
if (property == 'disabledLabelVerticalAlignment') {
console.log(this)
console.log(this['disabledLabelVerticalAlignment'], propertyExistsOnItem, propertyExistsOnOverlay)
}
if (propertyExistsOnItem) propertyValue = item[property] if (propertyExistsOnItem) propertyValue = item[property]
else if (propertyExistsOnOverlay) propertyValue = this[property] else if (propertyExistsOnOverlay) propertyValue = this[property]
else { else {
@@ -229,6 +392,15 @@ export default class Overlay {
else return false else return false
} }
/**
* An overlay can have a reference to another file.
* That single overlays are more easy to maintain.
*
* @param {*} item
* @param {*} property
* @returns {any} - Returns the reference as object or the property.
* @memberof Overlay
*/
_resolveReference(item, property) { _resolveReference(item, property) {
if (this._isReference(property)) { if (this._isReference(property)) {
let referencedProperty = property['@property'] let referencedProperty = property['@property']
@@ -281,18 +453,30 @@ export default class Overlay {
}) })
} }
/**
* Creates a geographic for each item.
* By default it uses the regular routine but you may
* specify a factory for the defined type.
*
* @param {*} item
* @param {*} [informationCallback=null]
* @returns {GeoGraphics} - Returns the created geographics.
* @memberof Overlay
*/
createItem(item, informationCallback = null) { createItem(item, informationCallback = null) {
let geographics let geographics
let type = this.getItemProperty(item, 'type')
/** /**
* Use a factory to draw the items, if a type is specified. * Use a factory to draw the items, if a type is specified.
*/ */
if (this.type != 'auto') { if (type != 'auto') {
let geographicsFactory = Overlay.requestFactory(this.type) let geographicsFactory = Overlay.requestFactory(type)
if (!geographicsFactory) if (!geographicsFactory)
console.error( console.error(
'Invalid Overlay Mode: ' + 'Invalid Overlay Mode: ' +
this.type + type +
'. Fallback to auto mode. Overlaymodes must be registeres beforehand. Valid modes are: ' + '. Fallback to auto mode. Overlaymodes must be registeres beforehand. Valid modes are: ' +
Overlay.listFactories().join(',') + Overlay.listFactories().join(',') +
'.' '.'
@@ -318,84 +502,100 @@ export default class Overlay {
this._drawRoutine(geographics, item, informationCallback) this._drawRoutine(geographics, item, informationCallback)
} }
this._drawLabel(item, geographics) let label = this._createLabel(item, geographics)
if (label != null && geographics != null) {
console.log(geographics)
geographics.graphics.addChild(label)
}
} }
return geographics return geographics
} }
_drawLabel(item, geographics) { /**
let label = this.getItemProperty(item, 'label') *
if (label) { * Draws a label that is shown next to the item on the map.
let textStyle = this._gatherFontStyle(item) *
let text = new PIXI.Text(label, textStyle) * @param {*} item
* @returns {PIXI.Text} - Returns the created label or null.
* @memberof Overlay
*/
_createLabel(item) {
let enabled = this.getItemProperty(item, 'enabled')
let labelText = this.pickItemProperty(item, 'label', enabled)
let label = null
let labelScale = this.getItemProperty(item, 'labelScale') if (labelText) {
text.scale.set(labelScale, labelScale) let textStyle = this._gatherFontStyle(item)
label = new PIXI.Text(labelText, textStyle)
let labelScale = this.pickItemProperty(item, 'labelScale', enabled)
label.scale.set(labelScale, labelScale)
let position = new PIXI.Point() let position = new PIXI.Point()
let align = this.getItemProperty(item, 'labelAlignment') let align = this.pickItemProperty(item, 'labelAlignment', enabled)
if (align == 'left'); if (align == 'left');
else if (align == 'center') position.set(text.position.x - text.width / 2, text.position.y) else if (align == 'center') position.set(label.position.x - label.width / 2, label.position.y)
else if (align == 'right') position.set(text.position.x - text.width, text.position.y) else if (align == 'right') position.set(label.position.x - label.width, label.position.y)
else this._logPropertyNotImplemented('labelAlignment', align) else this._logPropertyNotImplemented('labelAlignment', align)
let verticalAlign = this.getItemProperty(item, 'labelVerticalAlignment') let verticalAlign = this.pickItemProperty(item, 'labelVerticalAlignment', enabled)
if (verticalAlign == 'underneath') { if (verticalAlign == 'underneath') {
let size = this.getItemProperty(item, 'size') let size = this.pickItemProperty(item, 'size', enabled)
let scale = this.getItemProperty(item, 'scale') let scale = this.pickItemProperty(item, 'scale', enabled)
let labelSpacing = this.getItemProperty(item, 'labelSpacing') let labelSpacing = this.pickItemProperty(item, 'labelSpacing', enabled)
position.y += size * scale + labelSpacing position.y += size * scale + labelSpacing
} else if (verticalAlign == 'above') { } else if (verticalAlign == 'above') {
let size = this.getItemProperty(item, 'size') let size = this.pickItemProperty(item, 'size', enabled)
let scale = this.getItemProperty(item, 'scale') let scale = this.pickItemProperty(item, 'scale', enabled)
let labelSpacing = this.getItemProperty(item, 'labelSpacing') let labelSpacing = this.pickItemProperty(item, 'labelSpacing', enabled)
position.y -= size * scale + text.height + labelSpacing position.y -= size * scale + label.height + labelSpacing
} else this._logPropertyNotImplemented('labelVerticalAlignment', verticalAlign) } else this._logPropertyNotImplemented('labelVerticalAlignment', verticalAlign)
text.position.set(position.x, position.y) label.position.set(position.x, position.y)
geographics.graphics.addChild(text)
} }
return label
} }
_gatherFontStyle(item) { _gatherFontStyle(item) {
const enabled = this.getItemProperty(item, 'enabled')
return { return {
align: this.getItemProperty(item, 'labelMultiLineAlign'), align: this.pickItemProperty(item, 'labelMultiLineAlign', enabled),
breakWords: this.getItemProperty(item, 'labelBreakWords'), breakWords: this.pickItemProperty(item, 'labelBreakWords', enabled),
dropShadow: this.getItemProperty(item, 'labelDropShadow'), dropShadow: this.pickItemProperty(item, 'labelDropShadow', enabled),
dropShadowAlpha: this.getItemProperty(item, 'labelDropShadowAlpha'), dropShadowAlpha: this.pickItemProperty(item, 'labelDropShadowAlpha', enabled),
dropShadowAngle: this.getItemProperty(item, 'labelDropShadowAngle'), dropShadowAngle: this.pickItemProperty(item, 'labelDropShadowAngle', enabled),
dropShadowBlur: this.getItemProperty(item, 'labelDropShadowBlur'), dropShadowBlur: this.pickItemProperty(item, 'labelDropShadowBlur', enabled),
dropShadowColor: this.getItemProperty(item, 'labelDropShadowColor'), dropShadowColor: this.pickItemProperty(item, 'labelDropShadowColor', enabled),
dropShadowDistance: this.getItemProperty(item, 'labelDropShadowDistance'), dropShadowDistance: this.pickItemProperty(item, 'labelDropShadowDistance', enabled),
fontFamily: this.getItemProperty(item, 'fontFamily'), fontFamily: this.pickItemProperty(item, 'fontFamily', enabled),
fontSize: this.getItemProperty(item, 'fontSize'), fontSize: this.pickItemProperty(item, 'fontSize', enabled),
fontStyle: this.getItemProperty(item, 'fontStyle'), fontStyle: this.pickItemProperty(item, 'fontStyle', enabled),
fontWeight: this.getItemProperty(item, 'fontWeight'), fontWeight: this.pickItemProperty(item, 'fontWeight', enabled),
stroke: this.getItemProperty(item, 'labelStroke'), stroke: this.pickItemProperty(item, 'labelStroke', enabled),
strokeThickness: this.getItemProperty(item, 'labelStrokeThickness'), strokeThickness: this.pickItemProperty(item, 'labelStrokeThickness', enabled),
wordWrap: this.getItemProperty(item, 'wordWrap'), wordWrap: this.pickItemProperty(item, 'wordWrap', enabled),
wordWrapWidth: this.getItemProperty(item, 'wordWrapWidth'), wordWrapWidth: this.pickItemProperty(item, 'wordWrapWidth', enabled),
fill: this.pickItemProperty(item, 'labelFill', enabled),
fill: this.getItemProperty(item, 'labelFill'), fillGradientType: this.pickItemProperty(item, 'labelFillGradientType', enabled),
fillGradientType: this.getItemProperty(item, 'labelFillGradientType'), fillGradientStops: this.pickItemProperty(item, 'labelFillGradientStops', enabled),
fillGradientStops: this.getItemProperty(item, 'labelFillGradientStops'), fontVariant: this.pickItemProperty(item, 'fontVariant', enabled),
fontVariant: this.getItemProperty(item, 'fontVariant'), leading: this.pickItemProperty(item, 'labelLeading', enabled),
leading: this.getItemProperty(item, 'labelLeading'), letterSpacing: this.pickItemProperty(item, 'letterSpacing', enabled),
letterSpacing: this.getItemProperty(item, 'letterSpacing'), lineHeight: this.pickItemProperty(item, 'labelLineHeight', enabled),
lineHeight: this.getItemProperty(item, 'labelLineHeight'), lineJoin: this.pickItemProperty(item, 'labelLineJoin', enabled),
lineJoin: this.getItemProperty(item, 'labelLineJoin'), miterLimit: this.pickItemProperty(item, 'labelMiterLimit', enabled),
miterLimit: this.getItemProperty(item, 'labelMiterLimit'), padding: this.pickItemProperty(item, 'labelPadding', enabled),
padding: this.getItemProperty(item, 'labelPadding'), trim: this.pickItemProperty(item, 'labelTrim', enabled),
trim: this.getItemProperty(item, 'labelTrim'), textBaseline: this.pickItemProperty(item, 'textBaseline', enabled),
textBaseline: this.getItemProperty(item, 'textBaseline'), whiteSpace: this.pickItemProperty(item, 'labelWhiteSpace', enabled)
whiteSpace: this.getItemProperty(item, 'labelWhiteSpace')
} }
} }
@@ -440,25 +640,27 @@ export default class Overlay {
} }
_createIcon(geographics, item) { _createIcon(geographics, item) {
let icon = this.getItemProperty(item, 'icon') let enabled = this.getItemProperty(item, 'enabled')
let icon = this.pickItemProperty(item, 'icon', enabled)
if (icon) { if (icon) {
if (icon.texture) { if (icon.texture) {
let sprite = new PIXI.Sprite(icon.texture) let sprite = new PIXI.Sprite(icon.texture)
const iconAnchor = this.getItemProperty(item, 'iconAnchor') const iconAnchor = this.pickItemProperty(item, 'iconAnchor', enabled)
sprite.anchor.set(iconAnchor.x, iconAnchor.y) sprite.anchor.set(iconAnchor.x, iconAnchor.y)
const iconScale = this.getItemProperty(item, 'iconScale') const iconScale = this.pickItemProperty(item, 'iconScale', enabled)
if (iconScale) sprite.scale.set(iconScale, iconScale) if (iconScale) sprite.scale.set(iconScale, iconScale)
const iconOffset = this.getItemProperty(item, 'iconOffset') const iconOffset = this.pickItemProperty(item, 'iconOffset', enabled)
if (iconOffset && iconOffset.x != null && iconOffset.y != null) if (iconOffset && iconOffset.x != null && iconOffset.y != null)
sprite.position.set(iconOffset.x, iconOffset.y) sprite.position.set(iconOffset.x, iconOffset.y)
const iconColor = this.getItemProperty(item, 'iconColor') const iconColor = this.pickItemProperty(item, 'iconColor', enabled)
if (iconColor) sprite.tint = iconColor if (iconColor) sprite.tint = iconColor
const iconAlpha = this.getItemProperty(item, 'iconAlpha') const iconAlpha = this.pickItemProperty(item, 'iconAlpha', enabled)
if (iconAlpha) sprite.alpha = iconAlpha if (iconAlpha) sprite.alpha = iconAlpha
geographics.graphics.addChild(sprite) geographics.graphics.addChild(sprite)
@@ -468,28 +670,6 @@ export default class Overlay {
_createInformation(geographics, item, callback = null) { _createInformation(geographics, item, callback = null) {
if (item.information) { if (item.information) {
/**
* SO: The overlay class is quite convenient.
* But managing the information creation solely inside this
* class restricts us massively.
*
* Maybe a restructuring would be good, that we can handle
* the creation of information with a callback and can adapt
* to any occuring situation.
*
* e.g.
*
* overlay.informationHandler((geo, type, item)=>{
* switch(type){
* case "popup":
* createPopup(item.information)
* break;
*
* ....
* }
* })
*/
geographics.graphics.interactive = true geographics.graphics.interactive = true
const informationType = this.getItemProperty(item, 'informationType') const informationType = this.getItemProperty(item, 'informationType')
@@ -538,17 +718,21 @@ export default class Overlay {
_fill(geographics, item) { _fill(geographics, item) {
const enabled = this.getItemProperty(item, 'enabled') const enabled = this.getItemProperty(item, 'enabled')
const color = enabled ? this.getItemProperty(item, 'color') : this.getItemProperty(item, 'disabledColor') const color = this.pickItemProperty(item, 'color', enabled)
let alpha = enabled ? this.getItemProperty(item, 'fillAlpha') : this.getItemProperty(item, 'disabledAlpha') const alpha = this.pickItemProperty(item, 'fillAlpha', enabled)
geographics.graphics.beginFill(color, alpha) geographics.graphics.beginFill(color, alpha)
} }
_drawPoint(item, informationCallback = null) { _drawPoint(item, informationCallback = null) {
const overlay = this const overlay = this
const enabled = this.getItemProperty(item, 'enabled')
let that = this
let geographic = new GeoPoint(item.location, { let geographic = new GeoPoint(item.location, {
onDraw: function() { onDraw: function() {
overlay._fill.call(overlay, this, item) overlay._fill.call(overlay, this, item)
const size = overlay.getItemProperty(item, 'size') ? overlay.getItemProperty(item, 'size') : 0 const size = that.pickItemProperty(item, 'size', enabled)
this.graphics.drawCircle(0, 0, size) this.graphics.drawCircle(0, 0, size)
overlay._drawRoutine.call(overlay, this, item, informationCallback) overlay._drawRoutine.call(overlay, this, item, informationCallback)
} }
@@ -563,10 +747,19 @@ export default class Overlay {
Overlay.Modes = { auto: null } Overlay.Modes = { auto: null }
Overlay.createFactory = function(name, geographicsFactory) { /**
if (Overlay.Modes.hasOwnProperty(name)) * Creates a factory for a specific type.
console.warn(`The mode ${name} was already implemented and was overwritten!`) *
Overlay.Modes[name] = geographicsFactory * @param {string} type - Name of the factory. When overlay items match the type, the factory is applied.
* @param {function} geographicsFactory - A function that is called with every item. Can be used to draw the geographics individually.
*
* @static
* @memberof {Overlay}
*/
Overlay.createFactory = function(type, geographicsFactory) {
if (Overlay.Modes.hasOwnProperty(type))
console.warn(`The mode ${type} was already implemented and was overwritten!`)
Overlay.Modes[type] = geographicsFactory
} }
Overlay.requestFactory = function(name) { Overlay.requestFactory = function(name) {
+1 -1
View File
@@ -77,7 +77,7 @@
<body onload="Doctest.run()"> <body onload="Doctest.run()">
<h1>Projections</h1> <h1>Projections</h1>
<p> <p>
Projections are used on the mapdata to translate coordinates to pixelpositions. There are various Projections are used on the map projection to translate coordinates to pixelpositions. There are various
projections that can be used. All implemented ones are showcased here. projections that can be used. All implemented ones are showcased here.
</p> </p>
+25
View File
@@ -258,6 +258,31 @@ export class Vector {
} }
} }
export class PIXIUtils {
/*
* Transform a pixi text to it's actual screensize,
* ignoring it's local transforms
*/
static toScreenFontSize(pixiText, fontSize = null) {
pixiText._recursivePostUpdateTransform();
let normalizedScale = {
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
y: pixiText.scale.x / pixiText.transform.worldTransform.d
};
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y };
if (fontSize) pixiText.style.fontSize = fontSize;
}
static saveFill(graphics) {
return {
fill: graphics.fill.color,
alpha: graphics.fill.alpha
}
}
}
/** /**
* The EventHandler class is used to take care of a event based design * The EventHandler class is used to take care of a event based design
* pattern. Callbacks can subscribe to an event and these unknown sources * pattern. Callbacks can subscribe to an event and these unknown sources
+24 -3
View File
@@ -18,7 +18,7 @@ export class InteractivePopup extends AbstractPopup {
* @constructor * @constructor
* @param {object} [opts] - An options object to specify to style and behaviour of the popup. * @param {object} [opts] - An options object to specify to style and behaviour of the popup.
* @param {boolean} [opts.closeOnPopup=false] - Should the popup be closed when the user clicks on the popup? * @param {boolean} [opts.closeOnPopup=false] - Should the popup be closed when the user clicks on the popup?
* @param {boolean} [opts.closeButton=true] - Should a close button be displayed on the upper right corner? * @param {boolean|string} [opts.closeButton=true] - Should a close button be displayed on the upper right corner? Alternatively, a URL to the image can be specified.
* @param {object} [opts.button] - A Button object to be display on the lower right corner. * @param {object} [opts.button] - A Button object to be display on the lower right corner.
* @param {object} [opts.buttonGroup] - A ButtonGroup object to be displayed on the lower right corner. * @param {object} [opts.buttonGroup] - A ButtonGroup object to be displayed on the lower right corner.
*/ */
@@ -73,7 +73,16 @@ export class InteractivePopup extends AbstractPopup {
// closeButton // closeButton
//----------------- //-----------------
if (this.opts.closeButton) { if (this.opts.closeButton) {
let closeButton = PIXI.Sprite.from('../../assets/icons/close.png') let url = null
if (typeof this.opts.closeButton === 'string' || this.opts.closeButton instanceof String) {
url = InteractivePopup.iconIsUrl(this.opts.closeButton)
? this.opts.closeButton
: `../../assets/icons/${this.opts.closeButton}.png`
} else {
url = '../../assets/icons/close.png'
}
let closeButton = PIXI.Sprite.from(url)
closeButton.width = this.headerStyle.fontSize closeButton.width = this.headerStyle.fontSize
closeButton.height = closeButton.width closeButton.height = closeButton.width
closeButton.tint = this.theme.color2 closeButton.tint = this.theme.color2
@@ -172,6 +181,18 @@ export class InteractivePopup extends AbstractPopup {
return size return size
} }
/**
* Tests if an icon string is an url.
*
* @private
* @static
* @param {string} url - The url to test.
* @return {boolean} true if the url is an url to an image.
*/
static iconIsUrl(url) {
return /\.(png|svg|gif|jpg|jpeg|tif|tiff)$/i.test(url)
}
} }
/** /**
@@ -197,7 +218,7 @@ export default class Popup extends InteractivePopup {
* *
* @constructor * @constructor
* @param {object} [opts] - An options object to specify to style and behaviour of the popup. * @param {object} [opts] - An options object to specify to style and behaviour of the popup.
* @param {boolean} [opts.closeButton=false] - Should a close button be displayed on the upper right corner? * @param {boolean|string} [opts.closeButton=false] - Should a close button be displayed on the upper right corner? Alternatively, a URL to the image can be specified.
* @param {number} [opts.minWidth=0] - The minimum width of the popup. * @param {number} [opts.minWidth=0] - The minimum width of the popup.
* @param {number} [opts.minHeight=0] - The minimum height of the popup. * @param {number} [opts.minHeight=0] - The minimum height of the popup.
*/ */
+102
View File
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Resolution Doctest</title>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mousetrap/1.6.5/mousetrap.min.js"></script>
<style>
body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script class="doctest">
const app = new PIXIApp({
view: canvas,
fpsLogging: true,
transparent: false
})
app.setup()
app.run()
const textProps = {fontSize: 72, fill: 0xffffff}
Mousetrap.bind(['1', '2', '3', '4', '5'], (event, key) => {
app.stage.removeChildren()
let sprite = null
let label = null
if (key === '1') {
sprite = PIXI.Sprite.from('resolution/image-4k.jpg')
label = `4096 * 2732 ~ 4K`
} else if (key === '2') {
sprite = PIXI.Sprite.from('resolution/image-8k.jpg')
label = `7952 * 5304 ~ 8K`
} else if (key === '3') {
const texture = PIXI.Texture.from('resolution/video-1k.mp4')
texture.baseTexture.resource.source.loop = true
sprite = new PIXI.Sprite(texture)
label = `1920 * 1080 = 1K`
} else if (key === '4') {
const texture = PIXI.Texture.from('resolution/video-4k.mp4')
texture.baseTexture.resource.source.loop = true
sprite = new PIXI.Sprite(texture)
label = `3840 * 2160 = 4K`
} else if (key === '5') {
const texture = PIXI.Texture.from('resolution/video-8k.mp4')
texture.baseTexture.resource.source.loop = true
sprite = new PIXI.Sprite(texture)
label = `7680 * 4320 ~ 8K`
}
app.stage.addChild(sprite)
// texts
//--------------------
const shortcuts = new PIXI.Text(`Press 1 - 5 to change Sprites`, textProps)
shortcuts.x = 100
shortcuts.y = 100
const width = new PIXI.Text(`Width: ${app.renderer.width}`, textProps)
width.x = 100
width.y = shortcuts.y + shortcuts.height * 2
const height = new PIXI.Text(`Height: ${app.renderer.height}`, textProps)
height.x = 100
height.y = width.y + width.height + 10
const resolution = new PIXI.Text(`Resolution: ${app.renderer.resolution}`, textProps)
resolution.x = 100
resolution.y = height.y + height.height + 10
const devicePixelRatio = new PIXI.Text(`devicePixelRatio: ${window.devicePixelRatio}`, textProps)
devicePixelRatio.x = 100
devicePixelRatio.y = resolution.y + resolution.height + 10
const text = new PIXI.Text(label, textProps)
text.x = 100
text.y = devicePixelRatio.y + devicePixelRatio.height * 2
app.stage.addChild(shortcuts, width, height, resolution, devicePixelRatio, text)
})
Mousetrap.trigger('1')
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 MiB

+133
View File
@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Scatter Resolution Doctest</title>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
</head>
<body>
<canvas id="canvas" />
<script>
const app = new PIXIApp({
view: canvas,
roundPixels: true,
resolution: 2
})
.setup()
.run()
app.loadSprites(
[
'../examples/front__1_dpi75.png',
'../examples/front__1_dpi150.png',
'../examples/front__1_dpi300.png',
'../examples/front__1_dpi600.png',
'../examples/front__1_dpi1200.png'
],
sprites => {
app.scatterContainerFront = new ScatterContainer(app.renderer, { app })
app.scatterContainerBack = new ScatterContainer(app.renderer, { app })
app.scene.addChild(app.scatterContainerBack)
app.scene.addChild(app.scatterContainerFront)
let startScale = 0.5
// sprite1
//--------------------
let sprite1 = sprites.get('../examples/front__1_dpi75.png')
sprite1.interactive = true
let scatter1 = new DisplayObjectScatter(sprite1, app.renderer, {
x: 20,
y: 20,
startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerBack.addChild(sprite1)
// sprite2
//--------------------
let sprite2 = sprites.get('../examples/front__1_dpi150.png')
sprite2.interactive = true
let scatter2 = new DisplayObjectScatter(sprite2, app.renderer, {
x: 320,
y: 20,
startScale: 0.5 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite2)
// sprite3
//--------------------
let sprite3 = sprites.get('../examples/front__1_dpi300.png')
sprite3.interactive = true
let scatter3 = new DisplayObjectScatter(sprite3, app.renderer, {
x: 620,
y: 20,
startScale: 0.25 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerBack.addChild(sprite3)
// sprite4
//--------------------
let sprite4 = sprites.get('../examples/front__1_dpi600.png')
sprite4.interactive = true
let scatter4 = new DisplayObjectScatter(sprite4, app.renderer, {
x: 920,
y: 20,
startScale: 0.125 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite4)
// sprite5
//--------------------
let sprite5 = sprites.get('../examples/front__1_dpi1200.png')
sprite5.interactive = true
let scatter5 = new DisplayObjectScatter(sprite5, app.renderer, {
x: 1220,
y: 20,
startScale: 0.0625 * startScale,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite5)
// renderTexture
//--------------------
sprites.forEach(value => {
const matrix = new PIXI.Matrix()
matrix.translate(-value.x, -value.y)
const texture = PIXI.RenderTexture.create({ width: value.width, height: value.height, resolution: 2 })
app.renderer.render(value, texture, true, matrix)
const sprite = new PIXI.Sprite(texture)
sprite.interactive = true
new DisplayObjectScatter(sprite, app.renderer, {
x: value.x,
y: 400,
minScale: 0.01,
maxScale: 5.0
})
app.scatterContainerFront.addChild(sprite)
})
},
{ resolutionDependent: false }
)
</script>
</body>
</html>
+90
View File
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>PIXI Scatter Resolution Doctest</title>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.pixi.js"></script>
</head>
<body>
<canvas id="canvas2" class="interactive">Canvas not supported</canvas>
<script>
class ScatterApp extends PIXIApp {
setup() {
super.setup()
// Obey order in which ScatterContainer are created because the
// InteractionMapper register event handlers in a first come first serve
// order
this.scatterContainerFront = new ScatterContainer(this.renderer, { app: this })
this.scatterContainerBack = new ScatterContainer(this.renderer, { app: this })
// Note that the addChild order is different because later children
// are placed in front of earlier children.
this.scene.addChild(this.scatterContainerBack)
this.scene.addChild(this.scatterContainerFront)
// Add the queen to ScatterContainer one
let sprite1 = PIXI.Sprite.from('../examples/test-1280-720.png')
sprite1.interactive = true
let scatter1 = new DisplayObjectScatter(sprite1, this.renderer, {
x: 20,
y: 40,
startScale: 0.5,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerBack.addChild(sprite1)
// Add the king to ScatterContainer two
let sprite2 = PIXI.Sprite.from('../examples/test-1280-720.png')
sprite2.interactive = true
let scatter2 = new DisplayObjectScatter(sprite2, this.renderer, {
x: 280,
y: 40,
startScale: 1,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerFront.addChild(sprite2)
// Add the queen to ScatterContainer one
let sprite3 = PIXI.Sprite.from('../examples/test-800-450.png')
sprite3.interactive = true
let scatter3 = new DisplayObjectScatter(sprite3, this.renderer, {
x: 140,
y: 400,
startScale: 0.5,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerBack.addChild(sprite3)
// Add the king to ScatterContainer two
let sprite4 = PIXI.Sprite.from('../examples/test-800-450.png')
sprite4.interactive = true
let scatter4 = new DisplayObjectScatter(sprite4, this.renderer, {
x: 280,
y: 400,
startScale: 1,
minScale: 0.01,
maxScale: 5.0
})
this.scatterContainerFront.addChild(sprite4)
return this
}
}
const scatterApp = new ScatterApp({
view: canvas2,
roundPixels: true,
antialias: true
})
.setup()
.run()
</script>
</body>
</html>
-1
View File
@@ -315,7 +315,6 @@ export class DisplayObjectScatter extends AbstractScatter {
if (y != null) this.y = y if (y != null) this.y = y
} }
getWorldScatter() { getWorldScatter() {
return this return this
} }
+15 -11
View File
@@ -80,7 +80,9 @@ export default class Stylus extends PIXI.Graphics {
backgroundFill = 0xffffff, backgroundFill = 0xffffff,
colorAlpha = 1, colorAlpha = 1,
captureEvents = true, captureEvents = true,
acceptMouseEvents = true acceptMouseEvents = true,
lineWidth = 16,
minStrokeLength = 4
} = {}) { } = {}) {
super() super()
this.activePointers = 0 this.activePointers = 0
@@ -99,7 +101,8 @@ export default class Stylus extends PIXI.Graphics {
this.undoCommandStack = [] this.undoCommandStack = []
this.strokes = [] this.strokes = []
this.stroke = [] this.stroke = []
this.minStrokeLength = 4 this.lineWidth = lineWidth
this.minStrokeLength = minStrokeLength
if (captureEvents) this.registerEventHandler(acceptMouseEvents) if (captureEvents) this.registerEventHandler(acceptMouseEvents)
this.drawBackground() this.drawBackground()
} }
@@ -273,7 +276,8 @@ export default class Stylus extends PIXI.Graphics {
pressure: event.pressure || null, pressure: event.pressure || null,
tiltX: this.tiltX, tiltX: this.tiltX,
tiltY: this.tiltY, tiltY: this.tiltY,
color: this.color color: this.color,
lineWidth: this.tiltToLineWidth(this.tiltY)
} }
return desc return desc
} }
@@ -297,7 +301,7 @@ export default class Stylus extends PIXI.Graphics {
} }
tiltToLineWidth(value) { tiltToLineWidth(value) {
return 16 //Math.round(Math.abs(value / 10) + 1) return this.lineWidth * Math.round(Math.abs(value / 10) + 1)
} }
drawStroke(stroke) { drawStroke(stroke) {
@@ -305,11 +309,11 @@ export default class Stylus extends PIXI.Graphics {
let start = stroke[0] let start = stroke[0]
this.beginFill(0, 0) this.beginFill(0, 0)
this.moveTo(start.x, start.y) this.moveTo(start.x, start.y)
let lineWidth = this.tiltToLineWidth(start.tiltY) let lineWidth = start.lineWidth
this.lineStyle(lineWidth, start.color, this.colorAlpha) this.lineStyle(lineWidth, start.color, this.colorAlpha)
for (let i = 1; i < stroke.length; i++) { for (let i = 1; i < stroke.length; i++) {
let info = stroke[i] let info = stroke[i]
let lw = this.tiltToLineWidth(info.tiltY) let lw = info.lineWidth
if (lw != lineWidth) { if (lw != lineWidth) {
lineWidth = lw lineWidth = lw
this.lineStyle(lineWidth, info.color, this.colorAlpha) this.lineStyle(lineWidth, info.color, this.colorAlpha)
@@ -328,7 +332,7 @@ export default class Stylus extends PIXI.Graphics {
drawStrokes() { drawStrokes() {
this.drawBackground() this.drawBackground()
this.lineStyle(1.0, 0xff0000, 1) this.lineStyle(this.lineWidth, 0xff0000, 1)
for (let stroke of this.iterStrokes()) { for (let stroke of this.iterStrokes()) {
this.drawStroke(stroke) this.drawStroke(stroke)
} }
@@ -356,17 +360,17 @@ export default class Stylus extends PIXI.Graphics {
} }
normalizeInfo(info) { normalizeInfo(info) {
let { x, y, pressure, tiltX, tiltY, color } = info let { x, y, pressure, tiltX, tiltY, color, lineWidth } = info
x /= this.wantedWidth x /= this.wantedWidth
y /= this.wantedHeight y /= this.wantedHeight
return { x, y, pressure, tiltX, tiltY, color } return { x, y, pressure, tiltX, tiltY, color, lineWidth }
} }
denormalizeInfo(info) { denormalizeInfo(info) {
let { x, y, pressure, tiltX, tiltY, color } = info let { x, y, pressure, tiltX, tiltY, color, lineWidth } = info
x = x * this.wantedWidth x = x * this.wantedWidth
y = y * this.wantedHeight y = y * this.wantedHeight
return { x, y, pressure, tiltX, tiltY, color } return { x, y, pressure, tiltX, tiltY, color, lineWidth }
} }
// Convert strokes into an object that can be stored in an Indexed DB. // Convert strokes into an object that can be stored in an Indexed DB.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 33 KiB

+2 -2
View File
@@ -34,8 +34,8 @@ oncontextmenu="PopupMenu.open(cmds,
oncontextmenu="PopupMenu.open(cmds, {x: event.offsetX, y: event.offsetY}, { parent: example, keepWithin: example}); return false;"></div> oncontextmenu="PopupMenu.open(cmds, {x: event.offsetX, y: event.offsetY}, { parent: example, keepWithin: example}); return false;"></div>
<script class="doctest"> <script class="doctest">
let cmds = { let cmds = {
"Start": () => alert("Start"), "Start": (event, key) => {console.log("start", event, key); alert(key) },
"Stop": () => alert("Stop") "Stop": (event, key) => alert(key)
} }
let app = new App() let app = new App()
let popup = new PopupMenu({ parent: example, fontSize: "2em", autoClose: false }) let popup = new PopupMenu({ parent: example, fontSize: "2em", autoClose: false })
+12 -6
View File
@@ -83,10 +83,10 @@ export default class PopupMenu extends Popup {
Elements.addClass(item, 'popupMenuItem') Elements.addClass(item, 'popupMenuItem')
this.items[key] = item this.items[key] = item
item.onclick = event => { item.onclick = event => {
this.perform(key) this.perform(event, key)
} }
item.ontap = event => { item.ontap = event => {
this.perform(key) this.perform(event, key)
} }
item.onmouseover = event => { item.onmouseover = event => {
this.over(event, key) this.over(event, key)
@@ -105,16 +105,22 @@ export default class PopupMenu extends Popup {
} }
/** Execute a menu command. /** Execute a menu command.
* @param {object} event - The trigger event.
* @param {string} key - The selected key. * @param {string} key - The selected key.
*/ */
perform(key) { perform(event, key) {
let func = this.commands[key] let func = this.commands[key]
if (this.autoClose) { if (this.autoClose) {
this.close() this.close()
} }
setTimeout(() => { setTimeout(
func.call() (event, key) => {
}, 20) func(event, key)
},
20,
event,
key
)
} }
/** Update the menu item denoted by key. /** Update the menu item denoted by key.
+33 -28
View File
@@ -44,8 +44,7 @@ const END = 'onEnd'
*/ */
export class ScatterEvent extends BaseEvent { export class ScatterEvent extends BaseEvent {
constructor( constructor(
target, target, { translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
{ translate = { x: 0, y: 0 }, scale = null, rotate = 0, about = null, fast = false, type = null } = {}
) { ) {
super('scatterTransformed', { target: target }) super('scatterTransformed', { target: target })
this.translate = translate this.translate = translate
@@ -106,7 +105,7 @@ class Throwable {
this.velocity = null this.velocity = null
this.timestamp = null this.timestamp = null
this.onThrowFinished = onThrowFinished this.onThrowFinished = onThrowFinished
//console.log("onThrowFinished", onThrowFinished) //console.log("onThrowFinished", onThrowFinished)
} }
observeVelocity() { observeVelocity() {
@@ -117,6 +116,9 @@ class Throwable {
let t = performance.now() let t = performance.now()
let dt = t - this.lastframe let dt = t - this.lastframe
this.lastframe = t this.lastframe = t
// When number is not set or is zero. Use one instead.
let number = delta.number ? delta.number : 1
if (dt > 0) { if (dt > 0) {
// Avoid division by zero errors later on // Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the // and consider the number of involved pointers sind addVelocity will be called by the
@@ -124,13 +126,14 @@ class Throwable {
let velocity = { let velocity = {
t: t, t: t,
dt: dt, dt: dt,
dx: delta.x / delta.number, dx: delta.x / number,
dy: delta.y / delta.number dy: delta.y / number
} }
this.velocities.push(velocity) this.velocities.push(velocity)
while (this.velocities.length > buffer) { while (this.velocities.length > buffer) {
this.velocities.shift() this.velocities.shift()
} }
} }
} }
@@ -180,7 +183,7 @@ class Throwable {
animateThrow(time) { animateThrow(time) {
if (this.velocity != null) { if (this.velocity != null) {
let dt = this._throwDeltaTime() let dt = this._throwDeltaTime()
// console.log("animateThrow", dt) // console.log("animateThrow", dt)
let next = this.nextVelocity(this.velocity) let next = this.nextVelocity(this.velocity)
let prevLength = Points.length(this.velocity) let prevLength = Points.length(this.velocity)
let nextLength = Points.length(next) let nextLength = Points.length(next)
@@ -378,7 +381,7 @@ export class AbstractScatter extends Throwable {
let delta = interaction.delta() let delta = interaction.delta()
if (delta != null) { if (delta != null) {
this.addVelocity(delta)
let rotate = delta.rotate let rotate = delta.rotate
let zoom = delta.zoom let zoom = delta.zoom
if (this.maxRotation != null) { if (this.maxRotation != null) {
@@ -399,6 +402,8 @@ export class AbstractScatter extends Throwable {
} }
this.transform(delta, zoom, rotate, delta.about) this.transform(delta, zoom, rotate, delta.about)
this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
if (zoom != 1) this.interactionAnchor = delta.about if (zoom != 1) this.interactionAnchor = delta.about
} }
} }
@@ -463,8 +468,8 @@ export class AbstractScatter extends Throwable {
keepOnStage(velocity, collision = 0.5) { keepOnStage(velocity, collision = 0.5) {
let stagePolygon = this.containerPolygon let stagePolygon = this.containerPolygon
// UO: since keepOnStage is called in nextVelocity we need to // UO: since keepOnStage is called in nextVelocity we need to
// ensure a return value // ensure a return value
if (!stagePolygon) return { x: 0, y: 0 } if (!stagePolygon) return { x: 0, y: 0 }
let polygon = this.polygon let polygon = this.polygon
let bounced = this.bouncing() let bounced = this.bouncing()
@@ -475,7 +480,7 @@ export class AbstractScatter extends Throwable {
let dx = this.movableX ? velocity.x : 0 let dx = this.movableX ? velocity.x : 0
let dy = this.movableY ? velocity.y : 0 let dy = this.movableY ? velocity.y : 0
let factor = this.throwDamping let factor = this.throwDamping
// if (recentered) { // if (recentered) {
if (x < 0) { if (x < 0) {
dx = -dx dx = -dx
factor = collision factor = collision
@@ -511,10 +516,10 @@ export class AbstractScatter extends Throwable {
onComplete: this.close.bind(this) onComplete: this.close.bind(this)
}) })
} else if (this.scale < this.minScale + this.scaleCloseThreshold) { } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
this.zoom(this.minScale + this.scaleCloseThreshold, { this.zoom(this.minScale + this.scaleCloseThreshold, {
animate: 0.4 animate: 0.4
}) })
} }
} }
rotateDegrees(degrees, anchor) { rotateDegrees(degrees, anchor) {
@@ -578,6 +583,7 @@ export class AbstractScatter extends Throwable {
} }
_move(delta) { _move(delta) {
// this.addVelocity(delta) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
this.x += this.movableX ? delta.x : 0 this.x += this.movableX ? delta.x : 0
this.y += this.movableX ? delta.y : 0 this.y += this.movableX ? delta.y : 0
} }
@@ -615,14 +621,14 @@ export class AbstractScatter extends Throwable {
let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom) let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom)
let extra = Points.subtract(newOrigin, origin) let extra = Points.subtract(newOrigin, origin)
let offset = Points.subtract(anchor, origin) const anchorOffset = Points.subtract(anchor, origin)
this._move(offset) // this._move(offset)
this.scale = newScale this.scale = newScale
this.rotation += rotate this.rotation += rotate
offset = Points.negate(offset) let offset = Points.negate(anchorOffset)
offset = Points.add(offset, extra) offset = Points.add(offset, extra)
offset = Points.add(offset, translate) offset = Points.add(offset, translate)
this._move(offset) this._move(Points.add(anchorOffset, offset))
delta.x += extra.x delta.x += extra.x
delta.y += extra.y delta.y += extra.y
@@ -906,8 +912,7 @@ export class DOMScatterContainer {
* @param {DOM node} debugCanvas - Shows debug infos about touches if not null * @param {DOM node} debugCanvas - Shows debug infos about touches if not null
*/ */
constructor( constructor(
element, element, {
{
stopEvents = 'auto', stopEvents = 'auto',
claimEvents = true, claimEvents = true,
useCapture = true, useCapture = true,
@@ -1071,8 +1076,7 @@ export class DOMScatterContainer {
export class DOMScatter extends AbstractScatter { export class DOMScatter extends AbstractScatter {
constructor( constructor(
element, element,
container, container, {
{
startScale = 1.0, startScale = 1.0,
minScale = 0.1, minScale = 0.1,
maxScale = 1.0, maxScale = 1.0,
@@ -1269,10 +1273,10 @@ export class DOMScatter extends AbstractScatter {
let r = this.bounds let r = this.bounds
let w2 = r.width / 2 let w2 = r.width / 2
let h2 = r.height / 2 let h2 = r.height / 2
// if (this.resizable) { // if (this.resizable) {
// w2 *= this.scale // w2 *= this.scale
// h2 *= this.scale // h2 *= this.scale
// } // }
var x = r.left + w2 var x = r.left + w2
var y = r.top + h2 var y = r.top + h2
return { x, y } return { x, y }
@@ -1392,7 +1396,8 @@ export class DOMScatter extends AbstractScatter {
} }
this._x = x this._x = x
this._y = y this._y = y
TweenLite.set(this.element, { x: x, y: y }) // this.addVelocity({ x: delta.x, y: delta.y }) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
TweenLite.set(this.element, { x, y })
} }
resizeAfterTransform(zoom) { resizeAfterTransform(zoom) {
@@ -1498,4 +1503,4 @@ export class DOMScatter extends AbstractScatter {
} }
} }
DOMScatter.zIndex = 1000 DOMScatter.zIndex = 1000
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 55 KiB

+12 -3
View File
@@ -396,9 +396,18 @@ export class Points {
// Distance == 0.0 indicates an inside relation. // Distance == 0.0 indicates an inside relation.
static distanceToRect(p, r) { static distanceToRect(p, r) {
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x) let dx = 0
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y) let dy = 0
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy)) if (p.x < r.x) dx = p.x - r.x
else if (p.x > r.x + r.width) dx = p.x - (r.x + r.width)
if (p.y < r.y) dy = p.y - r.y
else if (p.y > r.y + r.height) dy = p.y - (r.y + r.height)
return Math.sqrt(dx * dx + dy * dy)
/* let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
let result = Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
console.log("distanceToRect", p, r, result)
return result */
} }
static fromPageToNode(element, p) { static fromPageToNode(element, p) {
+12686 -1167
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -1,6 +1,6 @@
{ {
"name": "iwmlib", "name": "iwmlib",
"version": "1.1.0", "version": "1.1.2",
"description": "An Open Source library for multi-touch, WebGL powered applications.", "description": "An Open Source library for multi-touch, WebGL powered applications.",
"main": "index.js", "main": "index.js",
"directories": { "directories": {
@@ -27,10 +27,10 @@
"author": "", "author": "",
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@pixi/jsdoc-template": "^2.4.3", "@pixi/jsdoc-template": "^2.5.1",
"eslint": "^6.6.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^6.5.0", "eslint-config-prettier": "^6.9.0",
"eslint-plugin-prettier": "^3.1.1", "eslint-plugin-prettier": "^3.1.2",
"fs-extra": "^8.0.1", "fs-extra": "^8.0.1",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
@@ -48,13 +48,13 @@
"gsap": "^2.1.3", "gsap": "^2.1.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"optimal-select": "^4.0.1", "optimal-select": "^4.0.1",
"pixi-compressed-textures": "^2.0.3", "pixi-compressed-textures": "^2.0.5",
"pixi-ease": "^2.4.2", "pixi-ease": "^2.4.2",
"pixi-filters": "^3.0.3", "pixi-filters": "^3.0.3",
"pixi-particles": "^4.2.0", "pixi-particles": "^4.2.0",
"pixi-projection": "^0.3.5", "pixi-projection": "^0.3.7",
"pixi-viewport": "^4.3.3", "pixi-viewport": "^4.8.0",
"pixi.js": "^5.2.0", "pixi.js": "^5.2.1",
"propagating-hammerjs": "^1.4.7" "propagating-hammerjs": "^1.4.7"
} }
} }