Browse Source

Removed setup from constructor to simplify specializations

obersalzberg
Uwe Oestermeier 1 year ago
parent
commit
808106262e
19 changed files with 3849 additions and 3456 deletions
  1. +2
    -0
      .eslintignore
  2. +35
    -0
      .eslintrc.json
  3. +1
    -0
      .gitignore
  4. +3
    -0
      .stylelintignore
  5. +11
    -0
      .stylelintrc.json
  6. +23
    -25
      css/doctest.css
  7. +16
    -16
      css/flipeffect.css
  8. +39
    -47
      css/index.css
  9. +3091
    -2992
      dist/iwmlib.js
  10. +254
    -156
      dist/iwmlib.pixi.js
  11. +2
    -2
      lib/flippable.html
  12. +33
    -31
      lib/flippable.js
  13. +48
    -40
      lib/interaction.js
  14. +46
    -4
      lib/scatter.html
  15. +183
    -103
      lib/scatter.js
  16. +7
    -0
      lib/utils.js
  17. +50
    -35
      package-lock.json
  18. +4
    -4
      package.json
  19. +1
    -1
      rollup.config.js

+ 2
- 0
.eslintignore View File

@ -0,0 +1,2 @@
dist/*
doc/out/*

+ 35
- 0
.eslintrc.json View 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
- 0
.gitignore View File

@ -79,3 +79,4 @@ typings/
# own
*.code-workspace
.history/
.vscode/

+ 3
- 0
.stylelintignore View File

@ -0,0 +1,3 @@
dist/*
doc/*
lib/*

+ 11
- 0
.stylelintrc.json View 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": []
}

+ 23
- 25
css/doctest.css View File

@ -1,12 +1,11 @@
html
{
padding: 0px;
font-size: 16px;
background: white;
font-family: Arial,sans-serif;
color: #000;
max-width: 932px;
margin:0 auto;
html {
padding: 0;
font-size: 16px;
background: white;
font-family: Arial, sans-serif;
color: #000;
max-width: 932px;
margin: 0 auto;
}
.grayBorder {
@ -24,10 +23,10 @@ html
}
.unselectable {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
body {
@ -44,28 +43,27 @@ canvas {
margin-left: 8px;
}
.intrinsic-container {
position: relative;
height: 0;
overflow: hidden;
position: relative;
height: 0;
overflow: hidden;
}
/* 16x9 Aspect Ratio */
.intrinsic-container-16x9 {
padding-bottom: 56.25%;
padding-bottom: 56.25%;
}
/* 4x3 Aspect Ratio */
.intrinsic-container-4x3 {
padding-bottom: 75%;
padding-bottom: 75%;
}
.intrinsic-container iframe {
position: absolute;
border: 0;
top:0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
border: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

+ 16
- 16
css/flipeffect.css View File

@ -1,6 +1,5 @@
.flipWrapper
{
.flipWrapper {
position: absolute;
top: 0;
left: 0;
@ -16,7 +15,8 @@
width: 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;
-khtml-user-drag: none;
-moz-user-drag: none;
@ -24,21 +24,21 @@
user-drag: none; */
}
.flipFace{
.flipFace {
box-shadow: 2px 2px 10px #000;
visibility: hidden;
}
.front{
.front {
width: 100%;
height: 100%;
position:absolute;
background-color:#333;
position: absolute;
background-color: #333;
}
.back{
background-color:#333;
position:absolute;
.back {
background-color: #333;
position: absolute;
border: 8px solid white;
}
@ -48,8 +48,8 @@
width: 44px;
height: 44px;
padding: 4px;
right: 0px;
top: 0px;
right: 0;
top: 0;
}
.infoBtn {
@ -58,8 +58,8 @@
width: 44px;
height: 44px;
padding: 4px;
right: 0px;
bottom: 0px;
right: 0;
bottom: 0;
}
.backBtn {
@ -68,6 +68,6 @@
width: 44px;
height: 44px;
padding: 4px;
right: 0px;
bottom: 0px;
right: 0;
bottom: 0;
}

+ 39
- 47
css/index.css View File

@ -1,19 +1,18 @@
html {
height: 100%;
width: 100%;
margin: 0px;
margin: 0;
}
body
{
margin: 0px;
padding: 0px;
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: sans-serif;
font-size: 22pt;
-webkit-tap-highlight-color: #ccc;
background-color: #DDD;
background-color: #ddd;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
@ -22,24 +21,22 @@ body
user-select: none;
-webkit-hyphens: auto;
hyphens: auto;
/* https://davidwalsh.name/font-smoothing */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h3
{
h3 {
color: white;
padding: 4px;
margin: 2px;
background-color: rgba(0, 0, 15, .5);
background-color: rgba(0, 0, 15, 0.5);
}
a { text-decoration: none; }
div.wrapper
{
div.wrapper {
overflow: hidden;
width: 100%;
height: 100%;
@ -49,29 +46,32 @@ div.wrapper
/* Color animation from https://www.tjvantoll.com/2012/02/20/css3-color-animations/ */
@-webkit-keyframes color_change {
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
}
@-moz-keyframes color_change {
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
}
@-ms-keyframes color_change {
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
}
@-o-keyframes color_change {
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
}
@keyframes color_change {
from { background-color:rgba(0, 0, 0, 0); }
to { background-color: red; }
from { background-color: rgba(0, 0, 0, 0); }
to { background-color: red; }
}
/*** CSS taken from https://medium.com/@jamesfuthey/simulating-the-creation-of-website-thumbnail-screenshots-using-iframes-7145269891db#.7v7fshos5 ***/
.thumbnail
{
.thumbnail {
position: relative;
-ms-zoom: 0.25;
-moz-transform: scale(0.25);
@ -82,9 +82,7 @@ div.wrapper
-webkit-transform-origin: 0 0;
}
.thumbnail:after
{
.thumbnail::after {
content: "";
display: block;
position: absolute;
@ -95,11 +93,14 @@ div.wrapper
jamesfuthey blog. Otherwise touches would go through on iPad. ***/
}
.thumbnail iframe
{
iframe {
pointer-events: none;
}
.thumbnail iframe {
width: 1024px;
height: 624px;
-webkit-animation-delay: 3s; /* Safari 4.0 - 8.0 */
-webkit-animation-delay: 3s; /* Safari 4.0 - 8.0 */
animation-delay: 3s;
-webkit-animation: color_change 1s infinite alternate;
-moz-animation: color_change 1s infinite alternate;
@ -108,22 +109,20 @@ div.wrapper
animation: color_change 1s infinite alternate;
}
.thumbnail-container
{
.thumbnail-container {
width: calc(1024px * 0.25);
height: calc(624px * 0.25);
display: inline-block;
overflow: hidden;
position: relative;
box-shadow: 2px 2px 10px #000;
color: #DDD;
color: #ddd;
}
div.preview
{
div.preview {
display: inline-block;
margin: 22px;
padding: 0px;
padding: 0;
color: #333;
font-size: 12pt;
text-align: center;
@ -131,8 +130,7 @@ div.preview
height: 196px;
}
div.title
{
div.title {
padding-top: 8px;
width: 256px;
height: 20px;
@ -140,10 +138,9 @@ div.title
overflow: hidden;
}
.container
{
margin: 0px;
padding: 0px;
.container {
margin: 0;
padding: 0;
border: 2pt #000;
min-height: 100%;
min-width: 100%;
@ -156,12 +153,7 @@ div.title
align-content: flex-end;
}
iframe {
pointer-events: none;
}
/** See https://github.com/electron/electron/issues/4420 */
::selection {
background: transparent;
}

+ 3091
- 2992
dist/iwmlib.js
File diff suppressed because it is too large
View File


+ 254
- 156
dist/iwmlib.pixi.js View File

@ -3263,6 +3263,13 @@
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) {
// if (window.webkitConvertPointFromPageToNode) {
// return window.webkitConvertPointFromPageToNode(element,
@ -4781,7 +4788,7 @@
}
}
/* globals Hammer, propagating */
/* eslint-disable no-unused-vars */
/** Interaction patterns
@ -4789,6 +4796,7 @@
*/
class IInteractionTarget extends Interface {
capture(event) {
return typeof true
}
@ -4992,10 +5000,10 @@
let d1 = Points.subtract(c1, p1);
let d2 = Points.subtract(c2, p2);
let cm = Points.mean(c1, c2);
// Using the mean leads to jumps between time slices with 3 and 2 fingers
// We use the mean of deltas instead
let delta = Points.mean(d1, d2);
let delta = Points.mean(d1, d2);
let zoom = 1.0;
let distance1 = Points.distance(p1, p2);
let distance2 = Points.distance(c1, c2);
@ -5199,7 +5207,6 @@
}
let result = false;
if (this.isTap(key)) {
this.registerTap(key, ended);
result = this.tapCounts.get(key) == 2;
}
@ -5313,7 +5320,9 @@
if (this.capturePointerEvents) {
try {
element.setPointerCapture(e.pointerId);
} catch (e) { }
} catch (e) {
console.warn('Cannot setPointerCapture');
}
}
this.onStart(e);
}
@ -5345,7 +5354,9 @@
if (this.capturePointerEvents) {
try {
element.releasePointerCapture(e.pointerId);
} catch (e) { }
} catch (e) {
console.warn('Cannot release pointer');
}
}
},
useCapture
@ -5389,7 +5400,7 @@
e => {
if (this.debug) console.log('pointerout', e.pointerId, e.pointerType, e.target);
if (e.target == element) {
this.onEnd(e);
this.onEnd(e);
}
},
useCapture);
@ -5492,9 +5503,8 @@
e => {
if (e.target == element) {
this.onEnd(e);
console.warn("Shouldn't happen: mouseout ends interaction");
console.warn('Shouldn\'t happen: mouseout ends interaction');
}
},
useCapture
);
@ -5594,39 +5604,42 @@
// 'targetTouches'
let result = {};
switch (event.constructor.name) {
case 'MouseEvent':
let buttons = event.buttons || event.which;
if (buttons) result['mouse'] = this.getPosition(event);
break
case 'PointerEvent':
result[event.pointerId.toString()] = this.getPosition(event);
break
case 'Touch':
let id =
case 'MouseEvent': {
let buttons = event.buttons || event.which;
if (buttons) result['mouse'] = this.getPosition(event);
break
}
case 'PointerEvent': {
result[event.pointerId.toString()] = this.getPosition(event);
break
}
case 'Touch': {
let id =
event.touchType === 'stylus'
? 'stylus'
: event.identifier.toString();
result[id] = this.getPosition(event);
break
// case 'TouchEvent':
// // Needs to be observed: Perhaps changedTouches are all we need. If so
// // we can remove the touchEventKey default parameter
// if (touchEventKey == 'all') {
// for(let t of event.targetTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// else {
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// break
default:
break
result[id] = this.getPosition(event);
break
}
// case 'TouchEvent':
// // Needs to be observed: Perhaps changedTouches are all we need. If so
// // we can remove the touchEventKey default parameter
// if (touchEventKey == 'all') {
// for(let t of event.targetTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// else {
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// break
default:
break
}
return result
}
@ -5654,7 +5667,7 @@
let point = extracted[key];
let updated = this.interaction.update(key, point);
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);
}
}
@ -5805,6 +5818,7 @@
* @param {object} [opts] - An options object. See the hammer documentation for more details.
*/
static on(types, elements, cb, opts = {}) {
opts = Object.assign({}, {
}, opts);
@ -6039,6 +6053,11 @@
window.Capabilities = Capabilities;
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.
*
@ -6088,7 +6107,7 @@
toString() {
return (
"Event('scatterTransformed', scale: " +
'Event(\'scatterTransformed\', scale: ' +
this.scale +
' about: ' +
this.about.x +
@ -6163,7 +6182,7 @@
// Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the
// onMove events
let velocity = { t: t, dt: dt, dx: delta.x / delta.number, dy: delta.y / delta.number};
let velocity = { t: t, dt: dt, dx: delta.x / delta.number, dy: delta.y / delta.number };
this.velocities.push(velocity);
while (this.velocities.length > buffer) {
this.velocities.shift();
@ -6302,7 +6321,8 @@
onThrowFinished = null,
scaleAutoClose = false,
scaleCloseThreshold = 0.10,
scaleCloseBuffer = 0.05
scaleCloseBuffer = 0.05,
maxRotation = Angle.degree2radian(5)
} = {}) {
if (rotationDegrees != null && rotation != null) {
throw new Error('Use rotationDegrees or rotation but not both')
@ -6334,6 +6354,7 @@
this.startScale = startScale; // Needed to reset object
this.minScale = minScale;
this.maxScale = maxScale;
this.maxRotation = maxRotation;
this.overdoScaling = overdoScaling;
this.translatable = translatable;
if (!translatable) {
@ -6345,6 +6366,7 @@
this.resizable = resizable;
this.mouseZoomFactor = mouseZoomFactor;
this.autoBringToFront = autoBringToFront;
this.dragging = false;
this.onTransform = onTransform != null ? [onTransform] : null;
this.onClose = onClose != null ? [onClose] : null;
@ -6379,10 +6401,15 @@
gesture(interaction) {
let delta = interaction.delta();
//console.log("gesture", delta)
if (delta != null) {
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;
}
}
@ -6451,7 +6478,7 @@
let stagePolygon = this.containerPolygon;
// UO: since keepOnStage is called in nextVelocity we need to
// ensure a return value
if (!stagePolygon) return { x: 0, y: 0}
if (!stagePolygon) return { x: 0, y: 0 }
let polygon = this.polygon;
let bounced = this.bouncing();
if (bounced) {
@ -6735,20 +6762,6 @@
}
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) {
@ -6915,7 +6928,9 @@
width = null, // required
height = null, // required
resizable = false,
simulateClick = false,
clickOnTap = false,
triggerSVGClicks = false,
allowClickDistance = 44,
verbose = true,
onResize = null,
touchAction = 'none',
@ -6966,7 +6981,8 @@
this.height = height;
this.throwVisibility = Math.min(width, height, throwVisibility);
this.container = container;
this.simulateClick = simulateClick;
this.clickOnTap = clickOnTap;
this.triggerSVGClicks = triggerSVGClicks;
this.scale = startScale;
this.rotationDegrees = this.startRotationDegrees;
this.transformOrigin = transformOrigin;
@ -6979,7 +6995,8 @@
rotation: this.startRotationDegrees,
transformOrigin: transformOrigin
};
this.tapNodes = new Map();
this.allowClickDistance = allowClickDistance;
// For tweenlite we need initial values in _gsTransform
TweenLite.set(element, this.initialValues);
@ -6990,15 +7007,13 @@
}
this.resizeButton = null;
if (resizable) {
let button = document.createElement("div");
button.style.position = "absolute";
button.style.right = "0px";
button.style.bottom = "0px";
button.style.width = "50px";
button.style.height = "50px";
// button.style.borderRadius = "100% 0px 0px 0px";
// button.style.background = this.element.style.backgroundColor
button.className = "interactiveElement";
let button = document.createElement('div');
button.style.position = 'absolute';
button.style.right = '0px';
button.style.bottom = '0px';
button.style.width = '50px';
button.style.height = '50px';
button.className = 'interactiveElement';
this.element.appendChild(button);
button.addEventListener('pointerdown', (e) => {
@ -7014,6 +7029,25 @@
});
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);
}
@ -7173,47 +7207,111 @@
TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ });
}
toggleVideo(element) {
if (element.paused) {
element.play();
} else {
element.pause();
}
}
onTap(event, interaction, point) {
if (this.simulateClick) {
let p = Points.fromPageToNode(this.element, point);
let iframe = this.element.querySelector('iframe');
if (iframe) {
let doc = iframe.contentWindow.document;
let element = doc.elementFromPoint(p.x, p.y);
if (element == null) {
return
}
switch (element.tagName) {
case 'VIDEO':
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();
if (this.clickOnTap) {
let directNode = document.elementFromPoint(event.clientX, event.clientY);
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();
}
}
}
}
/**
* Adds a click or tap behavior to the node. Uses
* either the scatter clickOnTap version which requires click handlers
* or uses the hammer.js driven tap handler.
*
* @param {*} node
* @param {*} handler
* @memberof DOMScatter
*/
addTapListener(node, handler) {
if (this.clickOnTap) {
node.addEventListener('click', handler);
this.tapNodes.set(node, handler);
}
else {
InteractionMapper$1.on('tap', node, handler);
}
}
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) {
let node = child.parentNode;
while (node != null) {
@ -7250,37 +7348,35 @@
}
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) {
let w = this.width * this.scale;
let h = this.height * this.scale;
let event = new ResizeEvent(this, { width: w, height: h });
this.onResize(event);
}
if (this.resizeButton != null) ;
}
startResize(e) {
e.preventDefault();
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.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);
this.oldX = e.clientX;
this.oldY = e.clientY;
e.target.setAttribute('resizing', "true");
e.target.setAttribute('resizing', 'true');
e.target.setPointerCapture(e.pointerId);
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: { left: '+=' + offset.x + 'px' } });
TweenLite.to(this.element, 0, { css: { top: '+=' + offset.y + 'px' } });
this.element.dispatchEvent(event);
}
@ -7291,7 +7387,7 @@
let rotation = Angle.radian2degree(this.rotation);
rotation = (rotation + 360) % 360;
let event = new CustomEvent('resized');
if (e.target.getAttribute('resizing') == "true") {
if (e.target.getAttribute('resizing') == 'true') {
let deltaX = (e.clientX - this.oldX);
let deltaY = (e.clientY - this.oldY);
@ -7308,7 +7404,7 @@
let resizeW = r * Math.cos(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.oldY = e.clientY;
@ -7322,15 +7418,15 @@
e.preventDefault();
let event = new CustomEvent('resizeEnded');
let oldPostition = {x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top};
this.element.style.transformOrigin = "50% 50%";
let newPostition = {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%';
let newPostition = { x: this.element.getBoundingClientRect().left, y: this.element.getBoundingClientRect().top };
let offset = Points.subtract(oldPostition, newPostition);
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: { left: '+=' + offset.x + '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);
}
@ -7367,12 +7463,12 @@
this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight;
this.addedNode = null;
console.log({
width,
height,
maxWidth,
maxHeight,
});
}
@ -7401,6 +7497,7 @@
translatable = true,
scalable = true,
rotatable = true,
clickOnTap = false,
onFront = null,
onBack = null,
onClose = null,
@ -7420,6 +7517,7 @@
this.translatable = translatable;
this.scalable = scalable;
this.rotatable = rotatable;
this.clickOnTap = clickOnTap;
this.onFrontFlipped = onFront;
this.onBackFlipped = onBack;
this.onClose = onClose;
@ -7474,7 +7572,8 @@
translatable: this.translatable,
scalable: this.scalable,
rotatable: this.rotatable,
overdoScaling: this.overdoScaling
overdoScaling: this.overdoScaling,
clickOnTap: this.clickOnTap
}
);
@ -7483,7 +7582,6 @@
}
if (this.closeOnMinScale) {
const removeOnMinScale = function () {
if (scatter.scale <= scatter.minScale) {
this.flippable.close();
@ -7498,11 +7596,7 @@
scatter.onTransform.splice(callbackIdx, 1);
}
}
}.bind(this);
scatter.addTransformEventCallback(removeOnMinScale);
}
@ -7529,6 +7623,7 @@
}
setupFlippable(flippable, loader) {
console.log("setupFlippable", loader.wantedWidth);
flippable.wantedWidth = loader.wantedWidth;
flippable.wantedHeight = loader.wantedHeight;
flippable.wantedScale = loader.scale;
@ -7538,8 +7633,11 @@
}
start({ targetCenter = null } = {}) {
console.log('DOMFlip.start', targetCenter);
if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter });
console.log("DOMFlip.start", targetCenter);
if (this.preloadBack) {
this.flippable.start({ duration: this.flipDuration, targetCenter });
}
else {
let back = this.cardWrapper.querySelector('.back');
let flippable = this.flippable;
@ -7588,10 +7686,16 @@
this.onRemoved = flip.onRemoved;
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.fadeDuration = flip.fadeDuration;
scatter.addTransformEventCallback(this.scatterTransformed.bind(this));
console.log('lib.DOMFlippable', 5000);
TweenLite.set(this.element, { perspective: 5000 });
TweenLite.set(this.card, { transformStyle: 'preserve-3d' });
TweenLite.set(this.back, { rotationY: -180 });
@ -7604,16 +7708,22 @@
this.backBtn = element.querySelector('.backBtn');
this.closeBtn = element.querySelector('.closeBtn');
/* 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);
}
if (this.backBtn) {
InteractionMapper$1.on('tap', this.backBtn, event => this.start());
scatter.addTapListener(this.backBtn, event => {
this.start();
});
}
if (this.closeBtn) {
InteractionMapper$1.on('tap', this.closeBtn, event => this.close());
scatter.addTapListener(this.closeBtn, event => {
this.close();
});
this.enable(this.closeBtn);
}
this.scaleButtons();
@ -7663,18 +7773,6 @@
}
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], {
scale: this.buttonScale
});
@ -7687,6 +7785,7 @@
clickInfo() {
this.bringToFront();
console.log("clickInfo");
this.infoBtn.click();
}
@ -7728,8 +7827,6 @@
}
}
enable(button) {
this.show(button, this.fadeDuration);
if (button) {
@ -7785,15 +7882,15 @@
let x = this.flipped ? xx : this.startX;
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;
console.log(this.flipDuration);
console.log("start", this.flipDuration);
TweenLite.to(this.card, this.flipDuration, {
rotationY: targetY,
ease: Power1.easeOut,
transformOrigin: '50% 50%',
onUpdate,
onComplete: e => {
console.log("start end", this.flipDuration);
if (this.flipped) {
//this.hide(this.front)
this.enable(this.backBtn);
@ -7826,6 +7923,7 @@
force3D: true
});
console.log("start 2", this.wantedWidth, this.startWidth, {w, h});
// See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/
TweenLite.to(this.element, this.flipDuration / 2, {
scale: targetScale,

+ 2
- 2
lib/flippable.html View File

@ -66,13 +66,13 @@ templates.
</main>
<script class="doctest">
let scatterContainer = new DOMScatterContainer(main)
let scatterContainer = new DOMScatterContainer(main, {stopEvents: false})
if (Capabilities.supportsTemplate()) {
let flip = new DOMFlip(scatterContainer,
flipTemplate,
new ImageLoader('./examples/king.jpeg'),
new ImageLoader('./examples/women.jpeg'),
{ onUpdate: e => console.log(e)})
{ clickOnTap: true})
flip.load().then((flip) => {
flip.centerAt({ x: 150, y: 120})
})

+ 33
- 31
lib/flippable.js View File

@ -31,12 +31,12 @@ export class CardLoader {
this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight
this.addedNode = null
console.log({
width,
height,
maxWidth,
maxHeight,
})
}
@ -199,6 +199,7 @@ export class DOMFlip {
translatable = true,
scalable = true,
rotatable = true,
clickOnTap = false,
onFront = null,
onBack = null,
onClose = null,
@ -218,6 +219,7 @@ export class DOMFlip {
this.translatable = translatable
this.scalable = scalable
this.rotatable = rotatable
this.clickOnTap = clickOnTap
this.onFrontFlipped = onFront
this.onBackFlipped = onBack
this.onClose = onClose
@ -272,7 +274,8 @@ export class DOMFlip {
translatable: this.translatable,
scalable: this.scalable,
rotatable: this.rotatable,
overdoScaling: this.overdoScaling
overdoScaling: this.overdoScaling,
clickOnTap: this.clickOnTap
}
)
@ -281,7 +284,6 @@ export class DOMFlip {
}
if (this.closeOnMinScale) {
const removeOnMinScale = function () {
if (scatter.scale <= scatter.minScale) {
this.flippable.close()
@ -296,11 +298,7 @@ export class DOMFlip {
scatter.onTransform.splice(callbackIdx, 1)
}
}
}.bind(this)
scatter.addTransformEventCallback(removeOnMinScale)
}
@ -327,6 +325,7 @@ export class DOMFlip {
}
setupFlippable(flippable, loader) {
console.log("setupFlippable", loader.wantedWidth)
flippable.wantedWidth = loader.wantedWidth
flippable.wantedHeight = loader.wantedHeight
flippable.wantedScale = loader.scale
@ -336,8 +335,11 @@ export class DOMFlip {
}
start({ targetCenter = null } = {}) {
console.log('DOMFlip.start', targetCenter)
if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter })
console.log("DOMFlip.start", targetCenter)
if (this.preloadBack) {
this.flippable.start({ duration: this.flipDuration, targetCenter })
}
else {
let back = this.cardWrapper.querySelector('.back')
let flippable = this.flippable
@ -386,10 +388,16 @@ export class DOMFlippable {
this.onRemoved = flip.onRemoved
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.fadeDuration = flip.fadeDuration
scatter.addTransformEventCallback(this.scatterTransformed.bind(this))
console.log('lib.DOMFlippable', 5000)
TweenLite.set(this.element, { perspective: 5000 })
TweenLite.set(this.card, { transformStyle: 'preserve-3d' })
TweenLite.set(this.back, { rotationY: -180 })
@ -402,16 +410,22 @@ export class DOMFlippable {
this.backBtn = element.querySelector('.backBtn')
this.closeBtn = element.querySelector('.closeBtn')
/* 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)
}
if (this.backBtn) {
InteractionMapper.on('tap', this.backBtn, event => this.start())
scatter.addTapListener(this.backBtn, event => {
this.start()
})
}
if (this.closeBtn) {
InteractionMapper.on('tap', this.closeBtn, event => this.close())
scatter.addTapListener(this.closeBtn, event => {
this.close()
})
this.enable(this.closeBtn)
}
this.scaleButtons()
@ -461,18 +475,6 @@ export class DOMFlippable {
}
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], {
scale: this.buttonScale
})
@ -485,6 +487,7 @@ export class DOMFlippable {
clickInfo() {
this.bringToFront()
console.log("clickInfo")
this.infoBtn.click()
}
@ -526,8 +529,6 @@ export class DOMFlippable {
}
}
enable(button) {
this.show(button, this.fadeDuration)
if (button) {
@ -583,15 +584,15 @@ export class DOMFlippable {
let x = this.flipped ? xx : this.startX
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
console.log(this.flipDuration)
console.log("start", this.flipDuration)
TweenLite.to(this.card, this.flipDuration, {
rotationY: targetY,
ease: Power1.easeOut,
transformOrigin: '50% 50%',
onUpdate,
onComplete: e => {
console.log("start end", this.flipDuration)
if (this.flipped) {
//this.hide(this.front)
this.enable(this.backBtn)
@ -624,6 +625,7 @@ export class DOMFlippable {
force3D: true
})
console.log("start 2", this.wantedWidth, this.startWidth, {w, h})