Removed setup from constructor to simplify specializations
This commit is contained in:
commit
808106262e
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dist/*
|
||||||
|
doc/out/*
|
35
.eslintrc.json
Normal file
35
.eslintrc.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 10,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"impliedStrict": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"mocha"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true,
|
||||||
|
"node": true,
|
||||||
|
"jquery": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"PIXI": false,
|
||||||
|
"TweenLite": false,
|
||||||
|
"TweenMax": false,
|
||||||
|
"TimelineLite": false,
|
||||||
|
"TimelineMax": false,
|
||||||
|
"SystemJS": false
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"rules": {
|
||||||
|
"semi": ["error", "never"],
|
||||||
|
"quotes": ["warn", "single", {"allowTemplateLiterals": true}],
|
||||||
|
"no-console": "warn",
|
||||||
|
"no-unused-vars": ["warn", {"argsIgnorePattern": "^(e|event|points|ended)$"}],
|
||||||
|
"indent": ["warn", 4, {"SwitchCase": 1}],
|
||||||
|
"mocha/no-exclusive-tests": "error"
|
||||||
|
}
|
||||||
|
}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -79,3 +79,4 @@ typings/
|
|||||||
# own
|
# own
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
.history/
|
.history/
|
||||||
|
.vscode/
|
||||||
|
3
.stylelintignore
Normal file
3
.stylelintignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
dist/*
|
||||||
|
doc/*
|
||||||
|
lib/*
|
11
.stylelintrc.json
Normal file
11
.stylelintrc.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "stylelint-config-standard",
|
||||||
|
"rules": {
|
||||||
|
"indentation": 4,
|
||||||
|
"selector-list-comma-newline-after": "never-multi-line",
|
||||||
|
"no-eol-whitespace": [true, {
|
||||||
|
"ignore": ["empty-lines"]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"ignoreFiles": []
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
html
|
html {
|
||||||
{
|
padding: 0;
|
||||||
padding: 0px;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
background: white;
|
background: white;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
@ -44,7 +43,6 @@ canvas {
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.intrinsic-container {
|
.intrinsic-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
.flipWrapper
|
.flipWrapper {
|
||||||
{
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -17,6 +16,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
/*** See: https://stackoverflow.com/questions/7439042/css-js-to-prevent-dragging-of-ghost-image ***/
|
/*** See: https://stackoverflow.com/questions/7439042/css-js-to-prevent-dragging-of-ghost-image ***/
|
||||||
|
|
||||||
/* -webkit-user-drag: none;
|
/* -webkit-user-drag: none;
|
||||||
-khtml-user-drag: none;
|
-khtml-user-drag: none;
|
||||||
-moz-user-drag: none;
|
-moz-user-drag: none;
|
||||||
@ -48,8 +48,8 @@
|
|||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
right: 0px;
|
right: 0;
|
||||||
top: 0px;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infoBtn {
|
.infoBtn {
|
||||||
@ -58,8 +58,8 @@
|
|||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
right: 0px;
|
right: 0;
|
||||||
bottom: 0px;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.backBtn {
|
.backBtn {
|
||||||
@ -68,6 +68,6 @@
|
|||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
right: 0px;
|
right: 0;
|
||||||
bottom: 0px;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
html {
|
html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0px;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body
|
body {
|
||||||
{
|
margin: 0;
|
||||||
margin: 0px;
|
padding: 0;
|
||||||
padding: 0px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: 22pt;
|
font-size: 22pt;
|
||||||
-webkit-tap-highlight-color: #ccc;
|
-webkit-tap-highlight-color: #ccc;
|
||||||
background-color: #DDD;
|
background-color: #ddd;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-khtml-user-select: none;
|
-khtml-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
@ -22,24 +21,22 @@ body
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-hyphens: auto;
|
-webkit-hyphens: auto;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
|
|
||||||
/* https://davidwalsh.name/font-smoothing */
|
/* https://davidwalsh.name/font-smoothing */
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3
|
h3 {
|
||||||
{
|
|
||||||
color: white;
|
color: white;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
background-color: rgba(0, 0, 15, .5);
|
background-color: rgba(0, 0, 15, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
a { text-decoration: none; }
|
a { text-decoration: none; }
|
||||||
|
|
||||||
div.wrapper
|
div.wrapper {
|
||||||
{
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -52,26 +49,29 @@ div.wrapper
|
|||||||
from { background-color: rgba(0, 0, 0, 0); }
|
from { background-color: rgba(0, 0, 0, 0); }
|
||||||
to { background-color: red; }
|
to { background-color: red; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@-moz-keyframes color_change {
|
@-moz-keyframes color_change {
|
||||||
from { background-color: rgba(0, 0, 0, 0); }
|
from { background-color: rgba(0, 0, 0, 0); }
|
||||||
to { background-color: red; }
|
to { background-color: red; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@-ms-keyframes color_change {
|
@-ms-keyframes color_change {
|
||||||
from { background-color: rgba(0, 0, 0, 0); }
|
from { background-color: rgba(0, 0, 0, 0); }
|
||||||
to { background-color: red; }
|
to { background-color: red; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@-o-keyframes color_change {
|
@-o-keyframes color_change {
|
||||||
from { background-color: rgba(0, 0, 0, 0); }
|
from { background-color: rgba(0, 0, 0, 0); }
|
||||||
to { background-color: red; }
|
to { background-color: red; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes color_change {
|
@keyframes color_change {
|
||||||
from { background-color: rgba(0, 0, 0, 0); }
|
from { background-color: rgba(0, 0, 0, 0); }
|
||||||
to { background-color: red; }
|
to { background-color: red; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** CSS taken from https://medium.com/@jamesfuthey/simulating-the-creation-of-website-thumbnail-screenshots-using-iframes-7145269891db#.7v7fshos5 ***/
|
/*** CSS taken from https://medium.com/@jamesfuthey/simulating-the-creation-of-website-thumbnail-screenshots-using-iframes-7145269891db#.7v7fshos5 ***/
|
||||||
.thumbnail
|
.thumbnail {
|
||||||
{
|
|
||||||
position: relative;
|
position: relative;
|
||||||
-ms-zoom: 0.25;
|
-ms-zoom: 0.25;
|
||||||
-moz-transform: scale(0.25);
|
-moz-transform: scale(0.25);
|
||||||
@ -82,9 +82,7 @@ div.wrapper
|
|||||||
-webkit-transform-origin: 0 0;
|
-webkit-transform-origin: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.thumbnail::after {
|
||||||
.thumbnail:after
|
|
||||||
{
|
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -95,8 +93,11 @@ div.wrapper
|
|||||||
jamesfuthey blog. Otherwise touches would go through on iPad. ***/
|
jamesfuthey blog. Otherwise touches would go through on iPad. ***/
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumbnail iframe
|
iframe {
|
||||||
{
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail iframe {
|
||||||
width: 1024px;
|
width: 1024px;
|
||||||
height: 624px;
|
height: 624px;
|
||||||
-webkit-animation-delay: 3s; /* Safari 4.0 - 8.0 */
|
-webkit-animation-delay: 3s; /* Safari 4.0 - 8.0 */
|
||||||
@ -108,22 +109,20 @@ div.wrapper
|
|||||||
animation: color_change 1s infinite alternate;
|
animation: color_change 1s infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumbnail-container
|
.thumbnail-container {
|
||||||
{
|
|
||||||
width: calc(1024px * 0.25);
|
width: calc(1024px * 0.25);
|
||||||
height: calc(624px * 0.25);
|
height: calc(624px * 0.25);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 2px 2px 10px #000;
|
box-shadow: 2px 2px 10px #000;
|
||||||
color: #DDD;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.preview
|
div.preview {
|
||||||
{
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 22px;
|
margin: 22px;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -131,8 +130,7 @@ div.preview
|
|||||||
height: 196px;
|
height: 196px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.title
|
div.title {
|
||||||
{
|
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
width: 256px;
|
width: 256px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -140,10 +138,9 @@ div.title
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container
|
.container {
|
||||||
{
|
margin: 0;
|
||||||
margin: 0px;
|
padding: 0;
|
||||||
padding: 0px;
|
|
||||||
border: 2pt #000;
|
border: 2pt #000;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
@ -156,12 +153,7 @@ div.title
|
|||||||
align-content: flex-end;
|
align-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See https://github.com/electron/electron/issues/4420 */
|
/** See https://github.com/electron/electron/issues/4420 */
|
||||||
::selection {
|
::selection {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4499
dist/iwmlib.js
vendored
4499
dist/iwmlib.js
vendored
File diff suppressed because it is too large
Load Diff
326
dist/iwmlib.pixi.js
vendored
326
dist/iwmlib.pixi.js
vendored
@ -3263,6 +3263,13 @@
|
|||||||
return Math.sqrt(dx * dx + dy * dy)
|
return Math.sqrt(dx * dx + dy * dy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distance == 0.0 indicates an inside relation.
|
||||||
|
static distanceToRect(p, r) {
|
||||||
|
var cx = Math.max(Math.min(p.x, r.x + r.width), r.x);
|
||||||
|
var cy = Math.max(Math.min(p.y, r.y + r.height), r.y);
|
||||||
|
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
|
||||||
|
}
|
||||||
|
|
||||||
static fromPageToNode(element, p) {
|
static fromPageToNode(element, p) {
|
||||||
// if (window.webkitConvertPointFromPageToNode) {
|
// if (window.webkitConvertPointFromPageToNode) {
|
||||||
// return window.webkitConvertPointFromPageToNode(element,
|
// return window.webkitConvertPointFromPageToNode(element,
|
||||||
@ -4781,7 +4788,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* globals Hammer, propagating */
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
/** Interaction patterns
|
/** Interaction patterns
|
||||||
|
|
||||||
@ -4789,6 +4796,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class IInteractionTarget extends Interface {
|
class IInteractionTarget extends Interface {
|
||||||
|
|
||||||
capture(event) {
|
capture(event) {
|
||||||
return typeof true
|
return typeof true
|
||||||
}
|
}
|
||||||
@ -5199,7 +5207,6 @@
|
|||||||
}
|
}
|
||||||
let result = false;
|
let result = false;
|
||||||
if (this.isTap(key)) {
|
if (this.isTap(key)) {
|
||||||
|
|
||||||
this.registerTap(key, ended);
|
this.registerTap(key, ended);
|
||||||
result = this.tapCounts.get(key) == 2;
|
result = this.tapCounts.get(key) == 2;
|
||||||
}
|
}
|
||||||
@ -5313,7 +5320,9 @@
|
|||||||
if (this.capturePointerEvents) {
|
if (this.capturePointerEvents) {
|
||||||
try {
|
try {
|
||||||
element.setPointerCapture(e.pointerId);
|
element.setPointerCapture(e.pointerId);
|
||||||
} catch (e) { }
|
} catch (e) {
|
||||||
|
console.warn('Cannot setPointerCapture');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.onStart(e);
|
this.onStart(e);
|
||||||
}
|
}
|
||||||
@ -5345,7 +5354,9 @@
|
|||||||
if (this.capturePointerEvents) {
|
if (this.capturePointerEvents) {
|
||||||
try {
|
try {
|
||||||
element.releasePointerCapture(e.pointerId);
|
element.releasePointerCapture(e.pointerId);
|
||||||
} catch (e) { }
|
} catch (e) {
|
||||||
|
console.warn('Cannot release pointer');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
useCapture
|
useCapture
|
||||||
@ -5492,9 +5503,8 @@
|
|||||||
e => {
|
e => {
|
||||||
if (e.target == element) {
|
if (e.target == element) {
|
||||||
this.onEnd(e);
|
this.onEnd(e);
|
||||||
console.warn("Shouldn't happen: mouseout ends interaction");
|
console.warn('Shouldn\'t happen: mouseout ends interaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
useCapture
|
useCapture
|
||||||
);
|
);
|
||||||
@ -5594,20 +5604,23 @@
|
|||||||
// 'targetTouches'
|
// 'targetTouches'
|
||||||
let result = {};
|
let result = {};
|
||||||
switch (event.constructor.name) {
|
switch (event.constructor.name) {
|
||||||
case 'MouseEvent':
|
case 'MouseEvent': {
|
||||||
let buttons = event.buttons || event.which;
|
let buttons = event.buttons || event.which;
|
||||||
if (buttons) result['mouse'] = this.getPosition(event);
|
if (buttons) result['mouse'] = this.getPosition(event);
|
||||||
break
|
break
|
||||||
case 'PointerEvent':
|
}
|
||||||
|
case 'PointerEvent': {
|
||||||
result[event.pointerId.toString()] = this.getPosition(event);
|
result[event.pointerId.toString()] = this.getPosition(event);
|
||||||
break
|
break
|
||||||
case 'Touch':
|
}
|
||||||
|
case 'Touch': {
|
||||||
let id =
|
let id =
|
||||||
event.touchType === 'stylus'
|
event.touchType === 'stylus'
|
||||||
? 'stylus'
|
? 'stylus'
|
||||||
: event.identifier.toString();
|
: event.identifier.toString();
|
||||||
result[id] = this.getPosition(event);
|
result[id] = this.getPosition(event);
|
||||||
break
|
break
|
||||||
|
}
|
||||||
// case 'TouchEvent':
|
// case 'TouchEvent':
|
||||||
// // Needs to be observed: Perhaps changedTouches are all we need. If so
|
// // Needs to be observed: Perhaps changedTouches are all we need. If so
|
||||||
// // we can remove the touchEventKey default parameter
|
// // we can remove the touchEventKey default parameter
|
||||||
@ -5654,7 +5667,7 @@
|
|||||||
let point = extracted[key];
|
let point = extracted[key];
|
||||||
let updated = this.interaction.update(key, point);
|
let updated = this.interaction.update(key, point);
|
||||||
if (updated) {
|
if (updated) {
|
||||||
console.warn("new pointer in updateInteraction shouldn't happen", key);
|
console.warn('new pointer in updateInteraction shouldn\'t happen', key);
|
||||||
this.interactionStarted(event, key, point);
|
this.interactionStarted(event, key, point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5805,6 +5818,7 @@
|
|||||||
* @param {object} [opts] - An options object. See the hammer documentation for more details.
|
* @param {object} [opts] - An options object. See the hammer documentation for more details.
|
||||||
*/
|
*/
|
||||||
static on(types, elements, cb, opts = {}) {
|
static on(types, elements, cb, opts = {}) {
|
||||||
|
|
||||||
opts = Object.assign({}, {
|
opts = Object.assign({}, {
|
||||||
|
|
||||||
}, opts);
|
}, opts);
|
||||||
@ -6039,6 +6053,11 @@
|
|||||||
window.Capabilities = Capabilities;
|
window.Capabilities = Capabilities;
|
||||||
window.CapabilitiesTests = CapabilitiesTests;
|
window.CapabilitiesTests = CapabilitiesTests;
|
||||||
|
|
||||||
|
/** Basic class for poppable elements that need to be closed as soon as one poppable is
|
||||||
|
* shown.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
/**
|
/**
|
||||||
* A base class for scatter specific events.
|
* A base class for scatter specific events.
|
||||||
*
|
*
|
||||||
@ -6088,7 +6107,7 @@
|
|||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return (
|
return (
|
||||||
"Event('scatterTransformed', scale: " +
|
'Event(\'scatterTransformed\', scale: ' +
|
||||||
this.scale +
|
this.scale +
|
||||||
' about: ' +
|
' about: ' +
|
||||||
this.about.x +
|
this.about.x +
|
||||||
@ -6302,7 +6321,8 @@
|
|||||||
onThrowFinished = null,
|
onThrowFinished = null,
|
||||||
scaleAutoClose = false,
|
scaleAutoClose = false,
|
||||||
scaleCloseThreshold = 0.10,
|
scaleCloseThreshold = 0.10,
|
||||||
scaleCloseBuffer = 0.05
|
scaleCloseBuffer = 0.05,
|
||||||
|
maxRotation = Angle.degree2radian(5)
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (rotationDegrees != null && rotation != null) {
|
if (rotationDegrees != null && rotation != null) {
|
||||||
throw new Error('Use rotationDegrees or rotation but not both')
|
throw new Error('Use rotationDegrees or rotation but not both')
|
||||||
@ -6334,6 +6354,7 @@
|
|||||||
this.startScale = startScale; // Needed to reset object
|
this.startScale = startScale; // Needed to reset object
|
||||||
this.minScale = minScale;
|
this.minScale = minScale;
|
||||||
this.maxScale = maxScale;
|
this.maxScale = maxScale;
|
||||||
|
this.maxRotation = maxRotation;
|
||||||
this.overdoScaling = overdoScaling;
|
this.overdoScaling = overdoScaling;
|
||||||
this.translatable = translatable;
|
this.translatable = translatable;
|
||||||
if (!translatable) {
|
if (!translatable) {
|
||||||
@ -6345,6 +6366,7 @@
|
|||||||
this.resizable = resizable;
|
this.resizable = resizable;
|
||||||
this.mouseZoomFactor = mouseZoomFactor;
|
this.mouseZoomFactor = mouseZoomFactor;
|
||||||
this.autoBringToFront = autoBringToFront;
|
this.autoBringToFront = autoBringToFront;
|
||||||
|
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
this.onTransform = onTransform != null ? [onTransform] : null;
|
this.onTransform = onTransform != null ? [onTransform] : null;
|
||||||
this.onClose = onClose != null ? [onClose] : null;
|
this.onClose = onClose != null ? [onClose] : null;
|
||||||
@ -6379,10 +6401,15 @@
|
|||||||
|
|
||||||
gesture(interaction) {
|
gesture(interaction) {
|
||||||
let delta = interaction.delta();
|
let delta = interaction.delta();
|
||||||
//console.log("gesture", delta)
|
|
||||||
if (delta != null) {
|
if (delta != null) {
|
||||||
this.addVelocity(delta);
|
this.addVelocity(delta);
|
||||||
this.transform(delta, delta.zoom, delta.rotate, delta.about);
|
let alpha = delta.rotate;
|
||||||
|
if (this.maxRotation != null) {
|
||||||
|
if (Math.abs(alpha) > this.maxRotation) {
|
||||||
|
alpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.transform(delta, delta.zoom, alpha, delta.about);
|
||||||
if (delta.zoom != 1) this.interactionAnchor = delta.about;
|
if (delta.zoom != 1) this.interactionAnchor = delta.about;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6735,20 +6762,6 @@
|
|||||||
}
|
}
|
||||||
this._updateTransparency();
|
this._updateTransparency();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// if (this.onTransform != null) {
|
|
||||||
// let event = new ScatterEvent(this, {
|
|
||||||
// translate: {x: 0, y: 0},
|
|
||||||
// scale: this.scale,
|
|
||||||
// rotate: 0,
|
|
||||||
// about: null,
|
|
||||||
// fast: false,
|
|
||||||
// type: ZOOM
|
|
||||||
// })
|
|
||||||
// this.onTransform.forEach(function(f) {
|
|
||||||
// f(event)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onStart(event, interaction) {
|
onStart(event, interaction) {
|
||||||
@ -6915,7 +6928,9 @@
|
|||||||
width = null, // required
|
width = null, // required
|
||||||
height = null, // required
|
height = null, // required
|
||||||
resizable = false,
|
resizable = false,
|
||||||
simulateClick = false,
|
clickOnTap = false,
|
||||||
|
triggerSVGClicks = false,
|
||||||
|
allowClickDistance = 44,
|
||||||
verbose = true,
|
verbose = true,
|
||||||
onResize = null,
|
onResize = null,
|
||||||
touchAction = 'none',
|
touchAction = 'none',
|
||||||
@ -6966,7 +6981,8 @@
|
|||||||
this.height = height;
|
this.height = height;
|
||||||
this.throwVisibility = Math.min(width, height, throwVisibility);
|
this.throwVisibility = Math.min(width, height, throwVisibility);
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.simulateClick = simulateClick;
|
this.clickOnTap = clickOnTap;
|
||||||
|
this.triggerSVGClicks = triggerSVGClicks;
|
||||||
this.scale = startScale;
|
this.scale = startScale;
|
||||||
this.rotationDegrees = this.startRotationDegrees;
|
this.rotationDegrees = this.startRotationDegrees;
|
||||||
this.transformOrigin = transformOrigin;
|
this.transformOrigin = transformOrigin;
|
||||||
@ -6979,7 +6995,8 @@
|
|||||||
rotation: this.startRotationDegrees,
|
rotation: this.startRotationDegrees,
|
||||||
transformOrigin: transformOrigin
|
transformOrigin: transformOrigin
|
||||||
};
|
};
|
||||||
|
this.tapNodes = new Map();
|
||||||
|
this.allowClickDistance = allowClickDistance;
|
||||||
|
|
||||||
// For tweenlite we need initial values in _gsTransform
|
// For tweenlite we need initial values in _gsTransform
|
||||||
TweenLite.set(element, this.initialValues);
|
TweenLite.set(element, this.initialValues);
|
||||||
@ -6990,15 +7007,13 @@
|
|||||||
}
|
}
|
||||||
this.resizeButton = null;
|
this.resizeButton = null;
|
||||||
if (resizable) {
|
if (resizable) {
|
||||||
let button = document.createElement("div");
|
let button = document.createElement('div');
|
||||||
button.style.position = "absolute";
|
button.style.position = 'absolute';
|
||||||
button.style.right = "0px";
|
button.style.right = '0px';
|
||||||
button.style.bottom = "0px";
|
button.style.bottom = '0px';
|
||||||
button.style.width = "50px";
|
button.style.width = '50px';
|
||||||
button.style.height = "50px";
|
button.style.height = '50px';
|
||||||
// button.style.borderRadius = "100% 0px 0px 0px";
|
button.className = 'interactiveElement';
|
||||||
// button.style.background = this.element.style.backgroundColor
|
|
||||||
button.className = "interactiveElement";
|
|
||||||
this.element.appendChild(button);
|
this.element.appendChild(button);
|
||||||
|
|
||||||
button.addEventListener('pointerdown', (e) => {
|
button.addEventListener('pointerdown', (e) => {
|
||||||
@ -7014,6 +7029,25 @@
|
|||||||
});
|
});
|
||||||
this.resizeButton = button;
|
this.resizeButton = button;
|
||||||
}
|
}
|
||||||
|
if (clickOnTap) {
|
||||||
|
/* Since the tap triggers a synthetic click event
|
||||||
|
we must prevent the original trusted click event which
|
||||||
|
is also dispatched by the system.
|
||||||
|
*/
|
||||||
|
element.addEventListener('click', event => {
|
||||||
|
/* Currently we cannot send synthesized click events to SVG elements without unwanted side effects.
|
||||||
|
Therefore we make an exception and let the original click event through.
|
||||||
|
*/
|
||||||
|
if (event.target.ownerSVGElement) {
|
||||||
|
if (this.triggerSVGClicks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.isTrusted) {
|
||||||
|
Events$1.stop(event);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
container.add(this);
|
container.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7173,45 +7207,109 @@
|
|||||||
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ });
|
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ });
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVideo(element) {
|
onTap(event, interaction, point) {
|
||||||
if (element.paused) {
|
|
||||||
element.play();
|
if (this.clickOnTap) {
|
||||||
} else {
|
let directNode = document.elementFromPoint(event.clientX, event.clientY);
|
||||||
element.pause();
|
console.log("onTap", event);
|
||||||
|
if (this.isClickable(directNode)) {
|
||||||
|
directNode.click();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let nearestNode = this.nearestClickable(event);
|
||||||
|
if (this.isClickable(nearestNode)) {
|
||||||
|
/* 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.
|
||||||
|
Therefore we use the original click event (see constructor).
|
||||||
|
*/
|
||||||
|
if (nearestNode.tagName == 'svg') {
|
||||||
|
let handler = this.tapNodes.get(nearestNode);
|
||||||
|
console.log("Clicking beneath SVG: to be done", handler);
|
||||||
|
if (this.triggerSVGClicks)
|
||||||
|
nearestNode.dispatchEvent(new Event('click'));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("nearestNode clicked");
|
||||||
|
nearestNode.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {
|
/**
|
||||||
if (this.simulateClick) {
|
* Adds a click or tap behavior to the node. Uses
|
||||||
let p = Points.fromPageToNode(this.element, point);
|
* either the scatter clickOnTap version which requires click handlers
|
||||||
let iframe = this.element.querySelector('iframe');
|
* or uses the hammer.js driven tap handler.
|
||||||
if (iframe) {
|
*
|
||||||
let doc = iframe.contentWindow.document;
|
* @param {*} node
|
||||||
let element = doc.elementFromPoint(p.x, p.y);
|
* @param {*} handler
|
||||||
if (element == null) {
|
* @memberof DOMScatter
|
||||||
return
|
*/
|
||||||
|
|
||||||
|
addTapListener(node, handler) {
|
||||||
|
if (this.clickOnTap) {
|
||||||
|
node.addEventListener('click', handler);
|
||||||
|
this.tapNodes.set(node, handler);
|
||||||
}
|
}
|
||||||
switch (element.tagName) {
|
else {
|
||||||
case 'VIDEO':
|
InteractionMapper$1.on('tap', node, handler);
|
||||||
console.log(element.currentSrc);
|
|
||||||
if (PopupMenu) {
|
|
||||||
PopupMenu.open(
|
|
||||||
{
|
|
||||||
Fullscreen: () =>
|
|
||||||
window.open(element.currentSrc),
|
|
||||||
Play: () => element.play()
|
|
||||||
},
|
|
||||||
{ x, y }
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.toggleVideo(element);
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
element.click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isClickable(node) {
|
||||||
|
if (node == null)
|
||||||
|
return false
|
||||||
|
if (node.tagName == 'A')
|
||||||
|
return true
|
||||||
|
if (node.hasAttribute("onclick"))
|
||||||
|
return true
|
||||||
|
if (this.tapNodes.has(node))
|
||||||
|
return true
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all clickable nodes.
|
||||||
|
* Unfortunately we cannot search for all nodes with an attached 'click' event listener
|
||||||
|
* See https://stackoverflow.com/questions/11455515/how-to-check-whether-dynamically-attached-event-listener-exists-or-not
|
||||||
|
* Therefore we can only detect the following standard cases:
|
||||||
|
* I. All clickable objects like clickables
|
||||||
|
* II. Objects that have been attached a click handler by the scatter itself via
|
||||||
|
*/
|
||||||
|
clickableNodes() {
|
||||||
|
let result = [];
|
||||||
|
for (let node of this.element.querySelectorAll("*")) {
|
||||||
|
if (this.isClickable(node))
|
||||||
|
result.push(node);
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
nearestClickable(event) {
|
||||||
|
let element = this.element;
|
||||||
|
let clickables = this.clickableNodes();
|
||||||
|
let globalClick = (event.center) ? event.center : { x: event.x, y: event.y };
|
||||||
|
let localClick = Points.fromPageToNode(element, globalClick);
|
||||||
|
|
||||||
|
let clickRects = clickables.map(link => {
|
||||||
|
let rect = link.getBoundingClientRect();
|
||||||
|
let topLeft = Points.fromPageToNode(element, rect);
|
||||||
|
let center = Points.fromPageToNode(element, { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 });
|
||||||
|
return { x: topLeft.x, y: topLeft.y, width: rect.width, height: rect.height, center, link }
|
||||||
|
});
|
||||||
|
|
||||||
|
let distances = [];
|
||||||
|
clickRects.forEach(rect => {
|
||||||
|
let distance = Points.distanceToRect(localClick, rect);
|
||||||
|
distances.push(parseInt(distance));
|
||||||
|
});
|
||||||
|
|
||||||
|
let closestClickIndex = distances.indexOf(Math.min(...distances));
|
||||||
|
let closestClickable = clickables[closestClickIndex];
|
||||||
|
if (distances[closestClickIndex] < this.allowClickDistance) {
|
||||||
|
return closestClickable
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
isDescendant(parent, child) {
|
isDescendant(parent, child) {
|
||||||
@ -7250,14 +7348,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
resizeAfterTransform(zoom) {
|
resizeAfterTransform(zoom) {
|
||||||
// let w = this.width * this.scale
|
|
||||||
// let h = this.height * this.scale
|
|
||||||
// TweenLite.set(this.element, { width: w, height: h })
|
|
||||||
if (this.onResize) {
|
if (this.onResize) {
|
||||||
|
let w = this.width * this.scale;
|
||||||
|
let h = this.height * this.scale;
|
||||||
let event = new ResizeEvent(this, { width: w, height: h });
|
let event = new ResizeEvent(this, { width: w, height: h });
|
||||||
this.onResize(event);
|
this.onResize(event);
|
||||||
}
|
}
|
||||||
if (this.resizeButton != null) ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startResize(e) {
|
startResize(e) {
|
||||||
@ -7267,7 +7363,7 @@
|
|||||||
let oldPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
let oldPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
||||||
this.bringToFront();
|
this.bringToFront();
|
||||||
|
|
||||||
this.element.style.transformOrigin = "0% 0%";
|
this.element.style.transformOrigin = '0% 0%';
|
||||||
|
|
||||||
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
||||||
|
|
||||||
@ -7276,11 +7372,11 @@
|
|||||||
this.oldX = e.clientX;
|
this.oldX = e.clientX;
|
||||||
this.oldY = e.clientY;
|
this.oldY = e.clientY;
|
||||||
|
|
||||||
e.target.setAttribute('resizing', "true");
|
e.target.setAttribute('resizing', 'true');
|
||||||
e.target.setPointerCapture(e.pointerId);
|
e.target.setPointerCapture(e.pointerId);
|
||||||
|
|
||||||
TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } });
|
TweenLite.to(this.element, 0, { css: { left: '+=' + offset.x + 'px' } });
|
||||||
TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } });
|
TweenLite.to(this.element, 0, { css: { top: '+=' + offset.y + 'px' } });
|
||||||
|
|
||||||
this.element.dispatchEvent(event);
|
this.element.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
@ -7291,7 +7387,7 @@
|
|||||||
let rotation = Angle.radian2degree(this.rotation);
|
let rotation = Angle.radian2degree(this.rotation);
|
||||||
rotation = (rotation + 360) % 360;
|
rotation = (rotation + 360) % 360;
|
||||||
let event = new CustomEvent('resized');
|
let event = new CustomEvent('resized');
|
||||||
if (e.target.getAttribute('resizing') == "true") {
|
if (e.target.getAttribute('resizing') == 'true') {
|
||||||
|
|
||||||
let deltaX = (e.clientX - this.oldX);
|
let deltaX = (e.clientX - this.oldX);
|
||||||
let deltaY = (e.clientY - this.oldY);
|
let deltaY = (e.clientY - this.oldY);
|
||||||
@ -7323,14 +7419,14 @@
|
|||||||
|
|
||||||
let event = new CustomEvent('resizeEnded');
|
let event = new CustomEvent('resizeEnded');
|
||||||
let oldPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
let oldPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
||||||
this.element.style.transformOrigin = "50% 50%";
|
this.element.style.transformOrigin = '50% 50%';
|
||||||
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
|
||||||
let offset = Points.subtract(oldPostition, newPostition);
|
let offset = Points.subtract(oldPostition, newPostition);
|
||||||
|
|
||||||
TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } });
|
TweenLite.to(this.element, 0, { css: { left: '+=' + offset.x + 'px' } });
|
||||||
TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } });
|
TweenLite.to(this.element, 0, { css: { top: '+=' + offset.y + 'px' } });
|
||||||
|
|
||||||
e.target.setAttribute('resizing', "false");
|
e.target.setAttribute('resizing', 'false');
|
||||||
|
|
||||||
this.element.dispatchEvent(event);
|
this.element.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
@ -7401,6 +7497,7 @@
|
|||||||
translatable = true,
|
translatable = true,
|
||||||
scalable = true,
|
scalable = true,
|
||||||
rotatable = true,
|
rotatable = true,
|
||||||
|
clickOnTap = false,
|
||||||
onFront = null,
|
onFront = null,
|
||||||
onBack = null,
|
onBack = null,
|
||||||
onClose = null,
|
onClose = null,
|
||||||
@ -7420,6 +7517,7 @@
|
|||||||
this.translatable = translatable;
|
this.translatable = translatable;
|
||||||
this.scalable = scalable;
|
this.scalable = scalable;
|
||||||
this.rotatable = rotatable;
|
this.rotatable = rotatable;
|
||||||
|
this.clickOnTap = clickOnTap;
|
||||||
this.onFrontFlipped = onFront;
|
this.onFrontFlipped = onFront;
|
||||||
this.onBackFlipped = onBack;
|
this.onBackFlipped = onBack;
|
||||||
this.onClose = onClose;
|
this.onClose = onClose;
|
||||||
@ -7474,7 +7572,8 @@
|
|||||||
translatable: this.translatable,
|
translatable: this.translatable,
|
||||||
scalable: this.scalable,
|
scalable: this.scalable,
|
||||||
rotatable: this.rotatable,
|
rotatable: this.rotatable,
|
||||||
overdoScaling: this.overdoScaling
|
overdoScaling: this.overdoScaling,
|
||||||
|
clickOnTap: this.clickOnTap
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -7483,7 +7582,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.closeOnMinScale) {
|
if (this.closeOnMinScale) {
|
||||||
|
|
||||||
const removeOnMinScale = function () {
|
const removeOnMinScale = function () {
|
||||||
if (scatter.scale <= scatter.minScale) {
|
if (scatter.scale <= scatter.minScale) {
|
||||||
this.flippable.close();
|
this.flippable.close();
|
||||||
@ -7498,11 +7596,7 @@
|
|||||||
scatter.onTransform.splice(callbackIdx, 1);
|
scatter.onTransform.splice(callbackIdx, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
scatter.addTransformEventCallback(removeOnMinScale);
|
scatter.addTransformEventCallback(removeOnMinScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7529,6 +7623,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
setupFlippable(flippable, loader) {
|
setupFlippable(flippable, loader) {
|
||||||
|
console.log("setupFlippable", loader.wantedWidth);
|
||||||
flippable.wantedWidth = loader.wantedWidth;
|
flippable.wantedWidth = loader.wantedWidth;
|
||||||
flippable.wantedHeight = loader.wantedHeight;
|
flippable.wantedHeight = loader.wantedHeight;
|
||||||
flippable.wantedScale = loader.scale;
|
flippable.wantedScale = loader.scale;
|
||||||
@ -7538,8 +7633,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
start({ targetCenter = null } = {}) {
|
start({ targetCenter = null } = {}) {
|
||||||
console.log('DOMFlip.start', targetCenter);
|
console.log("DOMFlip.start", targetCenter);
|
||||||
if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter });
|
if (this.preloadBack) {
|
||||||
|
|
||||||
|
this.flippable.start({ duration: this.flipDuration, targetCenter });
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
let back = this.cardWrapper.querySelector('.back');
|
let back = this.cardWrapper.querySelector('.back');
|
||||||
let flippable = this.flippable;
|
let flippable = this.flippable;
|
||||||
@ -7588,10 +7686,16 @@
|
|||||||
this.onRemoved = flip.onRemoved;
|
this.onRemoved = flip.onRemoved;
|
||||||
this.onUpdate = flip.onUpdate;
|
this.onUpdate = flip.onUpdate;
|
||||||
|
|
||||||
|
this.wantedWidth = scatter.width;
|
||||||
|
this.wantedHeight = scatter.height;
|
||||||
|
this.wantedScale = scatter.scale;
|
||||||
|
this.minScale = scatter.minScale;
|
||||||
|
this.maxScale = scatter.maxScale;
|
||||||
|
|
||||||
this.flipDuration = flip.flipDuration;
|
this.flipDuration = flip.flipDuration;
|
||||||
this.fadeDuration = flip.fadeDuration;
|
this.fadeDuration = flip.fadeDuration;
|
||||||
scatter.addTransformEventCallback(this.scatterTransformed.bind(this));
|
scatter.addTransformEventCallback(this.scatterTransformed.bind(this));
|
||||||
console.log('lib.DOMFlippable', 5000);
|
|
||||||
TweenLite.set(this.element, { perspective: 5000 });
|
TweenLite.set(this.element, { perspective: 5000 });
|
||||||
TweenLite.set(this.card, { transformStyle: 'preserve-3d' });
|
TweenLite.set(this.card, { transformStyle: 'preserve-3d' });
|
||||||
TweenLite.set(this.back, { rotationY: -180 });
|
TweenLite.set(this.back, { rotationY: -180 });
|
||||||
@ -7604,16 +7708,22 @@
|
|||||||
this.backBtn = element.querySelector('.backBtn');
|
this.backBtn = element.querySelector('.backBtn');
|
||||||
this.closeBtn = element.querySelector('.closeBtn');
|
this.closeBtn = element.querySelector('.closeBtn');
|
||||||
/* Buttons are not guaranteed to exist. */
|
/* Buttons are not guaranteed to exist. */
|
||||||
if (this.infoBtn) {
|
|
||||||
InteractionMapper$1.on('tap', this.infoBtn, event => this.flip.start());
|
|
||||||
|
|
||||||
|
if (this.infoBtn) {
|
||||||
|
scatter.addTapListener(this.infoBtn, event => {
|
||||||
|
this.flip.start();
|
||||||
|
});
|
||||||
this.enable(this.infoBtn);
|
this.enable(this.infoBtn);
|
||||||
}
|
}
|
||||||
if (this.backBtn) {
|
if (this.backBtn) {
|
||||||
InteractionMapper$1.on('tap', this.backBtn, event => this.start());
|
scatter.addTapListener(this.backBtn, event => {
|
||||||
|
this.start();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (this.closeBtn) {
|
if (this.closeBtn) {
|
||||||
InteractionMapper$1.on('tap', this.closeBtn, event => this.close());
|
scatter.addTapListener(this.closeBtn, event => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
this.enable(this.closeBtn);
|
this.enable(this.closeBtn);
|
||||||
}
|
}
|
||||||
this.scaleButtons();
|
this.scaleButtons();
|
||||||
@ -7663,18 +7773,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
scaleButtons() {
|
scaleButtons() {
|
||||||
//This also works for svgs.
|
|
||||||
// if (this.infoBtn)
|
|
||||||
// this.infoBtn.style.transform = "scale(" + this.buttonScale + ")"
|
|
||||||
|
|
||||||
// if (this.backBtn)
|
|
||||||
// this.backBtn.style.transform = "scale(" + this.buttonScale + ")"
|
|
||||||
|
|
||||||
// if (this.closeBtn)
|
|
||||||
// this.closeBtn.style.transform = "scale(" + this.buttonScale + ")"
|
|
||||||
|
|
||||||
console.log(this.buttonScale);
|
|
||||||
//// This did not work with svgs!
|
|
||||||
TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], {
|
TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], {
|
||||||
scale: this.buttonScale
|
scale: this.buttonScale
|
||||||
});
|
});
|
||||||
@ -7687,6 +7785,7 @@
|
|||||||
|
|
||||||
clickInfo() {
|
clickInfo() {
|
||||||
this.bringToFront();
|
this.bringToFront();
|
||||||
|
console.log("clickInfo");
|
||||||
this.infoBtn.click();
|
this.infoBtn.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7728,8 +7827,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enable(button) {
|
enable(button) {
|
||||||
this.show(button, this.fadeDuration);
|
this.show(button, this.fadeDuration);
|
||||||
if (button) {
|
if (button) {
|
||||||
@ -7785,15 +7882,15 @@
|
|||||||
let x = this.flipped ? xx : this.startX;
|
let x = this.flipped ? xx : this.startX;
|
||||||
let y = this.flipped ? yy : this.startY;
|
let y = this.flipped ? yy : this.startY;
|
||||||
|
|
||||||
console.log("DOMFlippable.start", this.flipped, targetCenter, x, y, this.saved);
|
|
||||||
let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null;
|
let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null;
|
||||||
console.log(this.flipDuration);
|
console.log("start", this.flipDuration);
|
||||||
TweenLite.to(this.card, this.flipDuration, {
|
TweenLite.to(this.card, this.flipDuration, {
|
||||||
rotationY: targetY,
|
rotationY: targetY,
|
||||||
ease: Power1.easeOut,
|
ease: Power1.easeOut,
|
||||||
transformOrigin: '50% 50%',
|
transformOrigin: '50% 50%',
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onComplete: e => {
|
onComplete: e => {
|
||||||
|
console.log("start end", this.flipDuration);
|
||||||
if (this.flipped) {
|
if (this.flipped) {
|
||||||
//this.hide(this.front)
|
//this.hide(this.front)
|
||||||
this.enable(this.backBtn);
|
this.enable(this.backBtn);
|
||||||
@ -7826,6 +7923,7 @@
|
|||||||
force3D: true
|
force3D: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("start 2", this.wantedWidth, this.startWidth, {w, h});
|
||||||
// See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/
|
// See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/
|
||||||
TweenLite.to(this.element, this.flipDuration / 2, {
|
TweenLite.to(this.element, this.flipDuration / 2, {
|
||||||
scale: targetScale,
|
scale: targetScale,
|
||||||
|
@ -66,13 +66,13 @@ templates.
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
<script class="doctest">
|
<script class="doctest">
|
||||||
let scatterContainer = new DOMScatterContainer(main)
|
let scatterContainer = new DOMScatterContainer(main, {stopEvents: false})
|
||||||
if (Capabilities.supportsTemplate()) {
|
if (Capabilities.supportsTemplate()) {
|
||||||
let flip = new DOMFlip(scatterContainer,
|
let flip = new DOMFlip(scatterContainer,
|
||||||
flipTemplate,
|
flipTemplate,
|
||||||
new ImageLoader('./examples/king.jpeg'),
|
new ImageLoader('./examples/king.jpeg'),
|
||||||
new ImageLoader('./examples/women.jpeg'),
|
new ImageLoader('./examples/women.jpeg'),
|
||||||
{ onUpdate: e => console.log(e)})
|
{ clickOnTap: true})
|
||||||
flip.load().then((flip) => {
|
flip.load().then((flip) => {
|
||||||
flip.centerAt({ x: 150, y: 120})
|
flip.centerAt({ x: 150, y: 120})
|
||||||
})
|
})
|
||||||
|
@ -199,6 +199,7 @@ export class DOMFlip {
|
|||||||
translatable = true,
|
translatable = true,
|
||||||
scalable = true,
|
scalable = true,
|
||||||
rotatable = true,
|
rotatable = true,
|
||||||
|
clickOnTap = false,
|
||||||
onFront = null,
|
onFront = null,
|
||||||
onBack = null,
|
onBack = null,
|
||||||
onClose = null,
|
onClose = null,
|
||||||
@ -218,6 +219,7 @@ export class DOMFlip {
|
|||||||
this.translatable = translatable
|
this.translatable = translatable
|
||||||
this.scalable = scalable
|
this.scalable = scalable
|
||||||
this.rotatable = rotatable
|
this.rotatable = rotatable
|
||||||
|
this.clickOnTap = clickOnTap
|
||||||
this.onFrontFlipped = onFront
|
this.onFrontFlipped = onFront
|
||||||
this.onBackFlipped = onBack
|
this.onBackFlipped = onBack
|
||||||
this.onClose = onClose
|
this.onClose = onClose
|
||||||
@ -272,7 +274,8 @@ export class DOMFlip {
|
|||||||
translatable: this.translatable,
|
translatable: this.translatable,
|
||||||
scalable: this.scalable,
|
scalable: this.scalable,
|
||||||
rotatable: this.rotatable,
|
rotatable: this.rotatable,
|
||||||
overdoScaling: this.overdoScaling
|
overdoScaling: this.overdoScaling,
|
||||||
|
clickOnTap: this.clickOnTap
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -281,7 +284,6 @@ export class DOMFlip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.closeOnMinScale) {
|
if (this.closeOnMinScale) {
|
||||||
|
|
||||||
const removeOnMinScale = function () {
|
const removeOnMinScale = function () {
|
||||||
if (scatter.scale <= scatter.minScale) {
|
if (scatter.scale <= scatter.minScale) {
|
||||||
this.flippable.close()
|
this.flippable.close()
|
||||||
@ -296,11 +298,7 @@ export class DOMFlip {
|
|||||||
scatter.onTransform.splice(callbackIdx, 1)
|
scatter.onTransform.splice(callbackIdx, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
scatter.addTransformEventCallback(removeOnMinScale)
|
scatter.addTransformEventCallback(removeOnMinScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +325,7 @@ export class DOMFlip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setupFlippable(flippable, loader) {
|
setupFlippable(flippable, loader) {
|
||||||
|
console.log("setupFlippable", loader.wantedWidth)
|
||||||
flippable.wantedWidth = loader.wantedWidth
|
flippable.wantedWidth = loader.wantedWidth
|
||||||
flippable.wantedHeight = loader.wantedHeight
|
flippable.wantedHeight = loader.wantedHeight
|
||||||
flippable.wantedScale = loader.scale
|
flippable.wantedScale = loader.scale
|
||||||
@ -336,8 +335,11 @@ export class DOMFlip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start({ targetCenter = null } = {}) {
|
start({ targetCenter = null } = {}) {
|
||||||
console.log('DOMFlip.start', targetCenter)
|
console.log("DOMFlip.start", targetCenter)
|
||||||
if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter })
|
if (this.preloadBack) {
|
||||||
|
|
||||||
|
this.flippable.start({ duration: this.flipDuration, targetCenter })
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
let back = this.cardWrapper.querySelector('.back')
|
let back = this.cardWrapper.querySelector('.back')
|
||||||
let flippable = this.flippable
|
let flippable = this.flippable
|
||||||
@ -386,10 +388,16 @@ export class DOMFlippable {
|
|||||||
this.onRemoved = flip.onRemoved
|
this.onRemoved = flip.onRemoved
|
||||||
this.onUpdate = flip.onUpdate
|
this.onUpdate = flip.onUpdate
|
||||||
|
|
||||||
|
this.wantedWidth = scatter.width
|
||||||
|
this.wantedHeight = scatter.height
|
||||||
|
this.wantedScale = scatter.scale
|
||||||
|
this.minScale = scatter.minScale
|
||||||
|
this.maxScale = scatter.maxScale
|
||||||
|
|
||||||
this.flipDuration = flip.flipDuration
|
this.flipDuration = flip.flipDuration
|
||||||
this.fadeDuration = flip.fadeDuration
|
this.fadeDuration = flip.fadeDuration
|
||||||
scatter.addTransformEventCallback(this.scatterTransformed.bind(this))
|
scatter.addTransformEventCallback(this.scatterTransformed.bind(this))
|
||||||
console.log('lib.DOMFlippable', 5000)
|
|
||||||
TweenLite.set(this.element, { perspective: 5000 })
|
TweenLite.set(this.element, { perspective: 5000 })
|
||||||
TweenLite.set(this.card, { transformStyle: 'preserve-3d' })
|
TweenLite.set(this.card, { transformStyle: 'preserve-3d' })
|
||||||
TweenLite.set(this.back, { rotationY: -180 })
|
TweenLite.set(this.back, { rotationY: -180 })
|
||||||
@ -402,16 +410,22 @@ export class DOMFlippable {
|
|||||||
this.backBtn = element.querySelector('.backBtn')
|
this.backBtn = element.querySelector('.backBtn')
|
||||||
this.closeBtn = element.querySelector('.closeBtn')
|
this.closeBtn = element.querySelector('.closeBtn')
|
||||||
/* Buttons are not guaranteed to exist. */
|
/* Buttons are not guaranteed to exist. */
|
||||||
if (this.infoBtn) {
|
|
||||||
InteractionMapper.on('tap', this.infoBtn, event => this.flip.start())
|
|
||||||
|
|
||||||
|
if (this.infoBtn) {
|
||||||
|
scatter.addTapListener(this.infoBtn, event => {
|
||||||
|
this.flip.start()
|
||||||
|
})
|
||||||
this.enable(this.infoBtn)
|
this.enable(this.infoBtn)
|
||||||
}
|
}
|
||||||
if (this.backBtn) {
|
if (this.backBtn) {
|
||||||
InteractionMapper.on('tap', this.backBtn, event => this.start())
|
scatter.addTapListener(this.backBtn, event => {
|
||||||
|
this.start()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (this.closeBtn) {
|
if (this.closeBtn) {
|
||||||
InteractionMapper.on('tap', this.closeBtn, event => this.close())
|
scatter.addTapListener(this.closeBtn, event => {
|
||||||
|
this.close()
|
||||||
|
})
|
||||||
this.enable(this.closeBtn)
|
this.enable(this.closeBtn)
|
||||||
}
|
}
|
||||||
this.scaleButtons()
|
this.scaleButtons()
|
||||||
@ -461,18 +475,6 @@ export class DOMFlippable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scaleButtons() {
|
scaleButtons() {
|
||||||
//This also works for svgs.
|
|
||||||
// if (this.infoBtn)
|
|
||||||
// this.infoBtn.style.transform = "scale(" + this.buttonScale + ")"
|
|
||||||
|
|
||||||
// if (this.backBtn)
|
|
||||||
// this.backBtn.style.transform = "scale(" + this.buttonScale + ")"
|
|
||||||
|
|
||||||
// if (this.closeBtn)
|
|
||||||
// this.closeBtn.style.transform = "scale(" + this.buttonScale + ")"
|
|
||||||
|
|
||||||
console.log(this.buttonScale)
|
|
||||||
//// This did not work with svgs!
|
|
||||||
TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], {
|
TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], {
|
||||||
scale: this.buttonScale
|
scale: this.buttonScale
|
||||||
})
|
})
|
||||||
@ -485,6 +487,7 @@ export class DOMFlippable {
|
|||||||
|
|
||||||
clickInfo() {
|
clickInfo() {
|
||||||
this.bringToFront()
|
this.bringToFront()
|
||||||
|
console.log("clickInfo")
|
||||||
this.infoBtn.click()
|
this.infoBtn.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,8 +529,6 @@ export class DOMFlippable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enable(button) {
|
enable(button) {
|
||||||
this.show(button, this.fadeDuration)
|
this.show(button, this.fadeDuration)
|
||||||
if (button) {
|
if (button) {
|
||||||
@ -583,15 +584,15 @@ export class DOMFlippable {
|
|||||||
let x = this.flipped ? xx : this.startX
|
let x = this.flipped ? xx : this.startX
|
||||||
let y = this.flipped ? yy : this.startY
|
let y = this.flipped ? yy : this.startY
|
||||||
|
|
||||||
console.log("DOMFlippable.start", this.flipped, targetCenter, x, y, this.saved)
|
|
||||||
let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null
|
let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null
|
||||||
console.log(this.flipDuration)
|
console.log("start", this.flipDuration)
|
||||||
TweenLite.to(this.card, this.flipDuration, {
|
TweenLite.to(this.card, this.flipDuration, {
|
||||||
rotationY: targetY,
|
rotationY: targetY,
|
||||||
ease: Power1.easeOut,
|
ease: Power1.easeOut,
|
||||||
transformOrigin: '50% 50%',
|
transformOrigin: '50% 50%',
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onComplete: e => {
|
onComplete: e => {
|
||||||
|
console.log("start end", this.flipDuration)
|
||||||
if (this.flipped) {
|
if (this.flipped) {
|
||||||
//this.hide(this.front)
|
//this.hide(this.front)
|
||||||
this.enable(this.backBtn)
|
this.enable(this.backBtn)
|
||||||
@ -624,6 +625,7 @@ export class DOMFlippable {
|
|||||||
force3D: true
|
force3D: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log("start 2", this.wantedWidth, this.startWidth, {w, h})
|
||||||
// See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/
|
// See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/
|
||||||
TweenLite.to(this.element, this.flipDuration / 2, {
|
TweenLite.to(this.element, this.flipDuration / 2, {
|
||||||
scale: targetScale,
|
scale: targetScale,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
/* globals Hammer, propagating */
|
/* globals Hammer, propagating */
|
||||||
/*eslint no-console: ["error", { allow: ["log", "warn", "info", "error"] }]*/
|
/*eslint no-console: ["error", { allow: ["log", "warn", "info", "error"] }]*/
|
||||||
|
|
||||||
import Interface from './interface.js'
|
import Interface from './interface.js'
|
||||||
import { Points, Angle, MapProxy } from './utils.js'
|
import { Points, MapProxy } from './utils.js'
|
||||||
import Events from './events.js'
|
import Events from './events.js'
|
||||||
import Logging from './logging.js'
|
import Logging from './logging.js'
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ import Logging from './logging.js'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export class IInteractionTarget extends Interface {
|
export class IInteractionTarget extends Interface {
|
||||||
|
|
||||||
capture(event) {
|
capture(event) {
|
||||||
return typeof true
|
return typeof true
|
||||||
}
|
}
|
||||||
@ -422,7 +424,6 @@ export class Interaction extends InteractionPoints {
|
|||||||
}
|
}
|
||||||
let result = false
|
let result = false
|
||||||
if (this.isTap(key)) {
|
if (this.isTap(key)) {
|
||||||
|
|
||||||
this.registerTap(key, ended)
|
this.registerTap(key, ended)
|
||||||
result = this.tapCounts.get(key) == 2
|
result = this.tapCounts.get(key) == 2
|
||||||
}
|
}
|
||||||
@ -536,7 +537,9 @@ export class InteractionDelegate {
|
|||||||
if (this.capturePointerEvents) {
|
if (this.capturePointerEvents) {
|
||||||
try {
|
try {
|
||||||
element.setPointerCapture(e.pointerId)
|
element.setPointerCapture(e.pointerId)
|
||||||
} catch (e) { }
|
} catch (e) {
|
||||||
|
console.warn('Cannot setPointerCapture')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.onStart(e)
|
this.onStart(e)
|
||||||
}
|
}
|
||||||
@ -568,7 +571,9 @@ export class InteractionDelegate {
|
|||||||
if (this.capturePointerEvents) {
|
if (this.capturePointerEvents) {
|
||||||
try {
|
try {
|
||||||
element.releasePointerCapture(e.pointerId)
|
element.releasePointerCapture(e.pointerId)
|
||||||
} catch (e) { }
|
} catch (e) {
|
||||||
|
console.warn('Cannot release pointer')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
useCapture
|
useCapture
|
||||||
@ -715,9 +720,8 @@ export class InteractionDelegate {
|
|||||||
e => {
|
e => {
|
||||||
if (e.target == element) {
|
if (e.target == element) {
|
||||||
this.onEnd(e)
|
this.onEnd(e)
|
||||||
console.warn("Shouldn't happen: mouseout ends interaction")
|
console.warn('Shouldn\'t happen: mouseout ends interaction')
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
useCapture
|
useCapture
|
||||||
)
|
)
|
||||||
@ -819,20 +823,23 @@ export class InteractionDelegate {
|
|||||||
// 'targetTouches'
|
// 'targetTouches'
|
||||||
let result = {}
|
let result = {}
|
||||||
switch (event.constructor.name) {
|
switch (event.constructor.name) {
|
||||||
case 'MouseEvent':
|
case 'MouseEvent': {
|
||||||
let buttons = event.buttons || event.which
|
let buttons = event.buttons || event.which
|
||||||
if (buttons) result['mouse'] = this.getPosition(event)
|
if (buttons) result['mouse'] = this.getPosition(event)
|
||||||
break
|
break
|
||||||
case 'PointerEvent':
|
}
|
||||||
|
case 'PointerEvent': {
|
||||||
result[event.pointerId.toString()] = this.getPosition(event)
|
result[event.pointerId.toString()] = this.getPosition(event)
|
||||||
break
|
break
|
||||||
case 'Touch':
|
}
|
||||||
|
case 'Touch': {
|
||||||
let id =
|
let id =
|
||||||
event.touchType === 'stylus'
|
event.touchType === 'stylus'
|
||||||
? 'stylus'
|
? 'stylus'
|
||||||
: event.identifier.toString()
|
: event.identifier.toString()
|
||||||
result[id] = this.getPosition(event)
|
result[id] = this.getPosition(event)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
// case 'TouchEvent':
|
// case 'TouchEvent':
|
||||||
// // Needs to be observed: Perhaps changedTouches are all we need. If so
|
// // Needs to be observed: Perhaps changedTouches are all we need. If so
|
||||||
// // we can remove the touchEventKey default parameter
|
// // we can remove the touchEventKey default parameter
|
||||||
@ -879,7 +886,7 @@ export class InteractionDelegate {
|
|||||||
let point = extracted[key]
|
let point = extracted[key]
|
||||||
let updated = this.interaction.update(key, point)
|
let updated = this.interaction.update(key, point)
|
||||||
if (updated) {
|
if (updated) {
|
||||||
console.warn("new pointer in updateInteraction shouldn't happen", key)
|
console.warn('new pointer in updateInteraction shouldn\'t happen', key)
|
||||||
this.interactionStarted(event, key, point)
|
this.interactionStarted(event, key, point)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1032,6 +1039,7 @@ export class InteractionMapper extends InteractionDelegate {
|
|||||||
* @param {object} [opts] - An options object. See the hammer documentation for more details.
|
* @param {object} [opts] - An options object. See the hammer documentation for more details.
|
||||||
*/
|
*/
|
||||||
static on(types, elements, cb, opts = {}) {
|
static on(types, elements, cb, opts = {}) {
|
||||||
|
|
||||||
opts = Object.assign({}, {
|
opts = Object.assign({}, {
|
||||||
|
|
||||||
}, opts)
|
}, opts)
|
||||||
|
@ -13,12 +13,11 @@
|
|||||||
debugCanvas.width = main.getBoundingClientRect().width
|
debugCanvas.width = main.getBoundingClientRect().width
|
||||||
let context = debugCanvas.getContext('2d')
|
let context = debugCanvas.getContext('2d')
|
||||||
context.clearRect(0, 0, debugCanvas.width, debugCanvas.height)
|
context.clearRect(0, 0, debugCanvas.width, debugCanvas.height)
|
||||||
|
|
||||||
let stage = scatterContainer.polygon
|
let stage = scatterContainer.polygon
|
||||||
stage.draw(context, { stroke: '#FF0000'})
|
stage.draw(context, { stroke: "#0000FF"})
|
||||||
for(let scatter of scatterContainer.scatter.values()) {
|
for(let scatter of scatterContainer.scatter.values()) {
|
||||||
let polygon = scatter.polygon
|
let polygon = scatter.polygon
|
||||||
polygon.draw(context, { stroke: '#FF0000'})
|
polygon.draw(context, { stroke: '#0000FF'})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +25,7 @@
|
|||||||
requestAnimationFrame((dt) => {
|
requestAnimationFrame((dt) => {
|
||||||
drawPolygons()
|
drawPolygons()
|
||||||
animatePolygons()
|
animatePolygons()
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -48,7 +48,7 @@ we describe the more basic DOM scatter.
|
|||||||
<img id="women" draggable="false" style="position: absolute;" src="examples/women.jpeg" />
|
<img id="women" draggable="false" style="position: absolute;" src="examples/women.jpeg" />
|
||||||
<img id="king" draggable="false" style="position: absolute;" src="examples/king.jpeg" />
|
<img id="king" draggable="false" style="position: absolute;" src="examples/king.jpeg" />
|
||||||
|
|
||||||
<canvas id="debugCanvas" height="280" style="z-index: 100000; pointer-events: none; position: absolute; border: 1px solid red;">
|
<canvas id="debugCanvas" height="280" style="z-index: 100000; pointer-events: none; position: absolute; border: 1px solid blue;">
|
||||||
Canvas not supported.
|
Canvas not supported.
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
@ -80,5 +80,47 @@ for(let key of ['women', 'king']) {
|
|||||||
app.run()
|
app.run()
|
||||||
animatePolygons()
|
animatePolygons()
|
||||||
</script>
|
</script>
|
||||||
|
<h1>
|
||||||
|
Interactive Content
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
Scatter objects may contain interactive HTML structures. There is one major flag that allows
|
||||||
|
to simulate click events by using taps. If the scatter detects a tap it looks for clickable
|
||||||
|
elements under or nearby the event position and calls the click handler. Thus gestures
|
||||||
|
can be disambiguated as moves, zooms. or taps.
|
||||||
|
|
||||||
|
Note that on touch devices you can tap beside the object if you use clickOnTap. The allowed distance
|
||||||
|
can be configured by allowClickDistance. The default value is 44px.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="contentExample" class="grayBorder interactive" style="position: relative; width: 100%; height: 280px;">
|
||||||
|
|
||||||
|
<div id="interactiveContent">
|
||||||
|
<img draggable="false" style="position: absolute;" src="examples/women.jpeg" />
|
||||||
|
<a style="position:absolute; top: 10px; right: 10px; color:white;" href="javascript:alert('test link')">A Link</a>
|
||||||
|
<div onclick="alert('div clicked')" style="position:absolute; top: 30px; right: 10px; color:white;">A Div with click handler</div>
|
||||||
|
<svg onclick="alert('svg clicked')" style="position: absolute; right: 0px; bottom: 0px; width: 32px; height: 32px;" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
||||||
|
<circle cx="50" cy="50" r="44" stroke="white" stroke-width="8" fill="gray" />
|
||||||
|
<circle cx="50" cy="32" r="7" fill="white" />
|
||||||
|
<line x1="50" y1="46" x2="50" y2="78" stroke="white" stroke-width="12" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script class="doctest">
|
||||||
|
|
||||||
|
let contentContainer = new DOMScatterContainer(contentExample)
|
||||||
|
new DOMScatter(interactiveContent, contentContainer, {
|
||||||
|
x: 44,
|
||||||
|
y: 44,
|
||||||
|
width: 274,
|
||||||
|
height: 184,
|
||||||
|
clickOnTap: true,
|
||||||
|
throwVisibility: 88,
|
||||||
|
triggerSVGClicks: true,
|
||||||
|
minScale: 0.5,
|
||||||
|
maxScale: 1.5})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
276
lib/scatter.js
276
lib/scatter.js
@ -1,8 +1,11 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
/* globals TweenLite debugCanvas */
|
||||||
import { Points, Polygon, Angle, Elements } from './utils.js'
|
import { Points, Polygon, Angle, Elements } from './utils.js'
|
||||||
import Events from './events.js'
|
import Events from './events.js'
|
||||||
import { InteractionMapper } from './interaction.js'
|
import { InteractionMapper } from './interaction.js'
|
||||||
import { Capabilities } from './capabilities.js'
|
import { Capabilities } from './capabilities.js'
|
||||||
|
import PopupMenu from './popupmenu.js'
|
||||||
/**
|
/**
|
||||||
* A base class for scatter specific events.
|
* A base class for scatter specific events.
|
||||||
*
|
*
|
||||||
@ -21,8 +24,6 @@ export class BaseEvent {
|
|||||||
const START = 'onStart'
|
const START = 'onStart'
|
||||||
const UPDATE = 'onUpdate'
|
const UPDATE = 'onUpdate'
|
||||||
const END = 'onEnd'
|
const END = 'onEnd'
|
||||||
const ZOOM = 'onZoom'
|
|
||||||
const MOVE = 'onMove'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scatter event that describes how the scatter has changed.
|
* A scatter event that describes how the scatter has changed.
|
||||||
@ -54,7 +55,7 @@ export class ScatterEvent extends BaseEvent {
|
|||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return (
|
return (
|
||||||
"Event('scatterTransformed', scale: " +
|
'Event(\'scatterTransformed\', scale: ' +
|
||||||
this.scale +
|
this.scale +
|
||||||
' about: ' +
|
' about: ' +
|
||||||
this.about.x +
|
this.about.x +
|
||||||
@ -268,7 +269,8 @@ export class AbstractScatter extends Throwable {
|
|||||||
onThrowFinished = null,
|
onThrowFinished = null,
|
||||||
scaleAutoClose = false,
|
scaleAutoClose = false,
|
||||||
scaleCloseThreshold = 0.10,
|
scaleCloseThreshold = 0.10,
|
||||||
scaleCloseBuffer = 0.05
|
scaleCloseBuffer = 0.05,
|
||||||
|
maxRotation = Angle.degree2radian(5)
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (rotationDegrees != null && rotation != null) {
|
if (rotationDegrees != null && rotation != null) {
|
||||||
throw new Error('Use rotationDegrees or rotation but not both')
|
throw new Error('Use rotationDegrees or rotation but not both')
|
||||||
@ -300,6 +302,7 @@ export class AbstractScatter extends Throwable {
|
|||||||
this.startScale = startScale // Needed to reset object
|
this.startScale = startScale // Needed to reset object
|
||||||
this.minScale = minScale
|
this.minScale = minScale
|
||||||
this.maxScale = maxScale
|
this.maxScale = maxScale
|
||||||
|
this.maxRotation = maxRotation
|
||||||
this.overdoScaling = overdoScaling
|
this.overdoScaling = overdoScaling
|
||||||
this.translatable = translatable
|
this.translatable = translatable
|
||||||
if (!translatable) {
|
if (!translatable) {
|
||||||
@ -311,6 +314,7 @@ export class AbstractScatter extends Throwable {
|
|||||||
this.resizable = resizable
|
this.resizable = resizable
|
||||||
this.mouseZoomFactor = mouseZoomFactor
|
this.mouseZoomFactor = mouseZoomFactor
|
||||||
this.autoBringToFront = autoBringToFront
|
this.autoBringToFront = autoBringToFront
|
||||||
|
|
||||||
this.dragging = false
|
this.dragging = false
|
||||||
this.onTransform = onTransform != null ? [onTransform] : null
|
this.onTransform = onTransform != null ? [onTransform] : null
|
||||||
this.onClose = onClose != null ? [onClose] : null
|
this.onClose = onClose != null ? [onClose] : null
|
||||||
@ -345,10 +349,15 @@ export class AbstractScatter extends Throwable {
|
|||||||
|
|
||||||
gesture(interaction) {
|
gesture(interaction) {
|
||||||
let delta = interaction.delta()
|
let delta = interaction.delta()
|
||||||
//console.log("gesture", delta)
|
|
||||||
if (delta != null) {
|
if (delta != null) {
|
||||||
this.addVelocity(delta)
|
this.addVelocity(delta)
|
||||||
this.transform(delta, delta.zoom, delta.rotate, delta.about)
|
let alpha = delta.rotate
|
||||||
|
if (this.maxRotation != null) {
|
||||||
|
if (Math.abs(alpha) > this.maxRotation) {
|
||||||
|
alpha = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.transform(delta, delta.zoom, alpha, delta.about)
|
||||||
if (delta.zoom != 1) this.interactionAnchor = delta.about
|
if (delta.zoom != 1) this.interactionAnchor = delta.about
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -701,20 +710,6 @@ export class AbstractScatter extends Throwable {
|
|||||||
}
|
}
|
||||||
this._updateTransparency()
|
this._updateTransparency()
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// if (this.onTransform != null) {
|
|
||||||
// let event = new ScatterEvent(this, {
|
|
||||||
// translate: {x: 0, y: 0},
|
|
||||||
// scale: this.scale,
|
|
||||||
// rotate: 0,
|
|
||||||
// about: null,
|
|
||||||
// fast: false,
|
|
||||||
// type: ZOOM
|
|
||||||
// })
|
|
||||||
// this.onTransform.forEach(function(f) {
|
|
||||||
// f(event)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onStart(event, interaction) {
|
onStart(event, interaction) {
|
||||||
@ -829,10 +824,10 @@ export class AbstractScatter extends Throwable {
|
|||||||
scale: this.scale,
|
scale: this.scale,
|
||||||
fast: false,
|
fast: false,
|
||||||
type: null
|
type: null
|
||||||
});
|
})
|
||||||
this.onTransform.forEach(function (f) {
|
this.onTransform.forEach(function (f) {
|
||||||
f(event);
|
f(event)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,14 +865,20 @@ export class DOMScatterContainer {
|
|||||||
* @param {String} [touchAction=none] - CSS to set touch action style, needed to prevent
|
* @param {String} [touchAction=none] - CSS to set touch action style, needed to prevent
|
||||||
* pointer cancel events. Use null if the
|
* pointer cancel events. Use null if the
|
||||||
* the touch action should not be set.
|
* the touch action should not be set.
|
||||||
|
* @param {DOM node} debugCanvas - Shows debug infos about touches if not null
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
element,
|
element,
|
||||||
{ stopEvents = 'auto', claimEvents = true, useCapture = true, touchAction = 'none' } = {}
|
{ stopEvents = 'auto', claimEvents = true, useCapture = true, touchAction = 'none', debugCanvas = null } = {}
|
||||||
) {
|
) {
|
||||||
this.onCapture = null
|
this.onCapture = null
|
||||||
this.element = element
|
this.element = element
|
||||||
if (stopEvents === 'auto') {
|
if (stopEvents === 'auto') {
|
||||||
|
/*
|
||||||
|
The events have to be stopped in Safari, otherwise the whole page will be zoomed with
|
||||||
|
a pinch gesture (preventDefault in method preventPinch). In order to enable the
|
||||||
|
movement of scatter objects, the touchmove event has to be bound again.
|
||||||
|
*/
|
||||||
if (Capabilities.isSafari) {
|
if (Capabilities.isSafari) {
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
'touchmove',
|
'touchmove',
|
||||||
@ -900,16 +901,15 @@ export class DOMScatterContainer {
|
|||||||
mouseWheelElement: window
|
mouseWheelElement: window
|
||||||
})
|
})
|
||||||
|
|
||||||
if (typeof debugCanvas !== 'undefined') {
|
if (debugCanvas !== null) {
|
||||||
requestAnimationFrame(dt => {
|
requestAnimationFrame(dt => {
|
||||||
this.showTouches(dt)
|
this.showTouches(dt, debugCanvas)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showTouches(dt) {
|
showTouches(dt, canvas) {
|
||||||
let resolution = window.devicePixelRatio
|
let resolution = window.devicePixelRatio
|
||||||
let canvas = debugCanvas
|
|
||||||
let current = this.delegate.interaction.current
|
let current = this.delegate.interaction.current
|
||||||
let context = canvas.getContext('2d')
|
let context = canvas.getContext('2d')
|
||||||
let radius = 20 * resolution
|
let radius = 20 * resolution
|
||||||
@ -932,7 +932,7 @@ export class DOMScatterContainer {
|
|||||||
context.stroke()
|
context.stroke()
|
||||||
}
|
}
|
||||||
requestAnimationFrame(dt => {
|
requestAnimationFrame(dt => {
|
||||||
this.showTouches(dt)
|
this.showTouches(dt, canvas)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,7 +1043,9 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
width = null, // required
|
width = null, // required
|
||||||
height = null, // required
|
height = null, // required
|
||||||
resizable = false,
|
resizable = false,
|
||||||
simulateClick = false,
|
clickOnTap = false,
|
||||||
|
triggerSVGClicks = false,
|
||||||
|
allowClickDistance = 44,
|
||||||
verbose = true,
|
verbose = true,
|
||||||
onResize = null,
|
onResize = null,
|
||||||
touchAction = 'none',
|
touchAction = 'none',
|
||||||
@ -1094,7 +1096,8 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
this.height = height
|
this.height = height
|
||||||
this.throwVisibility = Math.min(width, height, throwVisibility)
|
this.throwVisibility = Math.min(width, height, throwVisibility)
|
||||||
this.container = container
|
this.container = container
|
||||||
this.simulateClick = simulateClick
|
this.clickOnTap = clickOnTap
|
||||||
|
this.triggerSVGClicks = triggerSVGClicks
|
||||||
this.scale = startScale
|
this.scale = startScale
|
||||||
this.rotationDegrees = this.startRotationDegrees
|
this.rotationDegrees = this.startRotationDegrees
|
||||||
this.transformOrigin = transformOrigin
|
this.transformOrigin = transformOrigin
|
||||||
@ -1107,7 +1110,8 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
rotation: this.startRotationDegrees,
|
rotation: this.startRotationDegrees,
|
||||||
transformOrigin: transformOrigin
|
transformOrigin: transformOrigin
|
||||||
}
|
}
|
||||||
|
this.tapNodes = new Map()
|
||||||
|
this.allowClickDistance = allowClickDistance
|
||||||
|
|
||||||
// For tweenlite we need initial values in _gsTransform
|
// For tweenlite we need initial values in _gsTransform
|
||||||
TweenLite.set(element, this.initialValues)
|
TweenLite.set(element, this.initialValues)
|
||||||
@ -1118,15 +1122,13 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
}
|
}
|
||||||
this.resizeButton = null
|
this.resizeButton = null
|
||||||
if (resizable) {
|
if (resizable) {
|
||||||
let button = document.createElement("div")
|
let button = document.createElement('div')
|
||||||
button.style.position = "absolute"
|
button.style.position = 'absolute'
|
||||||
button.style.right = "0px"
|
button.style.right = '0px'
|
||||||
button.style.bottom = "0px"
|
button.style.bottom = '0px'
|
||||||
button.style.width = "50px";
|
button.style.width = '50px'
|
||||||
button.style.height = "50px";
|
button.style.height = '50px'
|
||||||
// button.style.borderRadius = "100% 0px 0px 0px";
|
button.className = 'interactiveElement'
|
||||||
// button.style.background = this.element.style.backgroundColor
|
|
||||||
button.className = "interactiveElement"
|
|
||||||
this.element.appendChild(button)
|
this.element.appendChild(button)
|
||||||
|
|
||||||
button.addEventListener('pointerdown', (e) => {
|
button.addEventListener('pointerdown', (e) => {
|
||||||
@ -1142,6 +1144,25 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
})
|
})
|
||||||
this.resizeButton = button
|
this.resizeButton = button
|
||||||
}
|
}
|
||||||
|
if (clickOnTap) {
|
||||||
|
/* Since the tap triggers a synthetic click event
|
||||||
|
we must prevent the original trusted click event which
|
||||||
|
is also dispatched by the system.
|
||||||
|
*/
|
||||||
|
element.addEventListener('click', event => {
|
||||||
|
/* Currently we cannot send synthesized click events to SVG elements without unwanted side effects.
|
||||||
|
Therefore we make an exception and let the original click event through.
|
||||||
|
*/
|
||||||
|
if (event.target.ownerSVGElement) {
|
||||||
|
if (this.triggerSVGClicks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.isTrusted) {
|
||||||
|
Events.stop(event)
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
container.add(this)
|
container.add(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1301,45 +1322,109 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ })
|
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ })
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVideo(element) {
|
onTap(event, interaction, point) {
|
||||||
if (element.paused) {
|
|
||||||
element.play()
|
if (this.clickOnTap) {
|
||||||
} else {
|
let directNode = document.elementFromPoint(event.clientX, event.clientY)
|
||||||
element.pause()
|
console.log("onTap", event)
|
||||||
|
if (this.isClickable(directNode)) {
|
||||||
|
directNode.click()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let nearestNode = this.nearestClickable(event)
|
||||||
|
if (this.isClickable(nearestNode)) {
|
||||||
|
/* 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.
|
||||||
|
Therefore we use the original click event (see constructor).
|
||||||
|
*/
|
||||||
|
if (nearestNode.tagName == 'svg') {
|
||||||
|
let handler = this.tapNodes.get(nearestNode)
|
||||||
|
console.log("Clicking beneath SVG: to be done", handler)
|
||||||
|
if (this.triggerSVGClicks)
|
||||||
|
nearestNode.dispatchEvent(new Event('click'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("nearestNode clicked")
|
||||||
|
nearestNode.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTap(event, interaction, point) {
|
/**
|
||||||
if (this.simulateClick) {
|
* Adds a click or tap behavior to the node. Uses
|
||||||
let p = Points.fromPageToNode(this.element, point)
|
* either the scatter clickOnTap version which requires click handlers
|
||||||
let iframe = this.element.querySelector('iframe')
|
* or uses the hammer.js driven tap handler.
|
||||||
if (iframe) {
|
*
|
||||||
let doc = iframe.contentWindow.document
|
* @param {*} node
|
||||||
let element = doc.elementFromPoint(p.x, p.y)
|
* @param {*} handler
|
||||||
if (element == null) {
|
* @memberof DOMScatter
|
||||||
return
|
*/
|
||||||
|
|
||||||
|
addTapListener(node, handler) {
|
||||||
|
if (this.clickOnTap) {
|
||||||
|
node.addEventListener('click', handler)
|
||||||
|
this.tapNodes.set(node, handler)
|
||||||
}
|
}
|
||||||
switch (element.tagName) {
|
else {
|
||||||
case 'VIDEO':
|
InteractionMapper.on('tap', node, handler)
|
||||||
console.log(element.currentSrc)
|
|
||||||
if (PopupMenu) {
|
|
||||||
PopupMenu.open(
|
|
||||||
{
|
|
||||||
Fullscreen: () =>
|
|
||||||
window.open(element.currentSrc),
|
|
||||||
Play: () => element.play()
|
|
||||||
},
|
|
||||||
{ x, y }
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
this.toggleVideo(element)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
element.click()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isClickable(node) {
|
||||||
|
if (node == null)
|
||||||
|
return false
|
||||||
|
if (node.tagName == 'A')
|
||||||
|
return true
|
||||||
|
if (node.hasAttribute("onclick"))
|
||||||
|
return true
|
||||||
|
if (this.tapNodes.has(node))
|
||||||
|
return true
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of all clickable nodes.
|
||||||
|
* Unfortunately we cannot search for all nodes with an attached 'click' event listener
|
||||||
|
* See https://stackoverflow.com/questions/11455515/how-to-check-whether-dynamically-attached-event-listener-exists-or-not
|
||||||
|
* Therefore we can only detect the following standard cases:
|
||||||
|
* I. All clickable objects like clickables
|
||||||
|
* II. Objects that have been attached a click handler by the scatter itself via
|
||||||
|
*/
|
||||||
|
clickableNodes() {
|
||||||
|
let result = []
|
||||||
|
for (let node of this.element.querySelectorAll("*")) {
|
||||||
|
if (this.isClickable(node))
|
||||||
|
result.push(node)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
nearestClickable(event) {
|
||||||
|
let element = this.element
|
||||||
|
let clickables = this.clickableNodes()
|
||||||
|
let globalClick = (event.center) ? event.center : { x: event.x, y: event.y }
|
||||||
|
let localClick = Points.fromPageToNode(element, globalClick)
|
||||||
|
|
||||||
|
let clickRects = clickables.map(link => {
|
||||||
|
let rect = link.getBoundingClientRect()
|
||||||
|
let topLeft = Points.fromPageToNode(element, rect)
|
||||||
|
let center = Points.fromPageToNode(element, { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 })
|
||||||
|
return { x: topLeft.x, y: topLeft.y, width: rect.width, height: rect.height, center, link }
|
||||||
|
})
|
||||||
|
|
||||||
|
let distances = []
|
||||||
|
clickRects.forEach(rect => {
|
||||||
|
let distance = Points.distanceToRect(localClick, rect)
|
||||||
|
distances.push(parseInt(distance))
|
||||||
|
})
|
||||||
|
|
||||||
|
let closestClickIndex = distances.indexOf(Math.min(...distances))
|
||||||
|
let closestClickable = clickables[closestClickIndex]
|
||||||
|
if (distances[closestClickIndex] < this.allowClickDistance) {
|
||||||
|
return closestClickable
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
isDescendant(parent, child) {
|
isDescendant(parent, child) {
|
||||||
@ -1378,42 +1463,37 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resizeAfterTransform(zoom) {
|
resizeAfterTransform(zoom) {
|
||||||
// let w = this.width * this.scale
|
|
||||||
// let h = this.height * this.scale
|
|
||||||
// TweenLite.set(this.element, { width: w, height: h })
|
|
||||||
if (this.onResize) {
|
if (this.onResize) {
|
||||||
|
let w = this.width * this.scale
|
||||||
|
let h = this.height * this.scale
|
||||||
let event = new ResizeEvent(this, { width: w, height: h })
|
let event = new ResizeEvent(this, { width: w, height: h })
|
||||||
this.onResize(event)
|
this.onResize(event)
|
||||||
}
|
}
|
||||||
if (this.resizeButton != null) {
|
|
||||||
// this.resizeButton.style.width = 50/this.scale+"px"
|
|
||||||
// this.resizeButton.style.height = 50/this.scale+"px"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startResize(e) {
|
startResize(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
let event = new CustomEvent('resizeStarted')
|
let event = new CustomEvent('resizeStarted')
|
||||||
|
|
||||||
let oldPostition = {x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top};
|
let oldPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top }
|
||||||
this.bringToFront()
|
this.bringToFront()
|
||||||
|
|
||||||
this.element.style.transformOrigin = "0% 0%"
|
this.element.style.transformOrigin = '0% 0%'
|
||||||
|
|
||||||
let newPostition = {x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top};
|
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top }
|
||||||
|
|
||||||
let offset = Points.subtract(oldPostition, newPostition)
|
let offset = Points.subtract(oldPostition, newPostition)
|
||||||
|
|
||||||
this.oldX = e.clientX
|
this.oldX = e.clientX
|
||||||
this.oldY = e.clientY
|
this.oldY = e.clientY
|
||||||
|
|
||||||
e.target.setAttribute('resizing', "true")
|
e.target.setAttribute('resizing', 'true')
|
||||||
e.target.setPointerCapture(e.pointerId)
|
e.target.setPointerCapture(e.pointerId)
|
||||||
|
|
||||||
TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } })
|
TweenLite.to(this.element, 0, { css: { left: '+=' + offset.x + 'px' } })
|
||||||
TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } })
|
TweenLite.to(this.element, 0, { css: { top: '+=' + offset.y + 'px' } })
|
||||||
|
|
||||||
this.element.dispatchEvent(event);
|
this.element.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(e) {
|
resize(e) {
|
||||||
@ -1422,7 +1502,7 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
let rotation = Angle.radian2degree(this.rotation)
|
let rotation = Angle.radian2degree(this.rotation)
|
||||||
rotation = (rotation + 360) % 360
|
rotation = (rotation + 360) % 360
|
||||||
let event = new CustomEvent('resized')
|
let event = new CustomEvent('resized')
|
||||||
if (e.target.getAttribute('resizing') == "true") {
|
if (e.target.getAttribute('resizing') == 'true') {
|
||||||
|
|
||||||
let deltaX = (e.clientX - this.oldX)
|
let deltaX = (e.clientX - this.oldX)
|
||||||
let deltaY = (e.clientY - this.oldY)
|
let deltaY = (e.clientY - this.oldY)
|
||||||
@ -1439,13 +1519,13 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected))
|
let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected))
|
||||||
let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected))
|
let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected))
|
||||||
|
|
||||||
if ((this.element.offsetWidth + resizeW) / this.scale > this.width * 0.5 / this.scale && (this.element.offsetHeight + resizeH) / this.scale > this.height * 0.3 / this.scale) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale });
|
if ((this.element.offsetWidth + resizeW) / this.scale > this.width * 0.5 / this.scale && (this.element.offsetHeight + resizeH) / this.scale > this.height * 0.3 / this.scale) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale })
|
||||||
|
|
||||||
this.oldX = e.clientX
|
this.oldX = e.clientX
|
||||||
this.oldY = e.clientY
|
this.oldY = e.clientY
|
||||||
this.onResizing()
|
this.onResizing()
|
||||||
|
|
||||||
this.element.dispatchEvent(event);
|
this.element.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1453,17 +1533,17 @@ export class DOMScatter extends AbstractScatter {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
let event = new CustomEvent('resizeEnded')
|
let event = new CustomEvent('resizeEnded')
|
||||||
let oldPostition = {x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top};
|
let oldPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top }
|
||||||
this.element.style.transformOrigin = "50% 50%"
|
this.element.style.transformOrigin = '50% 50%'
|
||||||
let newPostition = {x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top};
|
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top }
|
||||||
let offset = Points.subtract(oldPostition, newPostition)
|
let offset = Points.subtract(oldPostition, newPostition)
|
||||||
|
|
||||||
TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } })
|
TweenLite.to(this.element, 0, { css: { left: '+=' + offset.x + 'px' } })
|
||||||
TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } })
|
TweenLite.to(this.element, 0, { css: { top: '+=' + offset.y + 'px' } })
|
||||||
|
|
||||||
e.target.setAttribute('resizing', "false")
|
e.target.setAttribute('resizing', 'false')
|
||||||
|
|
||||||
this.element.dispatchEvent(event);
|
this.element.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,6 +402,13 @@ export class Points {
|
|||||||
return Math.sqrt(dx * dx + dy * dy)
|
return Math.sqrt(dx * dx + dy * dy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distance == 0.0 indicates an inside relation.
|
||||||
|
static distanceToRect(p, r) {
|
||||||
|
var cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
|
||||||
|
var cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
|
||||||
|
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
|
||||||
|
}
|
||||||
|
|
||||||
static fromPageToNode(element, p) {
|
static fromPageToNode(element, p) {
|
||||||
// if (window.webkitConvertPointFromPageToNode) {
|
// if (window.webkitConvertPointFromPageToNode) {
|
||||||
// return window.webkitConvertPointFromPageToNode(element,
|
// return window.webkitConvertPointFromPageToNode(element,
|
||||||
|
85
package-lock.json
generated
85
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "iwmlib",
|
"name": "iwmlib",
|
||||||
"version": "1.0.10",
|
"version": "1.0.15",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -184,9 +184,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/pixi.js": {
|
"@types/pixi.js": {
|
||||||
"version": "4.8.7",
|
"version": "4.8.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/pixi.js/-/pixi.js-4.8.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/pixi.js/-/pixi.js-4.8.8.tgz",
|
||||||
"integrity": "sha512-SuaeAVDWNbvVzg+ipVrNzVxMDZHaa/MRNT/+Y270sYp/qxfB31KC1wEt7KDVNq9Ac/pRdSDrxVDcLDUaYn0aVg=="
|
"integrity": "sha512-5wmLnmL3foK/rqYMrrEM/3DxEwvwxJaP73RyqY8aMqq8zUm6CBlmc+12RIBH6iR/RHqU76XL238vWWJV1IN/zw=="
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "5.7.3",
|
"version": "5.7.3",
|
||||||
@ -194,9 +194,9 @@
|
|||||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
|
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
|
||||||
},
|
},
|
||||||
"agent-base": {
|
"agent-base": {
|
||||||
"version": "4.2.1",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
||||||
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
|
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-promisify": "^5.0.0"
|
"es6-promisify": "^5.0.0"
|
||||||
}
|
}
|
||||||
@ -1049,9 +1049,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es6-promise": {
|
"es6-promise": {
|
||||||
"version": "4.2.6",
|
"version": "4.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||||
"integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q=="
|
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||||
},
|
},
|
||||||
"es6-promisify": {
|
"es6-promisify": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
@ -1430,13 +1430,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "8.0.1",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
"integrity": "sha512-W+XLrggcDzlle47X/XnS7FXrXu9sDo+Ze9zpndeBxdgv88FHLm1HtmkhEwavruS6koanBjp098rUpHs65EmG7A==",
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
"universalify": "^0.1.0"
|
"universalify": "^0.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-mkdirp-stream": {
|
"fs-mkdirp-stream": {
|
||||||
@ -2502,9 +2509,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2770,6 +2777,14 @@
|
|||||||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"jsonfile": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||||
|
"requires": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"just-debounce": {
|
"just-debounce": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
|
||||||
@ -2962,9 +2977,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mime": {
|
"mime": {
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
|
||||||
"integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw=="
|
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
|
||||||
},
|
},
|
||||||
"mini-signals": {
|
"mini-signals": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
@ -3416,22 +3431,22 @@
|
|||||||
"integrity": "sha1-i0tcQzsx5Bm8N53FZc4bg1qRs3I="
|
"integrity": "sha1-i0tcQzsx5Bm8N53FZc4bg1qRs3I="
|
||||||
},
|
},
|
||||||
"pixi-particles": {
|
"pixi-particles": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pixi-particles/-/pixi-particles-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pixi-particles/-/pixi-particles-4.1.1.tgz",
|
||||||
"integrity": "sha512-By5470dTkHCTrM3T2xrFg9YRH6XK9BNpZjTZmWNzsHmyPbWB+4E/fg66b1DKFQ/vgez6xQm8wfHWcOpNrHWWHg=="
|
"integrity": "sha512-R/vnqXzD2X4v4mSi3zJE81i1vGWaaZSDI/ImaZr8G4E0qBq2+OxB97Kb9WVWd7BlLFj4wR09VGKm7e5sQGQy4Q=="
|
||||||
},
|
},
|
||||||
"pixi-projection": {
|
"pixi-projection": {
|
||||||
"version": "0.2.7",
|
"version": "0.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/pixi-projection/-/pixi-projection-0.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/pixi-projection/-/pixi-projection-0.2.8.tgz",
|
||||||
"integrity": "sha512-zrMUs2lDmPQX8AHXr14/MNGKXRlqr/XBjyI4Pm9fwz2sxMkz+QRLCa1weUrHXbfv1e6GjUe4tJ8KgqeNuRkc3w==",
|
"integrity": "sha512-3K06VHVDNm0rnCd72HhH9cZF/davWZXIMcJMFzwAuIV9Io2ExQrUv3Eauri4A682jsoriM+stHgm+6n75Ps5lg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/pixi.js": "^4.7.1"
|
"@types/pixi.js": "^4.7.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pixi.js": {
|
"pixi.js": {
|
||||||
"version": "4.8.7",
|
"version": "4.8.8",
|
||||||
"resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-4.8.7.tgz",
|
"resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-4.8.8.tgz",
|
||||||
"integrity": "sha512-mx7YbHPkkWoj8FT3qBMkieAjBuuJ4yZWU7rq9NnCSUGpNrVlocrW179xrJQPVR2Q7JZ73ZGTwH7NOUZ9wgh7wA==",
|
"integrity": "sha512-wQzuLAWSMfV+x2guL5jZBp37pwCmYXHiTXG7ZWXu4E/5IsC9xozwmOfLeCNEyPzlyucOgxAx/HS+tLqxWPYX7Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bit-twiddle": "^1.0.2",
|
"bit-twiddle": "^1.0.2",
|
||||||
"earcut": "^2.1.4",
|
"earcut": "^2.1.4",
|
||||||
@ -3501,9 +3516,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"puppeteer": {
|
"puppeteer": {
|
||||||
"version": "1.17.0",
|
"version": "1.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.18.1.tgz",
|
||||||
"integrity": "sha512-3EXZSximCzxuVKpIHtyec8Wm2dWZn1fc5tQi34qWfiUgubEVYHjUvr0GOJojqf3mifI6oyKnCdrGxaOI+lWReA==",
|
"integrity": "sha512-luUy0HPSuWPsPZ1wAp6NinE0zgetWtudf5zwZ6dHjMWfYpTQcmKveFRox7VBNhQ98OjNA9PQ9PzQyX8k/KrxTg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"extract-zip": "^1.6.6",
|
"extract-zip": "^1.6.6",
|
||||||
@ -3524,9 +3539,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -34,10 +34,10 @@
|
|||||||
"optimal-select": "^4.0.1",
|
"optimal-select": "^4.0.1",
|
||||||
"pixi-compressed-textures": "^1.1.8",
|
"pixi-compressed-textures": "^1.1.8",
|
||||||
"pixi-filters": "^2.7.1",
|
"pixi-filters": "^2.7.1",
|
||||||
"pixi-particles": "^4.1.0",
|
"pixi-particles": "^4.1.1",
|
||||||
"pixi-projection": "^0.2.7",
|
"pixi-projection": "^0.2.8",
|
||||||
"pixi.js": "^4.8.7",
|
"pixi.js": "^4.8.8",
|
||||||
"propagating-hammerjs": "^1.4.6",
|
"propagating-hammerjs": "^1.4.6",
|
||||||
"puppeteer": "^1.16.0"
|
"puppeteer": "^1.18.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,4 +19,4 @@ export default [{
|
|||||||
watch: {
|
watch: {
|
||||||
clearScreen: false
|
clearScreen: false
|
||||||
}
|
}
|
||||||
}];
|
}]
|
||||||
|
Loading…
Reference in New Issue
Block a user