Merge branch 'main' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib into main

* 'main' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib:
  Removed console.logs
  Fixed timestamp name mismatch.
  Updated iwmlib and removed log statement.
  Added pluggable throw behavior.
  Replaced requestAnimationFrame with setInterval
  Moved throws to a single loop.
  Added cancelAnimationFrame
  update code block to reflect current state
  Fixed html double code
  Fixed problem with scrolling text in card drawers.
  added fix for viewBox rotation bug
  fixed rollup
  documented/ solved svg rotation bug

# Conflicts:
#	package.json
This commit is contained in:
Uwe Oestermeier 2024-02-12 14:15:58 +01:00
commit 57d6e8b461
10 changed files with 1049 additions and 845 deletions

0
bin/browser.sh Executable file → Normal file
View File

File diff suppressed because it is too large Load Diff

196
dist/iwmlib.js vendored
View File

@ -3213,6 +3213,40 @@
* *
* @constructor * @constructor
*/ */
class RequestFrameThrower {
/** Implemenents the standard throw behavior. */
animateThrow(throwable) {
/*** Calls the animateThrow method in sync with the display link. */
requestAnimationFrame(throwable.animateThrow.bind(throwable));
}
}
class TimeoutThrower {
constructor(delay=20) {
this.throwables = new Set();
this.delay = delay;
setTimeout(this.animateStep.bind(this), this.delay);
}
animateThrow(throwable) {
this.throwables.add(throwable);
}
animateStep() {
let active = [...this.throwables];
this.throwables.clear();
for(let throwable of active) {
throwable.animateThrow();
}
setTimeout(this.animateStep.bind(this), this.delay);
}
}
let thrower = new RequestFrameThrower();
class Throwable { class Throwable {
constructor({ constructor({
movableX = true, movableX = true,
@ -3220,7 +3254,7 @@
throwVisibility = 44, throwVisibility = 44,
throwDamping = 0.95, throwDamping = 0.95,
autoThrow = true, autoThrow = true,
onThrowFinished = null, onThrowFinished = null
} = {}) { } = {}) {
this.movableX = movableX; this.movableX = movableX;
this.movableY = movableY; this.movableY = movableY;
@ -3229,9 +3263,16 @@
this.autoThrow = autoThrow; this.autoThrow = autoThrow;
this.velocities = []; this.velocities = [];
this.velocity = null; this.velocity = null;
this.timestamp = null; this.lastframe = null;
this.onThrowFinished = onThrowFinished; this.onThrowFinished = onThrowFinished;
//console.log("onThrowFinished", onThrowFinished) }
static defaultThrow() {
thrower = new RequestFrameThrower();
}
static timeoutThrow() {
thrower = new TimeoutThrower();
} }
observeVelocity() { observeVelocity() {
@ -3253,7 +3294,7 @@
t: t, t: t,
dt: dt, dt: dt,
dx: delta.x / number, dx: delta.x / number,
dy: delta.y / number, dy: delta.y / number
}; };
this.velocities.push(velocity); this.velocities.push(velocity);
while (this.velocities.length > buffer) { while (this.velocities.length > buffer) {
@ -3262,8 +3303,23 @@
} }
} }
addTestVelocity(delta, dt=20, buffer = 5) {
let t = performance.now();
this.lastframe = t;
let velocity = {
t: t,
dt: dt,
dx: delta.x ,
dy: delta.y
};
for(let i=0; i<buffer; i++) {
velocity.t += dt;
this.velocities.push(velocity);
}
}
meanVelocity(milliseconds = 30) { meanVelocity(milliseconds = 30) {
this.addVelocity({ x: 0, y: 0, number: 1 }); // this.addVelocity({ x: 0, y: 0, number: 1 })
let sum = { x: 0, y: 0 }; let sum = { x: 0, y: 0 };
let count = 0; let count = 0;
let t = 0; let t = 0;
@ -3284,6 +3340,7 @@
killAnimation() { killAnimation() {
this.velocity = null; this.velocity = null;
this.velocities = []; this.velocities = [];
this.lastframe = null;
} }
startThrow() { startThrow() {
@ -3305,6 +3362,10 @@
return dt return dt
} }
recurseAnimateThrow() {
ThrowableObjects.add(this);
}
animateThrow(time) { animateThrow(time) {
if (this.velocity != null) { if (this.velocity != null) {
let dt = this._throwDeltaTime(); let dt = this._throwDeltaTime();
@ -3315,7 +3376,7 @@
if (nextLength > prevLength) { if (nextLength > prevLength) {
let factor = nextLength / prevLength; let factor = nextLength / prevLength;
next = Points$1.multiplyScalar(next, 1 / factor); next = Points$1.multiplyScalar(next, 1 / factor);
console.log('Prevent acceleration', factor, this.velocity, next); // console.log('Prevent acceleration', factor, this.velocity, next)
} }
this.velocity = next; this.velocity = next;
let d = Points$1.multiplyScalar(this.velocity, dt); let d = Points$1.multiplyScalar(this.velocity, dt);
@ -3323,11 +3384,11 @@
this.onDragUpdate(d); this.onDragUpdate(d);
if (dt == 0 || this.needsAnimation()) { if (dt == 0 || this.needsAnimation()) {
requestAnimationFrame(this.animateThrow.bind(this)); thrower.animateThrow(this);
return return
} else { } else {
if (this.isOutside()) { if (this.isOutside()) {
requestAnimationFrame(this.animateThrow.bind(this)); thrower.animateThrow(this);
return return
} }
} }
@ -3351,7 +3412,7 @@
let next = Points$1.multiplyScalar(velocity, this.throwDamping); let next = Points$1.multiplyScalar(velocity, this.throwDamping);
return { return {
x: this.movableX ? next.x : 0, x: this.movableX ? next.x : 0,
y: this.movableY ? next.y : 0, y: this.movableY ? next.y : 0
} }
} }
@ -3396,7 +3457,7 @@
scaleCloseBuffer = 0.05, scaleCloseBuffer = 0.05,
maxRotation = Angle.degree2radian(5), maxRotation = Angle.degree2radian(5),
minInteractionDistance = 0, minInteractionDistance = 0,
useLowPassFilter = false, useLowPassFilter = false
} = {}) { } = {}) {
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')
@ -3411,7 +3472,7 @@
throwVisibility, throwVisibility,
throwDamping, throwDamping,
autoThrow, autoThrow,
onThrowFinished, onThrowFinished
}); });
/** /**
@ -3486,7 +3547,7 @@
_callCloseCallbacks() { _callCloseCallbacks() {
if (this.onClose) { if (this.onClose) {
this.onClose.forEach((callback) => callback(this)); this.onClose.forEach(callback => callback(this));
} }
} }
@ -3506,6 +3567,9 @@
let delta = interaction.delta(); let delta = interaction.delta();
if (delta != null) { if (delta != null) {
/* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
has to be tested */
this.addVelocity(delta);
let rotate = delta.rotate; let rotate = delta.rotate;
let zoom = delta.zoom; let zoom = delta.zoom;
if (this.maxRotation != null) { if (this.maxRotation != null) {
@ -3524,9 +3588,10 @@
zoomDelta *= ratio; zoomDelta *= ratio;
zoom = 1 + zoomDelta; zoom = 1 + zoomDelta;
} }
this.transform(delta, zoom, rotate, delta.about); this.transform(delta, zoom, rotate, delta.about);
this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 /* uo: This is too late an dangerous. transform sometimes modifies delta
this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
*/
if (zoom != 1) this.interactionAnchor = delta.about; if (zoom != 1) this.interactionAnchor = delta.about;
} }
@ -3637,11 +3702,11 @@
if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
this.zoom(this.minScale, { this.zoom(this.minScale, {
animate: 0.2, animate: 0.2,
onComplete: this.close.bind(this), onComplete: this.close.bind(this)
}); });
} else if (this.scale < this.minScale + this.scaleCloseThreshold) { } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
this.zoom(this.minScale + this.scaleCloseThreshold, { this.zoom(this.minScale + this.scaleCloseThreshold, {
animate: 0.4, animate: 0.4
}); });
} }
} }
@ -3663,12 +3728,12 @@
x: '+=' + d.x, x: '+=' + d.x,
y: '+=' + d.y, y: '+=' + d.y,
/* scale: scale, uo: not defined, why was this here? */ /* scale: scale, uo: not defined, why was this here? */
onUpdate: (e) => { onUpdate: e => {
let p = this.position; let p = this.position;
let dx = p.x - startPos.x; let dx = p.x - startPos.x;
let dy = p.x - startPos.y; let dy = p.x - startPos.y;
this.onMoved(dx, dy); this.onMoved(dx, dy);
}, }
}); });
} else { } else {
this._move(d); this._move(d);
@ -3697,7 +3762,7 @@
scale: scale, scale: scale,
delay: delay, delay: delay,
onComplete: onComplete, onComplete: onComplete,
onUpdate: this.onZoomed.bind(this), onUpdate: this.onZoomed.bind(this)
}); });
} else { } else {
this.scale = scale; this.scale = scale;
@ -3715,7 +3780,7 @@
transform(translate, zoom, rotate, anchor) { transform(translate, zoom, rotate, anchor) {
let delta = { let delta = {
x: this.movableX ? translate.x : 0, x: this.movableX ? translate.x : 0,
y: this.movableY ? translate.y : 0, y: this.movableY ? translate.y : 0
}; };
if (this.resizable) var vzoom = zoom; if (this.resizable) var vzoom = zoom;
if (!this.translatable) delta = { x: 0, y: 0 }; if (!this.translatable) delta = { x: 0, y: 0 };
@ -3730,9 +3795,9 @@
rotate: 0, rotate: 0,
about: anchor, about: anchor,
fast: false, fast: false,
type: UPDATE, type: UPDATE
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3761,9 +3826,9 @@
translate: delta, translate: delta,
scale: newScale, scale: newScale,
rotate: rotate, rotate: rotate,
about: anchor, about: anchor
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3829,7 +3894,7 @@
if (this.scale > this.maxScale) zoom = 1 - amount; if (this.scale > this.maxScale) zoom = 1 - amount;
if (zoom != 1) { if (zoom != 1) {
this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor);
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.animateZoomBounce(dt); this.animateZoomBounce(dt);
}); });
return return
@ -3886,9 +3951,9 @@
rotate: 0, rotate: 0,
about: null, about: null,
fast: false, fast: false,
type: START, type: START
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3904,13 +3969,13 @@
} }
onEnd(event, interaction) { onEnd(event, interaction) {
console.log('Scatter.onEnd', this.dragging); //console.log('Scatter.onEnd', this.dragging)
if (interaction.isFinished()) { if (interaction.isFinished()) {
this.endGesture(interaction); this.endGesture(interaction);
this.dragging = false; this.dragging = false;
for (let key of interaction.ended.keys()) { for (let key of interaction.ended.keys()) {
if (interaction.isTap(key)) { if (interaction.isTap(key)) {
console.log('Scatter.isTap'); //console.log('Scatter.isTap')
let point = interaction.ended.get(key); let point = interaction.ended.get(key);
this.onTap(event, interaction, point); this.onTap(event, interaction, point);
} }
@ -3922,9 +3987,9 @@
rotate: 0, rotate: 0,
about: null, about: null,
fast: false, fast: false,
type: END, type: END
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3938,7 +4003,7 @@
//onTap(event, interaction, point) {} //onTap(event, interaction, point) {}
onTap(event, interaction, point) { onTap(event, interaction, point) {
console.log('AbstractScatter.onTap', this.tapDelegate, interaction); //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
if (this.tapDelegate) { if (this.tapDelegate) {
Events.stop(event); Events.stop(event);
this.tapDelegate.tap(event, 'scatter'); this.tapDelegate.tap(event, 'scatter');
@ -3952,9 +4017,9 @@
translate: delta, translate: delta,
scale: this.scale, scale: this.scale,
about: this.currentAbout, about: this.currentAbout,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3966,9 +4031,9 @@
scale: this.scale, scale: this.scale,
about: this.currentAbout, about: this.currentAbout,
fast: false, fast: false,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3980,9 +4045,9 @@
translate: { x: dx, y: dy }, translate: { x: dx, y: dy },
about: about, about: about,
fast: true, fast: true,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -3993,9 +4058,9 @@
let event = new ScatterEvent(this, { let event = new ScatterEvent(this, {
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);
}); });
} }
@ -4009,9 +4074,9 @@
scale: this.scale, scale: this.scale,
about: about, about: about,
fast: false, fast: false,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -4043,7 +4108,7 @@
useCapture = true, useCapture = true,
capturePointerEvents = true, capturePointerEvents = true,
touchAction = 'none', touchAction = 'none',
debugCanvas = null, debugCanvas = null
} = {} } = {}
) { ) {
this.onCapture = null; this.onCapture = null;
@ -4055,7 +4120,7 @@
movement of scatter objects, the touchmove event has to be bound again. movement of scatter objects, the touchmove event has to be bound again.
*/ */
if (Capabilities.isSafari) { if (Capabilities.isSafari) {
document.addEventListener('touchmove', (event) => this.preventPinch(event), false); document.addEventListener('touchmove', event => this.preventPinch(event), false);
stopEvents = false; stopEvents = false;
} else { } else {
stopEvents = true; stopEvents = true;
@ -4070,11 +4135,11 @@
this.delegate = new InteractionMapper$1(element, this, { this.delegate = new InteractionMapper$1(element, this, {
useCapture, useCapture,
capturePointerEvents, capturePointerEvents,
mouseWheelElement: window, mouseWheelElement: window
}); });
if (debugCanvas !== null) { if (debugCanvas !== null) {
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.showTouches(dt, debugCanvas); this.showTouches(dt, debugCanvas);
}); });
} }
@ -4096,7 +4161,7 @@
context.fill(); context.fill();
context.stroke(); context.stroke();
} }
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.showTouches(dt, canvas); this.showTouches(dt, canvas);
}); });
} }
@ -4238,7 +4303,7 @@
scaleCloseBuffer = 0.05, scaleCloseBuffer = 0.05,
useLowPassFilter = false, useLowPassFilter = false,
maxRotation = Angle.degree2radian(15), maxRotation = Angle.degree2radian(15),
minInteractionDistance = 200, minInteractionDistance = 200
} = {} } = {}
) { ) {
super({ super({
@ -4265,7 +4330,7 @@
onClose, onClose,
useLowPassFilter, useLowPassFilter,
maxRotation, maxRotation,
minInteractionDistance, minInteractionDistance
}); });
if (container == null || width == null || height == null) { if (container == null || width == null || height == null) {
throw new Error('Invalid value: null') throw new Error('Invalid value: null')
@ -4293,7 +4358,7 @@
height: height, height: height,
scale: startScale, scale: startScale,
rotation: this.startRotationDegrees, rotation: this.startRotationDegrees,
transformOrigin: transformOrigin, transformOrigin: transformOrigin
}; };
this.tapNodes = new Map(); this.tapNodes = new Map();
@ -4315,15 +4380,15 @@
button.className = 'interactiveElement'; button.className = 'interactiveElement';
this.element.appendChild(button); this.element.appendChild(button);
button.addEventListener('pointerdown', (e) => { button.addEventListener('pointerdown', e => {
this.startResize(e); this.startResize(e);
}); });
button.addEventListener('pointermove', (e) => { button.addEventListener('pointermove', e => {
this.resize(e); this.resize(e);
}); });
button.addEventListener('pointerup', (e) => { button.addEventListener('pointerup', e => {
this.stopResize(e); this.stopResize(e);
}); });
this.resizeButton = button; this.resizeButton = button;
@ -4340,7 +4405,7 @@
scale: this.scale, scale: this.scale,
x: this.x, x: this.x,
y: this.y, y: this.y,
rotation: this.rotation, rotation: this.rotation
} }
} }
@ -4391,7 +4456,7 @@
top: rect.top - stage.top, top: rect.top - stage.top,
left: rect.left - stage.left, left: rect.left - stage.left,
width: rect.width, width: rect.width,
height: rect.height, height: rect.height
} }
} }
@ -4432,7 +4497,7 @@
set scale(scale) { set scale(scale) {
TweenLite.set(this.element, { TweenLite.set(this.element, {
scale: scale, scale: scale,
transformOrigin: this.transformOrigin, transformOrigin: this.transformOrigin
}); });
this._scale = scale; this._scale = scale;
} }
@ -4464,9 +4529,9 @@
hide() { hide() {
TweenLite.to(this.element, 0.1, { TweenLite.to(this.element, 0.1, {
display: 'none', display: 'none',
onComplete: (e) => { onComplete: e => {
this.element.parentNode.removeChild(this.element); this.element.parentNode.removeChild(this.element);
}, }
}); });
} }
@ -4480,7 +4545,7 @@
x: p.x, x: p.x,
y: p.y, y: p.y,
rotation: rotationDegrees, rotation: rotationDegrees,
transformOrigin: this.transformOrigin, transformOrigin: this.transformOrigin
}); });
} }
@ -4541,7 +4606,7 @@
let oldPostition = { let oldPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
this.bringToFront(); this.bringToFront();
@ -4549,7 +4614,7 @@
let newPostition = { let newPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
let offset = Points$1.subtract(oldPostition, newPostition); let offset = Points$1.subtract(oldPostition, newPostition);
@ -4594,7 +4659,7 @@
) )
TweenLite.to(this.element, 0, { TweenLite.to(this.element, 0, {
width: this.element.offsetWidth + resizeW / this.scale, width: this.element.offsetWidth + resizeW / this.scale,
height: this.element.offsetHeight + resizeH / this.scale, height: this.element.offsetHeight + resizeH / this.scale
}); });
this.oldX = e.clientX; this.oldX = e.clientX;
@ -4611,12 +4676,12 @@
let event = new CustomEvent('resizeEnded'); let event = new CustomEvent('resizeEnded');
let oldPostition = { let oldPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
this.element.style.transformOrigin = '50% 50%'; this.element.style.transformOrigin = '50% 50%';
let newPostition = { let newPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
let offset = Points$1.subtract(oldPostition, newPostition); let offset = Points$1.subtract(oldPostition, newPostition);
@ -4789,7 +4854,6 @@
tap(event, calledBy = 'unknown') { tap(event, calledBy = 'unknown') {
if (event.isTrusted) { if (event.isTrusted) {
let node = this.nearestActive(event); let node = this.nearestActive(event);
console.log('tap', node);
this.nodeTapped(node, event); this.nodeTapped(node, event);
/* let node = document.elementFromPoint(event.clientX, event.clientY) /* let node = document.elementFromPoint(event.clientX, event.clientY)
@ -8195,7 +8259,6 @@
image, image,
}); });
let center = this._calculateCenterRelativeTo(target, image); let center = this._calculateCenterRelativeTo(target, image);
console.log('_calculateCenterRelativeTo', center);
TweenLite.set(maskImage, { TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}`, transformOrigin: `${center.x} ${center.y}`,
}); });
@ -9099,7 +9162,6 @@
} }
static _calculateCenterRelativeTo(target, image) { static _calculateCenterRelativeTo(target, image) {
// console.log('_calculateCenterRelativeTo', target, image)
let bbox = image.getBBox(); let bbox = image.getBBox();
let width = bbox.width; let width = bbox.width;
let height = bbox.height; let height = bbox.height;

184
dist/iwmlib.pixi.js vendored
View File

@ -6778,6 +6778,40 @@
* *
* @constructor * @constructor
*/ */
class RequestFrameThrower {
/** Implemenents the standard throw behavior. */
animateThrow(throwable) {
/*** Calls the animateThrow method in sync with the display link. */
requestAnimationFrame(throwable.animateThrow.bind(throwable));
}
}
class TimeoutThrower {
constructor(delay=20) {
this.throwables = new Set();
this.delay = delay;
setTimeout(this.animateStep.bind(this), this.delay);
}
animateThrow(throwable) {
this.throwables.add(throwable);
}
animateStep() {
let active = [...this.throwables];
this.throwables.clear();
for(let throwable of active) {
throwable.animateThrow();
}
setTimeout(this.animateStep.bind(this), this.delay);
}
}
let thrower = new RequestFrameThrower();
class Throwable { class Throwable {
constructor({ constructor({
movableX = true, movableX = true,
@ -6785,7 +6819,7 @@
throwVisibility = 44, throwVisibility = 44,
throwDamping = 0.95, throwDamping = 0.95,
autoThrow = true, autoThrow = true,
onThrowFinished = null, onThrowFinished = null
} = {}) { } = {}) {
this.movableX = movableX; this.movableX = movableX;
this.movableY = movableY; this.movableY = movableY;
@ -6794,9 +6828,16 @@
this.autoThrow = autoThrow; this.autoThrow = autoThrow;
this.velocities = []; this.velocities = [];
this.velocity = null; this.velocity = null;
this.timestamp = null; this.lastframe = null;
this.onThrowFinished = onThrowFinished; this.onThrowFinished = onThrowFinished;
//console.log("onThrowFinished", onThrowFinished) }
static defaultThrow() {
thrower = new RequestFrameThrower();
}
static timeoutThrow() {
thrower = new TimeoutThrower();
} }
observeVelocity() { observeVelocity() {
@ -6818,7 +6859,7 @@
t: t, t: t,
dt: dt, dt: dt,
dx: delta.x / number, dx: delta.x / number,
dy: delta.y / number, dy: delta.y / number
}; };
this.velocities.push(velocity); this.velocities.push(velocity);
while (this.velocities.length > buffer) { while (this.velocities.length > buffer) {
@ -6827,8 +6868,23 @@
} }
} }
addTestVelocity(delta, dt=20, buffer = 5) {
let t = performance.now();
this.lastframe = t;
let velocity = {
t: t,
dt: dt,
dx: delta.x ,
dy: delta.y
};
for(let i=0; i<buffer; i++) {
velocity.t += dt;
this.velocities.push(velocity);
}
}
meanVelocity(milliseconds = 30) { meanVelocity(milliseconds = 30) {
this.addVelocity({ x: 0, y: 0, number: 1 }); // this.addVelocity({ x: 0, y: 0, number: 1 })
let sum = { x: 0, y: 0 }; let sum = { x: 0, y: 0 };
let count = 0; let count = 0;
let t = 0; let t = 0;
@ -6849,6 +6905,7 @@
killAnimation() { killAnimation() {
this.velocity = null; this.velocity = null;
this.velocities = []; this.velocities = [];
this.lastframe = null;
} }
startThrow() { startThrow() {
@ -6870,6 +6927,10 @@
return dt return dt
} }
recurseAnimateThrow() {
ThrowableObjects.add(this);
}
animateThrow(time) { animateThrow(time) {
if (this.velocity != null) { if (this.velocity != null) {
let dt = this._throwDeltaTime(); let dt = this._throwDeltaTime();
@ -6880,7 +6941,7 @@
if (nextLength > prevLength) { if (nextLength > prevLength) {
let factor = nextLength / prevLength; let factor = nextLength / prevLength;
next = Points.multiplyScalar(next, 1 / factor); next = Points.multiplyScalar(next, 1 / factor);
console.log('Prevent acceleration', factor, this.velocity, next); // console.log('Prevent acceleration', factor, this.velocity, next)
} }
this.velocity = next; this.velocity = next;
let d = Points.multiplyScalar(this.velocity, dt); let d = Points.multiplyScalar(this.velocity, dt);
@ -6888,11 +6949,11 @@
this.onDragUpdate(d); this.onDragUpdate(d);
if (dt == 0 || this.needsAnimation()) { if (dt == 0 || this.needsAnimation()) {
requestAnimationFrame(this.animateThrow.bind(this)); thrower.animateThrow(this);
return return
} else { } else {
if (this.isOutside()) { if (this.isOutside()) {
requestAnimationFrame(this.animateThrow.bind(this)); thrower.animateThrow(this);
return return
} }
} }
@ -6916,7 +6977,7 @@
let next = Points.multiplyScalar(velocity, this.throwDamping); let next = Points.multiplyScalar(velocity, this.throwDamping);
return { return {
x: this.movableX ? next.x : 0, x: this.movableX ? next.x : 0,
y: this.movableY ? next.y : 0, y: this.movableY ? next.y : 0
} }
} }
@ -6961,7 +7022,7 @@
scaleCloseBuffer = 0.05, scaleCloseBuffer = 0.05,
maxRotation = Angle.degree2radian(5), maxRotation = Angle.degree2radian(5),
minInteractionDistance = 0, minInteractionDistance = 0,
useLowPassFilter = false, useLowPassFilter = false
} = {}) { } = {}) {
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')
@ -6976,7 +7037,7 @@
throwVisibility, throwVisibility,
throwDamping, throwDamping,
autoThrow, autoThrow,
onThrowFinished, onThrowFinished
}); });
/** /**
@ -7051,7 +7112,7 @@
_callCloseCallbacks() { _callCloseCallbacks() {
if (this.onClose) { if (this.onClose) {
this.onClose.forEach((callback) => callback(this)); this.onClose.forEach(callback => callback(this));
} }
} }
@ -7071,6 +7132,9 @@
let delta = interaction.delta(); let delta = interaction.delta();
if (delta != null) { if (delta != null) {
/* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
has to be tested */
this.addVelocity(delta);
let rotate = delta.rotate; let rotate = delta.rotate;
let zoom = delta.zoom; let zoom = delta.zoom;
if (this.maxRotation != null) { if (this.maxRotation != null) {
@ -7089,9 +7153,10 @@
zoomDelta *= ratio; zoomDelta *= ratio;
zoom = 1 + zoomDelta; zoom = 1 + zoomDelta;
} }
this.transform(delta, zoom, rotate, delta.about); this.transform(delta, zoom, rotate, delta.about);
this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 /* uo: This is too late an dangerous. transform sometimes modifies delta
this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
*/
if (zoom != 1) this.interactionAnchor = delta.about; if (zoom != 1) this.interactionAnchor = delta.about;
} }
@ -7202,11 +7267,11 @@
if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
this.zoom(this.minScale, { this.zoom(this.minScale, {
animate: 0.2, animate: 0.2,
onComplete: this.close.bind(this), onComplete: this.close.bind(this)
}); });
} else if (this.scale < this.minScale + this.scaleCloseThreshold) { } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
this.zoom(this.minScale + this.scaleCloseThreshold, { this.zoom(this.minScale + this.scaleCloseThreshold, {
animate: 0.4, animate: 0.4
}); });
} }
} }
@ -7228,12 +7293,12 @@
x: '+=' + d.x, x: '+=' + d.x,
y: '+=' + d.y, y: '+=' + d.y,
/* scale: scale, uo: not defined, why was this here? */ /* scale: scale, uo: not defined, why was this here? */
onUpdate: (e) => { onUpdate: e => {
let p = this.position; let p = this.position;
let dx = p.x - startPos.x; let dx = p.x - startPos.x;
let dy = p.x - startPos.y; let dy = p.x - startPos.y;
this.onMoved(dx, dy); this.onMoved(dx, dy);
}, }
}); });
} else { } else {
this._move(d); this._move(d);
@ -7262,7 +7327,7 @@
scale: scale, scale: scale,
delay: delay, delay: delay,
onComplete: onComplete, onComplete: onComplete,
onUpdate: this.onZoomed.bind(this), onUpdate: this.onZoomed.bind(this)
}); });
} else { } else {
this.scale = scale; this.scale = scale;
@ -7280,7 +7345,7 @@
transform(translate, zoom, rotate, anchor) { transform(translate, zoom, rotate, anchor) {
let delta = { let delta = {
x: this.movableX ? translate.x : 0, x: this.movableX ? translate.x : 0,
y: this.movableY ? translate.y : 0, y: this.movableY ? translate.y : 0
}; };
if (this.resizable) var vzoom = zoom; if (this.resizable) var vzoom = zoom;
if (!this.translatable) delta = { x: 0, y: 0 }; if (!this.translatable) delta = { x: 0, y: 0 };
@ -7295,9 +7360,9 @@
rotate: 0, rotate: 0,
about: anchor, about: anchor,
fast: false, fast: false,
type: UPDATE, type: UPDATE
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7326,9 +7391,9 @@
translate: delta, translate: delta,
scale: newScale, scale: newScale,
rotate: rotate, rotate: rotate,
about: anchor, about: anchor
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7394,7 +7459,7 @@
if (this.scale > this.maxScale) zoom = 1 - amount; if (this.scale > this.maxScale) zoom = 1 - amount;
if (zoom != 1) { if (zoom != 1) {
this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor);
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.animateZoomBounce(dt); this.animateZoomBounce(dt);
}); });
return return
@ -7451,9 +7516,9 @@
rotate: 0, rotate: 0,
about: null, about: null,
fast: false, fast: false,
type: START, type: START
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7469,13 +7534,13 @@
} }
onEnd(event, interaction) { onEnd(event, interaction) {
console.log('Scatter.onEnd', this.dragging); //console.log('Scatter.onEnd', this.dragging)
if (interaction.isFinished()) { if (interaction.isFinished()) {
this.endGesture(interaction); this.endGesture(interaction);
this.dragging = false; this.dragging = false;
for (let key of interaction.ended.keys()) { for (let key of interaction.ended.keys()) {
if (interaction.isTap(key)) { if (interaction.isTap(key)) {
console.log('Scatter.isTap'); //console.log('Scatter.isTap')
let point = interaction.ended.get(key); let point = interaction.ended.get(key);
this.onTap(event, interaction, point); this.onTap(event, interaction, point);
} }
@ -7487,9 +7552,9 @@
rotate: 0, rotate: 0,
about: null, about: null,
fast: false, fast: false,
type: END, type: END
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7503,7 +7568,7 @@
//onTap(event, interaction, point) {} //onTap(event, interaction, point) {}
onTap(event, interaction, point) { onTap(event, interaction, point) {
console.log('AbstractScatter.onTap', this.tapDelegate, interaction); //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
if (this.tapDelegate) { if (this.tapDelegate) {
Events$1.stop(event); Events$1.stop(event);
this.tapDelegate.tap(event, 'scatter'); this.tapDelegate.tap(event, 'scatter');
@ -7517,9 +7582,9 @@
translate: delta, translate: delta,
scale: this.scale, scale: this.scale,
about: this.currentAbout, about: this.currentAbout,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7531,9 +7596,9 @@
scale: this.scale, scale: this.scale,
about: this.currentAbout, about: this.currentAbout,
fast: false, fast: false,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7545,9 +7610,9 @@
translate: { x: dx, y: dy }, translate: { x: dx, y: dy },
about: about, about: about,
fast: true, fast: true,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7558,9 +7623,9 @@
let event = new ScatterEvent(this, { let event = new ScatterEvent(this, {
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);
}); });
} }
@ -7574,9 +7639,9 @@
scale: this.scale, scale: this.scale,
about: about, about: about,
fast: false, fast: false,
type: null, type: null
}); });
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event); f(event);
}); });
} }
@ -7623,7 +7688,7 @@
scaleCloseBuffer = 0.05, scaleCloseBuffer = 0.05,
useLowPassFilter = false, useLowPassFilter = false,
maxRotation = Angle.degree2radian(15), maxRotation = Angle.degree2radian(15),
minInteractionDistance = 200, minInteractionDistance = 200
} = {} } = {}
) { ) {
super({ super({
@ -7650,7 +7715,7 @@
onClose, onClose,
useLowPassFilter, useLowPassFilter,
maxRotation, maxRotation,
minInteractionDistance, minInteractionDistance
}); });
if (container == null || width == null || height == null) { if (container == null || width == null || height == null) {
throw new Error('Invalid value: null') throw new Error('Invalid value: null')
@ -7678,7 +7743,7 @@
height: height, height: height,
scale: startScale, scale: startScale,
rotation: this.startRotationDegrees, rotation: this.startRotationDegrees,
transformOrigin: transformOrigin, transformOrigin: transformOrigin
}; };
this.tapNodes = new Map(); this.tapNodes = new Map();
@ -7700,15 +7765,15 @@
button.className = 'interactiveElement'; button.className = 'interactiveElement';
this.element.appendChild(button); this.element.appendChild(button);
button.addEventListener('pointerdown', (e) => { button.addEventListener('pointerdown', e => {
this.startResize(e); this.startResize(e);
}); });
button.addEventListener('pointermove', (e) => { button.addEventListener('pointermove', e => {
this.resize(e); this.resize(e);
}); });
button.addEventListener('pointerup', (e) => { button.addEventListener('pointerup', e => {
this.stopResize(e); this.stopResize(e);
}); });
this.resizeButton = button; this.resizeButton = button;
@ -7725,7 +7790,7 @@
scale: this.scale, scale: this.scale,
x: this.x, x: this.x,
y: this.y, y: this.y,
rotation: this.rotation, rotation: this.rotation
} }
} }
@ -7776,7 +7841,7 @@
top: rect.top - stage.top, top: rect.top - stage.top,
left: rect.left - stage.left, left: rect.left - stage.left,
width: rect.width, width: rect.width,
height: rect.height, height: rect.height
} }
} }
@ -7817,7 +7882,7 @@
set scale(scale) { set scale(scale) {
TweenLite.set(this.element, { TweenLite.set(this.element, {
scale: scale, scale: scale,
transformOrigin: this.transformOrigin, transformOrigin: this.transformOrigin
}); });
this._scale = scale; this._scale = scale;
} }
@ -7849,9 +7914,9 @@
hide() { hide() {
TweenLite.to(this.element, 0.1, { TweenLite.to(this.element, 0.1, {
display: 'none', display: 'none',
onComplete: (e) => { onComplete: e => {
this.element.parentNode.removeChild(this.element); this.element.parentNode.removeChild(this.element);
}, }
}); });
} }
@ -7865,7 +7930,7 @@
x: p.x, x: p.x,
y: p.y, y: p.y,
rotation: rotationDegrees, rotation: rotationDegrees,
transformOrigin: this.transformOrigin, transformOrigin: this.transformOrigin
}); });
} }
@ -7926,7 +7991,7 @@
let oldPostition = { let oldPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
this.bringToFront(); this.bringToFront();
@ -7934,7 +7999,7 @@
let newPostition = { let newPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
let offset = Points.subtract(oldPostition, newPostition); let offset = Points.subtract(oldPostition, newPostition);
@ -7979,7 +8044,7 @@
) )
TweenLite.to(this.element, 0, { TweenLite.to(this.element, 0, {
width: this.element.offsetWidth + resizeW / this.scale, width: this.element.offsetWidth + resizeW / this.scale,
height: this.element.offsetHeight + resizeH / this.scale, height: this.element.offsetHeight + resizeH / this.scale
}); });
this.oldX = e.clientX; this.oldX = e.clientX;
@ -7996,12 +8061,12 @@
let event = new CustomEvent('resizeEnded'); let event = new CustomEvent('resizeEnded');
let oldPostition = { let oldPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
this.element.style.transformOrigin = '50% 50%'; this.element.style.transformOrigin = '50% 50%';
let newPostition = { let newPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
}; };
let offset = Points.subtract(oldPostition, newPostition); let offset = Points.subtract(oldPostition, newPostition);
@ -8174,7 +8239,6 @@
tap(event, calledBy = 'unknown') { tap(event, calledBy = 'unknown') {
if (event.isTrusted) { if (event.isTrusted) {
let node = this.nearestActive(event); let node = this.nearestActive(event);
console.log('tap', node);
this.nodeTapped(node, event); this.nodeTapped(node, event);
/* let node = document.elementFromPoint(event.clientX, event.clientY) /* let node = document.elementFromPoint(event.clientX, event.clientY)

0
lib/_menu.js Executable file → Normal file
View File

View File

@ -766,7 +766,6 @@ export default class Card {
} }
static _calculateCenterRelativeTo(target, image) { static _calculateCenterRelativeTo(target, image) {
// console.log('_calculateCenterRelativeTo', target, image)
let bbox = image.getBBox() let bbox = image.getBBox()
let width = bbox.width let width = bbox.width
let height = bbox.height let height = bbox.height

View File

@ -213,7 +213,6 @@ export default class Highlight extends Object {
image, image,
}) })
let center = this._calculateCenterRelativeTo(target, image) let center = this._calculateCenterRelativeTo(target, image)
console.log('_calculateCenterRelativeTo', center)
TweenLite.set(maskImage, { TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}`, transformOrigin: `${center.x} ${center.y}`,
}) })

View File

@ -158,7 +158,6 @@ export default class CardWrapper extends Object {
tap(event, calledBy = 'unknown') { tap(event, calledBy = 'unknown') {
if (event.isTrusted) { if (event.isTrusted) {
let node = this.nearestActive(event) let node = this.nearestActive(event)
console.log('tap', node)
this.nodeTapped(node, event) this.nodeTapped(node, event)
/* let node = document.elementFromPoint(event.clientX, event.clientY) /* let node = document.elementFromPoint(event.clientX, event.clientY)

View File

@ -17,44 +17,60 @@
<div class="flipFace back" style="visibility: hidden"></div> <div class="flipFace back" style="visibility: hidden"></div>
</div> </div>
<!-- Very tricky problem to scale svgs: see https://css-tricks.com/scale-svg/ --> <!-- Very tricky problem to scale svgs: see https://css-tricks.com/scale-svg/ -->
<svg <!-- SVG viewPort interferes with DOMMatrix calculations: see
class="flipButton backBtn" https://stackoverflow.com/questions/70696387/how-to-get-transform-matrix-of-a-dom-element-->
style="visibility: hidden" <div class="flipButton backBtn" style="visibility:hidden;">
viewBox="0 0 100 100" <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" style="width:inherit; height:inherit;">
preserveAspectRatio="xMidYMid meet"
>
<g stroke-width="8" stroke="white"> <g stroke-width="8" stroke="white">
<circle cx="50" cy="50" r="44" fill="gray" /> <circle cx="50" cy="50" r="44" fill="gray" />
<line x1="30" y1="30" x2="70" y2="70" /> <line x1="30" y1="30" x2="70" y2="70" />
<line x1="30" y1="70" x2="70" y2="30" /> <line x1="30" y1="70" x2="70" y2="30" />
</g> </g>
</svg> </svg>
</div>
<svg class="flipButton infoBtn" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet"> <div class="flipButton infoBtn">
<svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" style="width:inherit; height:inherit;">
<circle cx="50" cy="50" r="44" stroke="white" stroke-width="8" fill="gray" /> <circle cx="50" cy="50" r="44" stroke="white" stroke-width="8" fill="gray" />
<circle cx="50" cy="32" r="7" fill="white" /> <circle cx="50" cy="32" r="7" fill="white" />
<line x1="50" y1="46" x2="50" y2="78" stroke="white" stroke-width="12" /> <line x1="50" y1="46" x2="50" y2="78" stroke="white" stroke-width="12" />
</svg> </svg>
</div> </div>
</template>
</head> </div>
<body onload="Doctest.run()"> </template>
<h1><a href="index.html">lib.</a>Flippable</h1>
<p> </head>
The flip effect simulates a flip between a front and back view of an object by means of a 3D rotation. The <body onload="Doctest.run()">
DOMFlippable class implements this effect for two DOM nodes, one as the front view, the other as the back <h1>
view. Both views are connected via a HTML template that defines the placeholders for front and back views. Flippable
The style file "css/flipeffect.css" holds reasonable default styles for this kind of templates. </h1>
</p> <p>
<pre><code> The flip effect simulates a flip between a front and back view of an object
by means of a 3D rotation. The DOMFlippable class implements this effect for two
DOM nodes, one as the front view, the other as the back view. Both views are connected
via a HTML template that defines the placeholders for front and back views. The
style file "css/flipeffect.css" holds reasonable default styles for this kind of
templates.
</p>
<p>
The SVG buttons have to be wrapped in an HTML DOM element which handles events. Otherwise,
the viewbox of the SVG will interfere with the coordinate transformation.
</p>
<pre><code>
&lt;template id="flipTemplate"&gt; &lt;template id="flipTemplate"&gt;
&lt;div class="flipWrapper"&gt; &lt;div class="flipWrapper"&gt;
&lt;div class="flipCard"&gt; &lt;div class="flipCard"&gt;
&lt;div class="flipFace front"&gt;&lt;/div&gt; &lt;div class="flipFace front"&gt;&lt;/div&gt;
&lt;div class="flipFace back" style="visibility:hidden;"&gt;&lt;/div&gt; &lt;div class="flipFace back" style="visibility:hidden;"&gt;&lt;/div&gt;
&lt;/div> &lt;/div>
&lt;svg class="flipButton backBtn" .../&gt; &lt;div class="flipButton backBtn" .../&gt;
&lt;svg class="flipButton infoBtn" .../&gt; &lt;svg .../&gt;
&lt;/div>
&lt;div class="flipButton infoBtn" .../&gt;
&lt;svg .../&gt;
&lt;/div>
&lt;/div&gt; &lt;/div&gt;
&lt;/template&gt; &lt;/template&gt;
</code> </code>

View File

@ -88,6 +88,40 @@ export class ResizeEvent extends BaseEvent {
* *
* @constructor * @constructor
*/ */
class RequestFrameThrower {
/** Implemenents the standard throw behavior. */
animateThrow(throwable) {
/*** Calls the animateThrow method in sync with the display link. */
requestAnimationFrame(throwable.animateThrow.bind(throwable))
}
}
class TimeoutThrower {
constructor(delay=20) {
this.throwables = new Set()
this.delay = delay
setTimeout(this.animateStep.bind(this), this.delay)
}
animateThrow(throwable) {
this.throwables.add(throwable)
}
animateStep() {
let active = [...this.throwables]
this.throwables.clear()
for(let throwable of active) {
throwable.animateThrow()
}
setTimeout(this.animateStep.bind(this), this.delay)
}
}
let thrower = new RequestFrameThrower()
class Throwable { class Throwable {
constructor({ constructor({
movableX = true, movableX = true,
@ -95,7 +129,7 @@ class Throwable {
throwVisibility = 44, throwVisibility = 44,
throwDamping = 0.95, throwDamping = 0.95,
autoThrow = true, autoThrow = true,
onThrowFinished = null, onThrowFinished = null
} = {}) { } = {}) {
this.movableX = movableX this.movableX = movableX
this.movableY = movableY this.movableY = movableY
@ -104,9 +138,16 @@ class Throwable {
this.autoThrow = autoThrow this.autoThrow = autoThrow
this.velocities = [] this.velocities = []
this.velocity = null this.velocity = null
this.timestamp = null this.lastframe = null
this.onThrowFinished = onThrowFinished this.onThrowFinished = onThrowFinished
//console.log("onThrowFinished", onThrowFinished) }
static defaultThrow() {
thrower = new RequestFrameThrower()
}
static timeoutThrow() {
thrower = new TimeoutThrower()
} }
observeVelocity() { observeVelocity() {
@ -128,7 +169,7 @@ class Throwable {
t: t, t: t,
dt: dt, dt: dt,
dx: delta.x / number, dx: delta.x / number,
dy: delta.y / number, dy: delta.y / number
} }
this.velocities.push(velocity) this.velocities.push(velocity)
while (this.velocities.length > buffer) { while (this.velocities.length > buffer) {
@ -137,8 +178,23 @@ class Throwable {
} }
} }
addTestVelocity(delta, dt=20, buffer = 5) {
let t = performance.now()
this.lastframe = t
let velocity = {
t: t,
dt: dt,
dx: delta.x ,
dy: delta.y
}
for(let i=0; i<buffer; i++) {
velocity.t += dt
this.velocities.push(velocity)
}
}
meanVelocity(milliseconds = 30) { meanVelocity(milliseconds = 30) {
this.addVelocity({ x: 0, y: 0, number: 1 }) // this.addVelocity({ x: 0, y: 0, number: 1 })
let sum = { x: 0, y: 0 } let sum = { x: 0, y: 0 }
let count = 0 let count = 0
let t = 0 let t = 0
@ -159,6 +215,7 @@ class Throwable {
killAnimation() { killAnimation() {
this.velocity = null this.velocity = null
this.velocities = [] this.velocities = []
this.lastframe = null
} }
startThrow() { startThrow() {
@ -180,6 +237,10 @@ class Throwable {
return dt return dt
} }
recurseAnimateThrow() {
ThrowableObjects.add(this)
}
animateThrow(time) { animateThrow(time) {
if (this.velocity != null) { if (this.velocity != null) {
let dt = this._throwDeltaTime() let dt = this._throwDeltaTime()
@ -190,7 +251,7 @@ class Throwable {
if (nextLength > prevLength) { if (nextLength > prevLength) {
let factor = nextLength / prevLength let factor = nextLength / prevLength
next = Points.multiplyScalar(next, 1 / factor) next = Points.multiplyScalar(next, 1 / factor)
console.log('Prevent acceleration', factor, this.velocity, next) // console.log('Prevent acceleration', factor, this.velocity, next)
} }
this.velocity = next this.velocity = next
let d = Points.multiplyScalar(this.velocity, dt) let d = Points.multiplyScalar(this.velocity, dt)
@ -198,11 +259,11 @@ class Throwable {
this.onDragUpdate(d) this.onDragUpdate(d)
if (dt == 0 || this.needsAnimation()) { if (dt == 0 || this.needsAnimation()) {
requestAnimationFrame(this.animateThrow.bind(this)) thrower.animateThrow(this)
return return
} else { } else {
if (this.isOutside()) { if (this.isOutside()) {
requestAnimationFrame(this.animateThrow.bind(this)) thrower.animateThrow(this)
return return
} }
} }
@ -226,7 +287,7 @@ class Throwable {
let next = Points.multiplyScalar(velocity, this.throwDamping) let next = Points.multiplyScalar(velocity, this.throwDamping)
return { return {
x: this.movableX ? next.x : 0, x: this.movableX ? next.x : 0,
y: this.movableY ? next.y : 0, y: this.movableY ? next.y : 0
} }
} }
@ -271,7 +332,7 @@ export class AbstractScatter extends Throwable {
scaleCloseBuffer = 0.05, scaleCloseBuffer = 0.05,
maxRotation = Angle.degree2radian(5), maxRotation = Angle.degree2radian(5),
minInteractionDistance = 0, minInteractionDistance = 0,
useLowPassFilter = false, useLowPassFilter = false
} = {}) { } = {}) {
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')
@ -286,7 +347,7 @@ export class AbstractScatter extends Throwable {
throwVisibility, throwVisibility,
throwDamping, throwDamping,
autoThrow, autoThrow,
onThrowFinished, onThrowFinished
}) })
/** /**
@ -361,7 +422,7 @@ export class AbstractScatter extends Throwable {
_callCloseCallbacks() { _callCloseCallbacks() {
if (this.onClose) { if (this.onClose) {
this.onClose.forEach((callback) => callback(this)) this.onClose.forEach(callback => callback(this))
} }
} }
@ -381,6 +442,9 @@ export class AbstractScatter extends Throwable {
let delta = interaction.delta() let delta = interaction.delta()
if (delta != null) { if (delta != null) {
/* uo: Is this the best place to add velocity? It works with scrollable text in card drawers but
has to be tested */
this.addVelocity(delta)
let rotate = delta.rotate let rotate = delta.rotate
let zoom = delta.zoom let zoom = delta.zoom
if (this.maxRotation != null) { if (this.maxRotation != null) {
@ -399,9 +463,10 @@ export class AbstractScatter extends Throwable {
zoomDelta *= ratio zoomDelta *= ratio
zoom = 1 + zoomDelta zoom = 1 + zoomDelta
} }
this.transform(delta, zoom, rotate, delta.about) this.transform(delta, zoom, rotate, delta.about)
/* uo: This is too late an dangerous. transform sometimes modifies delta
this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855 this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
*/
if (zoom != 1) this.interactionAnchor = delta.about if (zoom != 1) this.interactionAnchor = delta.about
} }
@ -512,11 +577,11 @@ export class AbstractScatter extends Throwable {
if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
this.zoom(this.minScale, { this.zoom(this.minScale, {
animate: 0.2, animate: 0.2,
onComplete: this.close.bind(this), onComplete: this.close.bind(this)
}) })
} else if (this.scale < this.minScale + this.scaleCloseThreshold) { } else if (this.scale < this.minScale + this.scaleCloseThreshold) {
this.zoom(this.minScale + this.scaleCloseThreshold, { this.zoom(this.minScale + this.scaleCloseThreshold, {
animate: 0.4, animate: 0.4
}) })
} }
} }
@ -538,12 +603,12 @@ export class AbstractScatter extends Throwable {
x: '+=' + d.x, x: '+=' + d.x,
y: '+=' + d.y, y: '+=' + d.y,
/* scale: scale, uo: not defined, why was this here? */ /* scale: scale, uo: not defined, why was this here? */
onUpdate: (e) => { onUpdate: e => {
let p = this.position let p = this.position
let dx = p.x - startPos.x let dx = p.x - startPos.x
let dy = p.x - startPos.y let dy = p.x - startPos.y
this.onMoved(dx, dy) this.onMoved(dx, dy)
}, }
}) })
} else { } else {
this._move(d) this._move(d)
@ -572,7 +637,7 @@ export class AbstractScatter extends Throwable {
scale: scale, scale: scale,
delay: delay, delay: delay,
onComplete: onComplete, onComplete: onComplete,
onUpdate: this.onZoomed.bind(this), onUpdate: this.onZoomed.bind(this)
}) })
} else { } else {
this.scale = scale this.scale = scale
@ -590,7 +655,7 @@ export class AbstractScatter extends Throwable {
transform(translate, zoom, rotate, anchor) { transform(translate, zoom, rotate, anchor) {
let delta = { let delta = {
x: this.movableX ? translate.x : 0, x: this.movableX ? translate.x : 0,
y: this.movableY ? translate.y : 0, y: this.movableY ? translate.y : 0
} }
if (this.resizable) var vzoom = zoom if (this.resizable) var vzoom = zoom
if (!this.translatable) delta = { x: 0, y: 0 } if (!this.translatable) delta = { x: 0, y: 0 }
@ -605,9 +670,9 @@ export class AbstractScatter extends Throwable {
rotate: 0, rotate: 0,
about: anchor, about: anchor,
fast: false, fast: false,
type: UPDATE, type: UPDATE
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -636,9 +701,9 @@ export class AbstractScatter extends Throwable {
translate: delta, translate: delta,
scale: newScale, scale: newScale,
rotate: rotate, rotate: rotate,
about: anchor, about: anchor
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -704,7 +769,7 @@ export class AbstractScatter extends Throwable {
if (this.scale > this.maxScale) zoom = 1 - amount if (this.scale > this.maxScale) zoom = 1 - amount
if (zoom != 1) { if (zoom != 1) {
this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor) this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor)
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.animateZoomBounce(dt) this.animateZoomBounce(dt)
}) })
return return
@ -761,9 +826,9 @@ export class AbstractScatter extends Throwable {
rotate: 0, rotate: 0,
about: null, about: null,
fast: false, fast: false,
type: START, type: START
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -779,13 +844,13 @@ export class AbstractScatter extends Throwable {
} }
onEnd(event, interaction) { onEnd(event, interaction) {
console.log('Scatter.onEnd', this.dragging) //console.log('Scatter.onEnd', this.dragging)
if (interaction.isFinished()) { if (interaction.isFinished()) {
this.endGesture(interaction) this.endGesture(interaction)
this.dragging = false this.dragging = false
for (let key of interaction.ended.keys()) { for (let key of interaction.ended.keys()) {
if (interaction.isTap(key)) { if (interaction.isTap(key)) {
console.log('Scatter.isTap') //console.log('Scatter.isTap')
let point = interaction.ended.get(key) let point = interaction.ended.get(key)
this.onTap(event, interaction, point) this.onTap(event, interaction, point)
} }
@ -797,9 +862,9 @@ export class AbstractScatter extends Throwable {
rotate: 0, rotate: 0,
about: null, about: null,
fast: false, fast: false,
type: END, type: END
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -813,7 +878,7 @@ export class AbstractScatter extends Throwable {
//onTap(event, interaction, point) {} //onTap(event, interaction, point) {}
onTap(event, interaction, point) { onTap(event, interaction, point) {
console.log('AbstractScatter.onTap', this.tapDelegate, interaction) //console.log('AbstractScatter.onTap', this.tapDelegate, interaction)
if (this.tapDelegate) { if (this.tapDelegate) {
Events.stop(event) Events.stop(event)
this.tapDelegate.tap(event, 'scatter') this.tapDelegate.tap(event, 'scatter')
@ -827,9 +892,9 @@ export class AbstractScatter extends Throwable {
translate: delta, translate: delta,
scale: this.scale, scale: this.scale,
about: this.currentAbout, about: this.currentAbout,
type: null, type: null
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -841,9 +906,9 @@ export class AbstractScatter extends Throwable {
scale: this.scale, scale: this.scale,
about: this.currentAbout, about: this.currentAbout,
fast: false, fast: false,
type: null, type: null
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -855,9 +920,9 @@ export class AbstractScatter extends Throwable {
translate: { x: dx, y: dy }, translate: { x: dx, y: dy },
about: about, about: about,
fast: true, fast: true,
type: null, type: null
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -868,9 +933,9 @@ export class AbstractScatter extends Throwable {
let event = new ScatterEvent(this, { let event = new ScatterEvent(this, {
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)
}) })
} }
@ -884,9 +949,9 @@ export class AbstractScatter extends Throwable {
scale: this.scale, scale: this.scale,
about: about, about: about,
fast: false, fast: false,
type: null, type: null
}) })
this.onTransform.forEach(function (f) { this.onTransform.forEach(function(f) {
f(event) f(event)
}) })
} }
@ -918,7 +983,7 @@ export class DOMScatterContainer {
useCapture = true, useCapture = true,
capturePointerEvents = true, capturePointerEvents = true,
touchAction = 'none', touchAction = 'none',
debugCanvas = null, debugCanvas = null
} = {} } = {}
) { ) {
this.onCapture = null this.onCapture = null
@ -930,7 +995,7 @@ export class DOMScatterContainer {
movement of scatter objects, the touchmove event has to be bound again. movement of scatter objects, the touchmove event has to be bound again.
*/ */
if (Capabilities.isSafari) { if (Capabilities.isSafari) {
document.addEventListener('touchmove', (event) => this.preventPinch(event), false) document.addEventListener('touchmove', event => this.preventPinch(event), false)
stopEvents = false stopEvents = false
} else { } else {
stopEvents = true stopEvents = true
@ -945,11 +1010,11 @@ export class DOMScatterContainer {
this.delegate = new InteractionMapper(element, this, { this.delegate = new InteractionMapper(element, this, {
useCapture, useCapture,
capturePointerEvents, capturePointerEvents,
mouseWheelElement: window, mouseWheelElement: window
}) })
if (debugCanvas !== null) { if (debugCanvas !== null) {
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.showTouches(dt, debugCanvas) this.showTouches(dt, debugCanvas)
}) })
} }
@ -971,7 +1036,7 @@ export class DOMScatterContainer {
context.fill() context.fill()
context.stroke() context.stroke()
} }
requestAnimationFrame((dt) => { requestAnimationFrame(dt => {
this.showTouches(dt, canvas) this.showTouches(dt, canvas)
}) })
} }
@ -1113,7 +1178,7 @@ export class DOMScatter extends AbstractScatter {
scaleCloseBuffer = 0.05, scaleCloseBuffer = 0.05,
useLowPassFilter = false, useLowPassFilter = false,
maxRotation = Angle.degree2radian(15), maxRotation = Angle.degree2radian(15),
minInteractionDistance = 200, minInteractionDistance = 200
} = {} } = {}
) { ) {
super({ super({
@ -1140,7 +1205,7 @@ export class DOMScatter extends AbstractScatter {
onClose, onClose,
useLowPassFilter, useLowPassFilter,
maxRotation, maxRotation,
minInteractionDistance, minInteractionDistance
}) })
if (container == null || width == null || height == null) { if (container == null || width == null || height == null) {
throw new Error('Invalid value: null') throw new Error('Invalid value: null')
@ -1168,7 +1233,7 @@ export class DOMScatter extends AbstractScatter {
height: height, height: height,
scale: startScale, scale: startScale,
rotation: this.startRotationDegrees, rotation: this.startRotationDegrees,
transformOrigin: transformOrigin, transformOrigin: transformOrigin
} }
this.tapNodes = new Map() this.tapNodes = new Map()
@ -1190,15 +1255,15 @@ export class DOMScatter extends AbstractScatter {
button.className = 'interactiveElement' button.className = 'interactiveElement'
this.element.appendChild(button) this.element.appendChild(button)
button.addEventListener('pointerdown', (e) => { button.addEventListener('pointerdown', e => {
this.startResize(e) this.startResize(e)
}) })
button.addEventListener('pointermove', (e) => { button.addEventListener('pointermove', e => {
this.resize(e) this.resize(e)
}) })
button.addEventListener('pointerup', (e) => { button.addEventListener('pointerup', e => {
this.stopResize(e) this.stopResize(e)
}) })
this.resizeButton = button this.resizeButton = button
@ -1215,7 +1280,7 @@ export class DOMScatter extends AbstractScatter {
scale: this.scale, scale: this.scale,
x: this.x, x: this.x,
y: this.y, y: this.y,
rotation: this.rotation, rotation: this.rotation
} }
} }
@ -1266,7 +1331,7 @@ export class DOMScatter extends AbstractScatter {
top: rect.top - stage.top, top: rect.top - stage.top,
left: rect.left - stage.left, left: rect.left - stage.left,
width: rect.width, width: rect.width,
height: rect.height, height: rect.height
} }
} }
@ -1307,7 +1372,7 @@ export class DOMScatter extends AbstractScatter {
set scale(scale) { set scale(scale) {
TweenLite.set(this.element, { TweenLite.set(this.element, {
scale: scale, scale: scale,
transformOrigin: this.transformOrigin, transformOrigin: this.transformOrigin
}) })
this._scale = scale this._scale = scale
} }
@ -1339,9 +1404,9 @@ export class DOMScatter extends AbstractScatter {
hide() { hide() {
TweenLite.to(this.element, 0.1, { TweenLite.to(this.element, 0.1, {
display: 'none', display: 'none',
onComplete: (e) => { onComplete: e => {
this.element.parentNode.removeChild(this.element) this.element.parentNode.removeChild(this.element)
}, }
}) })
} }
@ -1355,7 +1420,7 @@ export class DOMScatter extends AbstractScatter {
x: p.x, x: p.x,
y: p.y, y: p.y,
rotation: rotationDegrees, rotation: rotationDegrees,
transformOrigin: this.transformOrigin, transformOrigin: this.transformOrigin
}) })
} }
@ -1416,7 +1481,7 @@ export class DOMScatter extends AbstractScatter {
let oldPostition = { let oldPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
} }
this.bringToFront() this.bringToFront()
@ -1424,7 +1489,7 @@ export class DOMScatter extends AbstractScatter {
let newPostition = { let newPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
} }
let offset = Points.subtract(oldPostition, newPostition) let offset = Points.subtract(oldPostition, newPostition)
@ -1469,7 +1534,7 @@ export class DOMScatter extends AbstractScatter {
) )
TweenLite.to(this.element, 0, { TweenLite.to(this.element, 0, {
width: this.element.offsetWidth + resizeW / this.scale, width: this.element.offsetWidth + resizeW / this.scale,
height: this.element.offsetHeight + resizeH / this.scale, height: this.element.offsetHeight + resizeH / this.scale
}) })
this.oldX = e.clientX this.oldX = e.clientX
@ -1486,12 +1551,12 @@ export class DOMScatter extends AbstractScatter {
let event = new CustomEvent('resizeEnded') let event = new CustomEvent('resizeEnded')
let oldPostition = { let oldPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
} }
this.element.style.transformOrigin = '50% 50%' this.element.style.transformOrigin = '50% 50%'
let newPostition = { let newPostition = {
x: this.element.getBoundingClientRect().left, x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top, y: this.element.getBoundingClientRect().top
} }
let offset = Points.subtract(oldPostition, newPostition) let offset = Points.subtract(oldPostition, newPostition)