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

184
dist/iwmlib.pixi.js vendored
View File

@ -6778,6 +6778,40 @@
*
* @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 {
constructor({
movableX = true,
@ -6785,7 +6819,7 @@
throwVisibility = 44,
throwDamping = 0.95,
autoThrow = true,
onThrowFinished = null,
onThrowFinished = null
} = {}) {
this.movableX = movableX;
this.movableY = movableY;
@ -6794,9 +6828,16 @@
this.autoThrow = autoThrow;
this.velocities = [];
this.velocity = null;
this.timestamp = null;
this.lastframe = null;
this.onThrowFinished = onThrowFinished;
//console.log("onThrowFinished", onThrowFinished)
}
static defaultThrow() {
thrower = new RequestFrameThrower();
}
static timeoutThrow() {
thrower = new TimeoutThrower();
}
observeVelocity() {
@ -6818,7 +6859,7 @@
t: t,
dt: dt,
dx: delta.x / number,
dy: delta.y / number,
dy: delta.y / number
};
this.velocities.push(velocity);
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) {
this.addVelocity({ x: 0, y: 0, number: 1 });
// this.addVelocity({ x: 0, y: 0, number: 1 })
let sum = { x: 0, y: 0 };
let count = 0;
let t = 0;
@ -6849,6 +6905,7 @@
killAnimation() {
this.velocity = null;
this.velocities = [];
this.lastframe = null;
}
startThrow() {
@ -6870,6 +6927,10 @@
return dt
}
recurseAnimateThrow() {
ThrowableObjects.add(this);
}
animateThrow(time) {
if (this.velocity != null) {
let dt = this._throwDeltaTime();
@ -6880,7 +6941,7 @@
if (nextLength > prevLength) {
let factor = nextLength / prevLength;
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;
let d = Points.multiplyScalar(this.velocity, dt);
@ -6888,11 +6949,11 @@
this.onDragUpdate(d);
if (dt == 0 || this.needsAnimation()) {
requestAnimationFrame(this.animateThrow.bind(this));
thrower.animateThrow(this);
return
} else {
if (this.isOutside()) {
requestAnimationFrame(this.animateThrow.bind(this));
thrower.animateThrow(this);
return
}
}
@ -6916,7 +6977,7 @@
let next = Points.multiplyScalar(velocity, this.throwDamping);
return {
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,
maxRotation = Angle.degree2radian(5),
minInteractionDistance = 0,
useLowPassFilter = false,
useLowPassFilter = false
} = {}) {
if (rotationDegrees != null && rotation != null) {
throw new Error('Use rotationDegrees or rotation but not both')
@ -6976,7 +7037,7 @@
throwVisibility,
throwDamping,
autoThrow,
onThrowFinished,
onThrowFinished
});
/**
@ -7051,7 +7112,7 @@
_callCloseCallbacks() {
if (this.onClose) {
this.onClose.forEach((callback) => callback(this));
this.onClose.forEach(callback => callback(this));
}
}
@ -7071,6 +7132,9 @@
let delta = interaction.delta();
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 zoom = delta.zoom;
if (this.maxRotation != null) {
@ -7089,9 +7153,10 @@
zoomDelta *= ratio;
zoom = 1 + zoomDelta;
}
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;
}
@ -7202,11 +7267,11 @@
if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) {
this.zoom(this.minScale, {
animate: 0.2,
onComplete: this.close.bind(this),
onComplete: this.close.bind(this)
});
} else if (this.scale < this.minScale + this.scaleCloseThreshold) {
this.zoom(this.minScale + this.scaleCloseThreshold, {
animate: 0.4,
animate: 0.4
});
}
}
@ -7228,12 +7293,12 @@
x: '+=' + d.x,
y: '+=' + d.y,
/* scale: scale, uo: not defined, why was this here? */
onUpdate: (e) => {
onUpdate: e => {
let p = this.position;
let dx = p.x - startPos.x;
let dy = p.x - startPos.y;
this.onMoved(dx, dy);
},
}
});
} else {
this._move(d);
@ -7262,7 +7327,7 @@
scale: scale,
delay: delay,
onComplete: onComplete,
onUpdate: this.onZoomed.bind(this),
onUpdate: this.onZoomed.bind(this)
});
} else {
this.scale = scale;
@ -7280,7 +7345,7 @@
transform(translate, zoom, rotate, anchor) {
let delta = {
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.translatable) delta = { x: 0, y: 0 };
@ -7295,9 +7360,9 @@
rotate: 0,
about: anchor,
fast: false,
type: UPDATE,
type: UPDATE
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7326,9 +7391,9 @@
translate: delta,
scale: newScale,
rotate: rotate,
about: anchor,
about: anchor
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7394,7 +7459,7 @@
if (this.scale > this.maxScale) zoom = 1 - amount;
if (zoom != 1) {
this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor);
requestAnimationFrame((dt) => {
requestAnimationFrame(dt => {
this.animateZoomBounce(dt);
});
return
@ -7451,9 +7516,9 @@
rotate: 0,
about: null,
fast: false,
type: START,
type: START
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7469,13 +7534,13 @@
}
onEnd(event, interaction) {
console.log('Scatter.onEnd', this.dragging);
//console.log('Scatter.onEnd', this.dragging)
if (interaction.isFinished()) {
this.endGesture(interaction);
this.dragging = false;
for (let key of interaction.ended.keys()) {
if (interaction.isTap(key)) {
console.log('Scatter.isTap');
//console.log('Scatter.isTap')
let point = interaction.ended.get(key);
this.onTap(event, interaction, point);
}
@ -7487,9 +7552,9 @@
rotate: 0,
about: null,
fast: false,
type: END,
type: END
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7503,7 +7568,7 @@
//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) {
Events$1.stop(event);
this.tapDelegate.tap(event, 'scatter');
@ -7517,9 +7582,9 @@
translate: delta,
scale: this.scale,
about: this.currentAbout,
type: null,
type: null
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7531,9 +7596,9 @@
scale: this.scale,
about: this.currentAbout,
fast: false,
type: null,
type: null
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7545,9 +7610,9 @@
translate: { x: dx, y: dy },
about: about,
fast: true,
type: null,
type: null
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7558,9 +7623,9 @@
let event = new ScatterEvent(this, {
scale: this.scale,
fast: false,
type: null,
type: null
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7574,9 +7639,9 @@
scale: this.scale,
about: about,
fast: false,
type: null,
type: null
});
this.onTransform.forEach(function (f) {
this.onTransform.forEach(function(f) {
f(event);
});
}
@ -7623,7 +7688,7 @@
scaleCloseBuffer = 0.05,
useLowPassFilter = false,
maxRotation = Angle.degree2radian(15),
minInteractionDistance = 200,
minInteractionDistance = 200
} = {}
) {
super({
@ -7650,7 +7715,7 @@
onClose,
useLowPassFilter,
maxRotation,
minInteractionDistance,
minInteractionDistance
});
if (container == null || width == null || height == null) {
throw new Error('Invalid value: null')
@ -7678,7 +7743,7 @@
height: height,
scale: startScale,
rotation: this.startRotationDegrees,
transformOrigin: transformOrigin,
transformOrigin: transformOrigin
};
this.tapNodes = new Map();
@ -7700,15 +7765,15 @@
button.className = 'interactiveElement';
this.element.appendChild(button);
button.addEventListener('pointerdown', (e) => {
button.addEventListener('pointerdown', e => {
this.startResize(e);
});
button.addEventListener('pointermove', (e) => {
button.addEventListener('pointermove', e => {
this.resize(e);
});
button.addEventListener('pointerup', (e) => {
button.addEventListener('pointerup', e => {
this.stopResize(e);
});
this.resizeButton = button;
@ -7725,7 +7790,7 @@
scale: this.scale,
x: this.x,
y: this.y,
rotation: this.rotation,
rotation: this.rotation
}
}
@ -7776,7 +7841,7 @@
top: rect.top - stage.top,
left: rect.left - stage.left,
width: rect.width,
height: rect.height,
height: rect.height
}
}
@ -7817,7 +7882,7 @@
set scale(scale) {
TweenLite.set(this.element, {
scale: scale,
transformOrigin: this.transformOrigin,
transformOrigin: this.transformOrigin
});
this._scale = scale;
}
@ -7849,9 +7914,9 @@
hide() {
TweenLite.to(this.element, 0.1, {
display: 'none',
onComplete: (e) => {
onComplete: e => {
this.element.parentNode.removeChild(this.element);
},
}
});
}
@ -7865,7 +7930,7 @@
x: p.x,
y: p.y,
rotation: rotationDegrees,
transformOrigin: this.transformOrigin,
transformOrigin: this.transformOrigin
});
}
@ -7926,7 +7991,7 @@
let oldPostition = {
x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top,
y: this.element.getBoundingClientRect().top
};
this.bringToFront();
@ -7934,7 +7999,7 @@
let newPostition = {
x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top,
y: this.element.getBoundingClientRect().top
};
let offset = Points.subtract(oldPostition, newPostition);
@ -7979,7 +8044,7 @@
)
TweenLite.to(this.element, 0, {
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;
@ -7996,12 +8061,12 @@
let event = new CustomEvent('resizeEnded');
let oldPostition = {
x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top,
y: this.element.getBoundingClientRect().top
};
this.element.style.transformOrigin = '50% 50%';
let newPostition = {
x: this.element.getBoundingClientRect().left,
y: this.element.getBoundingClientRect().top,
y: this.element.getBoundingClientRect().top
};
let offset = Points.subtract(oldPostition, newPostition);
@ -8174,7 +8239,6 @@
tap(event, calledBy = 'unknown') {
if (event.isTrusted) {
let node = this.nearestActive(event);
console.log('tap', node);
this.nodeTapped(node, event);
/* 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) {
// console.log('_calculateCenterRelativeTo', target, image)
let bbox = image.getBBox()
let width = bbox.width
let height = bbox.height

View File

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

View File

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

View File

@ -17,44 +17,60 @@
<div class="flipFace back" style="visibility: hidden"></div>
</div>
<!-- Very tricky problem to scale svgs: see https://css-tricks.com/scale-svg/ -->
<svg
class="flipButton backBtn"
style="visibility: hidden"
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid meet"
>
<!-- SVG viewPort interferes with DOMMatrix calculations: see
https://stackoverflow.com/questions/70696387/how-to-get-transform-matrix-of-a-dom-element-->
<div class="flipButton backBtn" style="visibility:hidden;">
<svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" style="width:inherit; height:inherit;">
<g stroke-width="8" stroke="white">
<circle cx="50" cy="50" r="44" fill="gray" />
<line x1="30" y1="30" x2="70" y2="70" />
<line x1="30" y1="70" x2="70" y2="30" />
<circle cx="50" cy="50" r="44" fill="gray" />
<line x1="30" y1="30" x2="70" y2="70" />
<line x1="30" y1="70" x2="70" y2="30" />
</g>
</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="32" r="7" fill="white" />
<line x1="50" y1="46" x2="50" y2="78" stroke="white" stroke-width="12" />
</svg>
</div>
</template>
</head>
<body onload="Doctest.run()">
<h1><a href="index.html">lib.</a>Flippable</h1>
<p>
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>
<pre><code>
</div>
</template>
</head>
<body onload="Doctest.run()">
<h1>
Flippable
</h1>
<p>
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;div class="flipWrapper"&gt;
&lt;div class="flipCard"&gt;
&lt;div class="flipFace front"&gt;&lt;/div&gt;
&lt;div class="flipFace back" style="visibility:hidden;"&gt;&lt;/div&gt;
&lt;/div>
&lt;svg class="flipButton backBtn" .../&gt;
&lt;svg class="flipButton infoBtn" .../&gt;
&lt;div class="flipButton backBtn" .../&gt;
&lt;svg .../&gt;
&lt;/div>
&lt;div class="flipButton infoBtn" .../&gt;
&lt;svg .../&gt;
&lt;/div>
&lt;/div&gt;
&lt;/template&gt;
</code>

View File

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