Files
2015-09-29 14:36:06 -05:00

134 lines
3.7 KiB
TypeScript

import {Activator} from './activator';
import {removeElement, raf} from '../../util/dom';
import {Animation} from '../../animations/animation';
export class RippleActivator extends Activator {
constructor(app, config) {
super(app, config);
this.ripples = {};
}
downAction(ev, activatableEle, pointerX, pointerY) {
if (this.disableActivated(ev)) return;
super.downAction(ev, activatableEle, pointerX, pointerY);
// create a new ripple element
let clientRect = activatableEle.getBoundingClientRect();
let clientPointerX = (pointerX - clientRect.left);
let clientPointerY = (pointerY - clientRect.top);
let x = Math.max(Math.abs(clientRect.width - clientPointerX), clientPointerX) * 2;
let y = Math.max(Math.abs(clientRect.height - clientPointerY), clientPointerY) * 2;
let diameter = Math.max(Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), 64);
let radius = Math.sqrt(clientRect.width + clientRect.height);
let duration = (1000 * Math.sqrt(radius / TOUCH_DOWN_ACCEL) + 0.5);
let rippleEle = document.createElement('md-ripple');
let eleStyle = rippleEle.style;
eleStyle.width = eleStyle.height = diameter + 'px';
eleStyle.marginTop = eleStyle.marginLeft = -(diameter / 2) + 'px';
eleStyle.left = clientPointerX + 'px';
eleStyle.top = clientPointerY + 'px';
activatableEle.appendChild(rippleEle);
let ripple = this.ripples[Date.now()] = {
ele: rippleEle,
radius: radius,
duration: duration
};
// expand the circle from the users starting point
// start slow, and when they let up, then speed up the animation
ripple.expand = new Animation(rippleEle, {renderDelay: 0});
ripple.expand
.fromTo('scale', '0.001', '1')
.duration(duration)
.playbackRate(EXPAND_DOWN_PLAYBACK_RATE)
.onFinish(()=> {
// finished expanding
ripple.expand && ripple.expand.dispose();
ripple.expand = null;
ripple.expanded = true;
this.next();
})
.play();
this.next();
}
upAction(forceFadeOut) {
this.deactivate();
let ripple;
for (let rippleId in this.ripples) {
ripple = this.ripples[rippleId];
if (!ripple.fade || forceFadeOut) {
// ripple has not been let up yet
// speed up the rate if the animation is still going
setTimeout(() => {
ripple.expand && ripple.expand.playbackRate(EXPAND_OUT_PLAYBACK_RATE);
ripple.fade = new Animation(ripple.ele);
ripple.fade
.fadeOut()
.duration(OPACITY_OUT_DURATION)
.playbackRate(1)
.onFinish(() => {
ripple.fade && ripple.fade.dispose();
ripple.fade = null;
ripple.faded = true;
this.next();
})
.play();
}, 16);
}
}
this.next();
}
next(forceComplete) {
let ripple, rippleEle;
for (let rippleId in this.ripples) {
ripple = this.ripples[rippleId];
if ((ripple.expanded && ripple.faded && ripple.ele) ||
forceComplete || parseInt(rippleId) + 5000 < Date.now()) {
// finished expanding and the user has lifted the pointer
raf(() => {
this.remove(rippleId);
});
}
}
}
clearState() {
this.deactivate();
this.next(true);
}
remove(rippleId) {
let ripple = this.ripples[rippleId];
if (ripple) {
ripple.expand && ripple.expand.dispose();
ripple.fade && ripple.fade.dispose();
removeElement(ripple.ele);
ripple.ele = ripple.expand = ripple.fade = null;
delete this.ripples[rippleId];
}
}
}
const TOUCH_DOWN_ACCEL = 512;
const EXPAND_DOWN_PLAYBACK_RATE = 0.35;
const EXPAND_OUT_PLAYBACK_RATE = 3;
const OPACITY_OUT_DURATION = 750;