just a few minor animation tweaks

This commit is contained in:
Adam Bradley
2015-06-12 10:58:29 -05:00
parent 7839c69a37
commit d2c3ed1125
53 changed files with 784 additions and 641 deletions

View File

@@ -1,28 +1,30 @@
import * as util from 'ionic/util/util';
import {CSS} from '../util/dom';
let registry = {};
let AnimationRegistry = {};
export class Animation {
constructor(el) {
this._el = [];
this._parent = null;
this._children = [];
this._players = [];
const self = this;
this._from = null;
this._to = null;
this._duration = null;
this._easing = null;
this._rate = null;
self._el = [];
self._parent = null;
self._children = [];
self._animations = [];
this._beforeAddCls = [];
this._beforeRmvCls = [];
this._afterAddCls = [];
this._afterRmvCls = [];
self._from = null;
self._to = null;
self._duration = null;
self._easing = null;
self._rate = null;
this.elements(el);
self._bfAdd = [];
self._bfRmv = [];
self._afAdd = [];
self._afRmv = [];
self.elements(el);
}
elements(el) {
@@ -79,12 +81,12 @@ export class Animation {
playbackRate(value) {
if (arguments.length) {
this._rate = value;
var i;
let i;
for (i = 0; i < this._children.length; i++) {
this._children[i].playbackRate(value);
}
for (i = 0; i < this._players.length; i++) {
this._players[i].playbackRate(value);
for (i = 0; i < this._animations.length; i++) {
this._animations[i].playbackRate(value);
}
return this;
}
@@ -107,192 +109,253 @@ export class Animation {
return this;
}
get beforePlay() {
fromTo(property, from, to) {
return this.from(property, from).to(property, to);
}
fadeIn() {
return this.fromTo('opacity', 0, 1);
}
fadeOut() {
return this.fromTo('opacity', 1, 0);
}
get before() {
return {
addClass: (className) => {
this._beforeAddCls.push(className);
this._bfAdd.push(className);
return this;
},
removeClass: (className) => {
this._beforeRmvCls.push(className);
this._bfRmv.push(className);
return this;
}
}
}
get afterFinish() {
get after() {
return {
addClass: (className) => {
this._afterAddCls.push(className);
this._afAdd.push(className);
return this;
},
removeClass: (className) => {
this._afterRmvCls.push(className);
this._afRmv.push(className);
return this;
}
}
}
play() {
let i, l;
const self = this;
const animations = self._animations;
const children = self._children;
let promises = [];
let players = this._players;
let i, l;
for (i = 0, l = this._children.length; i < l; i++) {
promises.push( this._children[i].play() );
}
// the actual play() method which may or may not start async
function beginPlay() {
let i, l;
let promises = [];
if (!players.length) {
// first time played
for (i = 0; i < this._el.length; i++) {
players.push(
new Animate( this._el[i],
this._from,
this._to,
this.duration(),
this.easing(),
this.playbackRate() )
);
for (i = 0, l = children.length; i < l; i++) {
promises.push( children[i].play() );
}
this._onReady();
} else {
// has been paused, now play again
for (i = 0, l = players.length; i < l; i++) {
players[i].play();
for (i = 0, l = animations.length; i < l; i++) {
promises.push( animations[i].play() );
}
return Promise.all(promises);
}
for (i = 0, l = players.length; i < l; i++) {
if (players[i].promise) {
promises.push(players[i].promise);
if (!self._parent) {
// this is the top level animation and is in full control
// of when the async play() should actually kick off
// stage all animations and child animations at their starting point
self.stage();
let resolve;
let promise = new Promise(res => { resolve = res; });
function kickoff() {
beginPlay().then(() => {
for (let i = 0, l = children.length; i < l; i++) {
children[i]._onFinish();
}
self._onFinish();
resolve();
});
}
if (this._duration > 36) {
// begin each animation when everything is rendered in their starting point
// give the browser some time to render everything in place before starting
setTimeout(() => {
kickoff();
}, 36);
} else {
// no need to render everything in there place before animating in
// just kick it off immediately to render them in their "to" locations
kickoff();
}
return promise;
}
let promise = Promise.all(promises);
promise.then(() => {
this._onFinish();
});
return promise;
// this is a child animation, it is told exactly when to
// start by the top level animation
return beginPlay();
}
pause() {
this._hasFinished = false;
stage() {
const self = this;
var i;
for (i = 0; i < this._children.length; i++) {
this._children[i].pause();
}
if (!self._isStaged) {
self._isStaged = true;
for (i = 0; i < this._players.length; i++) {
this._players[i].pause();
}
}
let i, l, j, ele;
progress(value) {
var i;
for (i = 0, l = self._children.length; i < l; i++) {
self._children[i].stage();
}
for (i = 0; i < this._children.length; i++) {
this._children[i].progress(value);
}
for (i = 0; i < self._el.length; i++) {
ele = self._el[i];
if (!this._players.length) {
this.play();
this.pause();
}
for (i = 0; i < this._players.length; i++) {
this._players[i].progress(value);
}
}
_onReady() {
if (!this._hasPlayed) {
this._hasPlayed = true;
var i, j, ele;
for (i = 0; i < this._el.length; i++) {
ele = this._el[i];
for (j = 0; j < this._beforeAddCls.length; j++) {
ele.classList.add(this._beforeAddCls[j]);
for (j = 0; j < self._bfAdd.length; j++) {
ele.classList.add(self._bfAdd[j]);
}
for (j = 0; j < this._beforeRmvCls.length; j++) {
ele.classList.remove(this._beforeRmvCls[j]);
for (j = 0; j < self._bfRmv.length; j++) {
ele.classList.remove(self._bfRmv[j]);
}
}
this.onReady && this.onReady();
if (self._to) {
// only animate the elements if there are defined "to" effects
for (i = 0; i < self._el.length; i++) {
var animation = new Animate( self._el[i],
self._from,
self._to,
self.duration(),
self.easing(),
self.playbackRate() );
if (animation.shouldAnimate) {
self._animations.push(animation);
}
}
}
self.onReady && self.onReady();
}
}
_onFinish() {
if (!this._hasFinished) {
this._hasFinished = true;
const self = this;
let i, j, ele;
var i, j, ele;
if (!self._isFinished) {
self._isFinished = true;
if (this.playbackRate() < 0) {
if (self.playbackRate() < 0) {
// reverse direction
for (i = 0; i < this._el.length; i++) {
ele = this._el[i];
for (i = 0; i < self._el.length; i++) {
ele = self._el[i];
for (j = 0; j < this._beforeAddCls.length; j++) {
ele.classList.remove(this._beforeAddCls[j]);
for (j = 0; j < self._bfAdd.length; j++) {
ele.classList.remove(self._bfAdd[j]);
}
for (j = 0; j < this._beforeRmvCls.length; j++) {
ele.classList.add(this._beforeRmvCls[j]);
for (j = 0; j < self._bfRmv.length; j++) {
ele.classList.add(self._bfRmv[j]);
}
}
} else {
// normal direction
for (i = 0; i < this._el.length; i++) {
ele = this._el[i];
for (i = 0; i < self._el.length; i++) {
ele = self._el[i];
for (j = 0; j < this._afterAddCls.length; j++) {
ele.classList.add(this._afterAddCls[j]);
for (j = 0; j < self._afAdd.length; j++) {
ele.classList.add(self._afAdd[j]);
}
for (j = 0; j < this._afterRmvCls.length; j++) {
ele.classList.remove(this._afterRmvCls[j]);
for (j = 0; j < self._afRmv.length; j++) {
ele.classList.remove(self._afRmv[j]);
}
}
}
this.onFinish && this.onFinish();
self.onFinish && self.onFinish();
}
}
pause() {
this._hasFinished = false;
let i;
for (i = 0; i < this._children.length; i++) {
this._children[i].pause();
}
for (i = 0; i < this._animations.length; i++) {
this._animations[i].pause();
}
}
progress(value) {
let i;
for (i = 0; i < this._children.length; i++) {
this._children[i].progress(value);
}
if (!this._initProgress) {
this._initProgress = true;
this.play();
this.pause();
}
for (i = 0; i < this._animations.length; i++) {
this._animations[i].progress(value);
}
}
dispose() {
var i;
let i;
for (i = 0; i < this._children.length; i++) {
this._children[i].dispose();
}
for (i = 0; i < this._players.length; i++) {
this._players[i].dispose();
for (i = 0; i < this._animations.length; i++) {
this._animations[i].dispose();
}
this._el = this._parent = this._children = this._players = null;
this._el = this._parent = this._children = this._animations = null;
}
/*
STATIC CLASSES
*/
static create(element, name) {
let AnimationClass = registry[name];
let AnimationClass = AnimationRegistry[name];
if (!AnimationClass) {
// couldn't find an animation by the given name
// fallback to just the base Animation class
AnimationClass = Animation;
}
return new AnimationClass(element);
}
static register(name, AnimationClass) {
registry[name] = AnimationClass;
AnimationRegistry[name] = AnimationClass;
}
}
@@ -300,52 +363,67 @@ export class Animation {
class Animate {
constructor(ele, fromEffect, toEffect, duration, easingConfig, playbackRate) {
if (!toEffect) {
// not an actual animation that tweens between from and to properties
// probably just adding/removing CSS classes, so resolve it right away
return;
}
// https://w3c.github.io/web-animations/
// not using the direct API methods because they're still in flux
// however, element.animate() seems locked in and uses the latest
// and correct API methods under the hood, so really doesn't matter
const self = this;
fromEffect = parseEffect(fromEffect);
toEffect = parseEffect(toEffect);
self.toEffect = parseEffect(toEffect);
this._duration = duration;
self.shouldAnimate = (duration > 36);
var easingName = easingConfig && easingConfig.name || 'linear';
var effects = [ convertProperties(fromEffect) ];
if (easingName in EASING_FN) {
insertEffects(effects, fromEffect, toEffect, easingConfig);
} else if (easingName in CUBIC_BEZIERS) {
easingName = 'cubic-bezier(' + CUBIC_BEZIERS[easingName] + ')';
if (!self.shouldAnimate) {
return inlineStyle(ele, self.toEffect);
}
effects.push( convertProperties(toEffect) );
self.ele = ele;
self.resolve;
self.promise = new Promise(res => { self.resolve = res; });
this.player = ele.animate(effects, {
duration: duration || 0,
easing: easingName,
playbackRate: playbackRate || 1
});
// stage where the element will start from
fromEffect = parseEffect(fromEffect);
inlineStyle(ele, fromEffect);
this.promise = new Promise(resolve => {
this.player.onfinish = () => {
resolve();
};
});
self.duration = duration;
self.rate = playbackRate;
self.easing = easingConfig && easingConfig.name || 'linear';
self.effects = [ convertProperties(fromEffect) ];
if (self.easing in EASING_FN) {
insertEffects(self.effects, fromEffect, self.toEffect, easingConfig);
} else if (self.easing in CUBIC_BEZIERS) {
self.easing = 'cubic-bezier(' + CUBIC_BEZIERS[self.easing] + ')';
}
self.effects.push( convertProperties(self.toEffect) );
}
play() {
this.player && this.player.play();
const self = this;
if (self.player) {
self.player.play();
} else {
self.player = self.ele.animate(self.effects, {
duration: self.duration || 0,
easing: self.easing,
playbackRate: self.rate || 1
});
self.player.onfinish = () => {
// lock in where the element will stop at
inlineStyle(self.ele, self.rate < 0 ? self.fromEffect : self.toEffect);
self.resolve();
};
}
return self.promise;
}
pause() {
@@ -355,53 +433,55 @@ class Animate {
progress(value) {
let player = this.player;
if (!player) return;
if (player) {
// passed a number between 0 and 1
value = Math.max(0, Math.min(1, value));
// passed a number between 0 and 1
value = Math.max(0, Math.min(1, value));
if (value >= 1) {
player.currentTime = (this.duration * 0.999);
return player.play();
}
if (value === 1) {
player.currentTime = (this._duration * 0.9999);
player.play();
return;
if (player.playState !== 'paused') {
player.pause();
}
player.currentTime = (this.duration * value);
}
if (player.playState !== 'paused') {
player.pause();
}
player.currentTime = (this._duration * value);
}
playbackRate(value) {
this.rate = value;
if (this.player) {
this.player.playbackRate = value;
}
}
dispose() {
this.player = null;
this.ele = this.player = this.effects = this.toEffect = null;
}
}
function insertEffects(effects, fromEffect, toEffect, easingConfig) {
easingConfig.opts = easingConfig.opts || {};
var increment = easingConfig.opts.increment || 0.04;
var easingFn = EASING_FN[easingConfig.name];
const increment = easingConfig.opts.increment || 0.04;
const easingFn = EASING_FN[easingConfig.name];
for(var pos = increment; pos <= (1 - increment); pos += increment) {
let pos, tweenEffect, addEffect, property, toProperty, fromValue, diffValue;
var tweenEffect = {};
var addEffect = false;
for(pos = increment; pos <= (1 - increment); pos += increment) {
tweenEffect = {};
addEffect = false;
for (property in toEffect) {
toProperty = toEffect[property];
for (var property in toEffect) {
var toProperty = toEffect[property];
if (toProperty.tween) {
var fromValue = fromEffect[property].num
var diffValue = toProperty.num - fromValue;
fromValue = fromEffect[property].num
diffValue = toProperty.num - fromValue;
tweenEffect[property] = {
value: roundValue( (easingFn(pos, easingConfig.opts) * diffValue) + fromValue ) + toProperty.unit
@@ -419,8 +499,8 @@ function insertEffects(effects, fromEffect, toEffect, easingConfig) {
}
function parseEffect(inputEffect) {
var val, r, num, property;
var outputEffect = {};
let val, r, num, property;
let outputEffect = {};
for (property in inputEffect) {
val = inputEffect[property];
@@ -439,11 +519,12 @@ function parseEffect(inputEffect) {
}
function convertProperties(inputEffect) {
var outputEffect = {};
var transforms = [];
let outputEffect = {};
let transforms = [];
let value, property;
for (var property in inputEffect) {
var value = inputEffect[property].value;
for (property in inputEffect) {
value = inputEffect[property].value;
if (TRANSFORMS.indexOf(property) > -1) {
transforms.push(property + '(' + value + ')');
@@ -454,12 +535,36 @@ function convertProperties(inputEffect) {
}
if (transforms.length) {
transforms.push('translateZ(0px)');
outputEffect.transform = transforms.join(' ');
}
return outputEffect;
}
function inlineStyle(ele, effect) {
if (ele && effect) {
let transforms = [];
let value, property;
for (property in effect) {
value = effect[property].value;
if (TRANSFORMS.indexOf(property) > -1) {
transforms.push(property + '(' + value + ')');
} else {
ele.style[property] = value;
}
}
if (transforms.length) {
transforms.push('translateZ(0px)');
ele.style[CSS.transform] = transforms.join(' ');
}
}
}
function roundValue(val) {
return Math.round(val * 10000) / 10000;
}
@@ -519,7 +624,7 @@ const CUBIC_BEZIERS = {
// Back
'ease-in-back': '0.6,-0.28,0.735,0.045',
'ease-out-back': '0.175, 0.885,0.32,1.275',
'ease-out-back': '0.175,0.885,0.32,1.275',
'ease-in-out-back': '0.68,-0.55,0.265,1.55',
};
@@ -531,18 +636,18 @@ const EASING_FN = {
},
'swing-from-to': function(pos, opts) {
var s = opts.s || 1.70158;
let s = opts.s || 1.70158;
return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) :
0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2);
},
'swing-from': function(pos, opts) {
var s = opts.s || 1.70158;
let s = opts.s || 1.70158;
return pos * pos * ((s + 1) * pos - s);
},
'swing-to': function(pos, opts) {
var s = opts.s || 1.70158;
let s = opts.s || 1.70158;
return (pos -= 1) * pos * ((s + 1) * pos + s) + 1;
},
@@ -597,8 +702,8 @@ const EASING_FN = {
* https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js
*/
'spring': function(pos, opts) {
var damping = opts.damping || 4.5;
var elasticity = opts.elasticity || 6;
let damping = opts.damping || 4.5;
let elasticity = opts.elasticity || 6;
return 1 - (Math.cos(pos * damping * Math.PI) * Math.exp(-pos * elasticity));
},

View File

@@ -8,22 +8,43 @@ class SlideIn extends Animation {
this
.easing('cubic-bezier(0.1,0.7,0.1,1)')
.duration(400)
.from('translateY', '100%')
.to('translateY', '0%');
.fromTo('translateY', '100%', '0%');
}
}
Animation.register('slide-in', SlideIn);
class SlideOut extends Animation {
constructor(element) {
super(element);
this
.easing('ease-out')
.duration(250)
.from('translateY', '0%')
.to('translateY', '100%');
.fromTo('translateY', '0%', '100%');
}
}
Animation.register('slide-out', SlideOut);
class FadeIn extends Animation {
constructor(element) {
super(element);
this
.easing('ease-in')
.duration(400)
.fadeIn();
}
}
Animation.register('fade-in', FadeIn);
class FadeOut extends Animation {
constructor(element) {
super(element);
this
.easing('ease-out')
.duration(250)
.fadeOut();
}
}
Animation.register('fade-out', FadeOut);

View File

@@ -59,12 +59,11 @@ ion-pane {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
display:none;
&.show-pane {
display: flex;
}
transform: translateZ(0px);
}
.nav-item {
@@ -92,6 +91,8 @@ ion-navbar {
min-height: 4.4rem;
order: $flex-order-toolbar-top;
transform: translateZ(0px);
display: none;
&.show-navbar {
display: flex;
@@ -127,11 +128,11 @@ ion-content {
// Hardware Acceleration
.transitioning {
ion-view,
.ion-view,
.nav-item,
ion-navbar,
.back-button,
ion-title {
.navbar-title,
.navbar-item {
will-change: transform, opacity;
}

View File

@@ -28,7 +28,7 @@ class IonicApp {
.from('translateX', '0px')
.to('translateX', '250px')
this.animation.addChild(ball);
this.animation.add(ball);
var row1 = new Animation( document.querySelectorAll('.square') );
@@ -36,7 +36,7 @@ class IonicApp {
.from('opacity', 0.8)
.to('opacity', 0.2)
this.animation.addChild(row1);
this.animation.add(row1);
var row2 = new Animation( document.querySelectorAll('.square2') );
row2
@@ -44,10 +44,10 @@ class IonicApp {
.from('scale', '1')
.to('rotate', '90deg')
.to('scale', '0.5')
.beforePlay.addClass('added-before-play')
.afterFinish.addClass('added-after-finish')
.before.addClass('added-before-play')
.after.addClass('added-after-finish')
this.animation.children(row1, row2);
this.animation.add(row1, row2);
this.animation.onReady = () => {
console.log('onReady');

View File

@@ -1,7 +1,6 @@
<html>
<head>
<title>Animation Tests</title>
<script src="./web-animations-js/web-animations-next.dev.js"></script>
<style>
.ball-container {
position: absolute;

View File

@@ -97,8 +97,8 @@ $content-padding: 10px !default;
// --------------------------------------------------
$swipe-handle-width: 20px !default;
$swipe-handle-top: 80px !default;
$swipe-handle-bottom: 80px !default;
$swipe-handle-top: 70px !default;
$swipe-handle-bottom: 70px !default;
.swipe-handle {
position: absolute;
@@ -108,22 +108,11 @@ $swipe-handle-bottom: 80px !default;
width: $swipe-handle-width;
z-index: $z-index-swipe-handle;
background: red;
opacity: 0.2;
//background: red;
//opacity: 0.2;
transform: translate3d(-999px, 0px, 0px);
&.show-handle {
transform: translate3d(0px, 0px, 0px);
}
}
// Node Inserted Animation
// --------------------------------------------------
// Used by the toolbar to know when the title has been rendered
// http://davidwalsh.name/detect-node-insertion
@keyframes nodeInserted {
from { transform: scale(0.999); }
to { transform: scale(1); }
}

View File

@@ -6,7 +6,7 @@ $content-container-ios-background-color: #000 !default;
$content-ios-background-color: #efeff4 !default;
.nav-ios {
.nav-ios.show-pane {
.content-container {
background-color: $content-container-ios-background-color;

View File

@@ -115,14 +115,16 @@ class ModalContainer {
open(animation) {
console.log('Opening w/ anim', animation);
var slideIn = Animation.create(this.domElement, animation);
return slideIn.play();
var enterAnimation = Animation.create(this.domElement, animation);
enterAnimation.before.addClass('show-modal');
return enterAnimation.play();
}
close(animation) {
console.log('Closing w/ anim', animation);
var slideOut = Animation.create(this.domElement, animation);
return slideOut.play();
var leavingAnimation = Animation.create(this.domElement, animation);
leavingAnimation.after.removeClass('show-modal');
return leavingAnimation.play();
}
}

View File

@@ -36,7 +36,6 @@ $tabs-height: 40;
}
ion-modal {
display: block;
position: absolute;
top: 0;
z-index: $z-index-modal;
@@ -45,23 +44,14 @@ ion-modal {
width: 100%;
background-color: $modal-bg-color;
//transform: translate3d(0, 100%, 0);
//transition: all cubic-bezier(.1, .7, .1, 1) 400ms;
transform: translateZ(0px);
display: none;
&.show-modal {
display: block;
}
}
// Slide up from the bottom, used for modals
// -------------------------------
ion-modal.active {
//transform: translate3d(0, 0, 0);
}
/*
ion-modal.slide-in-up.ng-leave,
ion-modal.slide-in-up > .ng-leave {
transition: all ease-in-out 250ms;
}
*/
@media (min-width: $modal-inset-mode-break-point) {

View File

@@ -1,63 +0,0 @@
import {Component} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {IonicComponent} from 'ionic/config/component';
import {ViewItem} from '../view/view-item';
@Component({
selector: 'back-button',
hostProperties: {
'!item.enableBack': 'hidden'
},
hostListeners: {
'^click': 'goBack($event)'
},
})
@View({
template: `
<icon class="back-button-icon ion-ios-arrow-back"></icon>
<span class="back-button-text">
<span class="back-default">Back</span>
<span class="back-title"></span>
</span>`
})
export class BackButton {
constructor(item: ViewItem, @ElementRef() element: ElementRef) {
this.item = item;
this.domElement = element.domElement;
setTimeout(() => {
// HACK!
this.config = BackButton.config.invoke(this);
});
}
goBack(ev) {
ev.stopPropagation();
ev.preventDefault();
let item = this.item;
item && item.viewCtrl && item.viewCtrl.pop();
}
}
new IonicComponent(BackButton, {
properties: {
icon: {
defaults: {
ios: 'ion-ios-arrow-back',
android: 'ion-android-arrow-back',
core: 'ion-chevron-left'
}
},
text: {
defaults: {
ios: 'Back',
android: '',
core: ''
}
}
}
});

View File

@@ -47,12 +47,12 @@ $navbar-ios-button-background-color: transparent !default;
ion-title {
order: map-get($navbar-order-ios, 'title');
text-align: center;
font-size: $navbar-ios-title-font-size;
font-weight: 500;
text-align: center;
}
.navbar-back-button {
.back-button {
order: map-get($navbar-order-ios, 'back-button');
}

View File

@@ -1,11 +1,10 @@
import {Component, Directive, onInit} from 'angular2/src/core/annotations_impl/annotations';
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {ViewItem} from '../view/view-item';
import {BackButton} from './back-button';
import * as dom from '../../util/dom';
@@ -15,9 +14,15 @@ import * as dom from '../../util/dom';
@View({
template: `
<div class="navbar-inner">
<back-button class="button navbar-item"></back-button>
<button class="back-button button">
<icon class="back-button-icon ion-ios-arrow-back"></icon>
<span class="back-button-text">
<span class="back-default">Back</span>
<span class="back-title"></span>
</span>
</button>
<div class="navbar-title">
<div class="navbar-inner-title navbar-title-hide">
<div class="navbar-inner-title">
<content select="ion-title"></content>
</div>
</div>
@@ -29,70 +34,50 @@ import * as dom from '../../util/dom';
</div>
</div>
`,
directives: [BackButton],
lifecycle: [onInit]
directives: [BackButton, Title, NavbarItem]
})
export class Navbar {
constructor(item: ViewItem, elementRef: ElementRef, ngZone: NgZone) {
constructor(item: ViewItem, elementRef: ElementRef) {
item.navbarElement(elementRef.domElement);
}
}
@Directive({
selector: '.back-button',
hostListeners: {
'^click': 'goBack($event)'
}
})
class BackButton {
constructor(item: ViewItem, elementRef: ElementRef) {
this.item = item;
this.domElement = elementRef.domElement;
this.zone = ngZone;
item.backButtonElement(elementRef.domElement);
}
onInit() {
this.zone.runOutsideAngular(() => {
setTimeout(() => {
this.alignTitle();
}, 32);
});
goBack(ev) {
ev.stopPropagation();
ev.preventDefault();
this.item.viewCtrl.pop();
}
}
alignTitle() {
const navbarEle = this.domElement;
const innerTitleEle = this._innerTitleEle || (this._innerTitleEle = navbarEle.querySelector('.navbar-inner-title'));
const titleEle = this._titleEle || (this._titleEle = innerTitleEle.querySelector('ion-title'));
const style = this._style || (this._style = window.getComputedStyle(titleEle));
const titleOffsetWidth = titleEle.offsetWidth;
const titleOffsetLeft = titleEle.offsetLeft;
const titleScrollWidth = titleEle.scrollWidth;
const navbarOffsetWidth = navbarEle.offsetWidth;
// TODO!!! When an element is being reused by angular2, it'll sometimes keep the
// styles from the original element's use, causing these calculations to be wrong
if (window.getComputedStyle(innerTitleEle).margin !== '0px') {
this._showTitle();
return;
}
// only align if the title is center and if it isn't already overflowing
if (style.textAlign !== 'center' || titleOffsetWidth < titleScrollWidth) {
this._showTitle();
} else {
let rightMargin = navbarOffsetWidth - (titleOffsetLeft + titleOffsetWidth);
let centerMargin = titleOffsetLeft - rightMargin;
innerTitleEle.style.margin = `0 ${centerMargin}px 0 0`;
dom.raf(() => {
if (titleEle.offsetWidth < titleEle.scrollWidth) {
// not enough room yet, just left align title
innerTitleEle.style.margin = '';
innerTitleEle.style.textAlign = 'left';
}
this._showTitle();
})
}
@Directive({
selector: '.navbar-title'
})
export class Title {
constructor(item: ViewItem, elementRef: ElementRef) {
item.titleElement(elementRef.domElement);
}
}
_showTitle() {
if (!this._shown) {
this._shown = true;
this._innerTitleEle.classList.remove('navbar-title-hide');
}
@Directive({
selector: '.navbar-item'
})
export class NavbarItem {
constructor(item: ViewItem, elementRef: ElementRef) {
item.navbarItemElements(elementRef.domElement);
}
}

View File

@@ -21,8 +21,14 @@ ion-navbar {
width: 100%;
}
ion-navbar back-button.navbar-item {
.back-button {
order: map-get($navbar-order, 'back-button');
transform: translateZ(0px);
display: none;
&.show-back-button {
display: inline-flex;
}
}
.navbar-title {
@@ -31,7 +37,29 @@ ion-navbar back-button.navbar-item {
display: flex;
align-items: center;
margin: 0 0 2px;
transform: translateZ(0px);
}
.navbar-inner-title {
width: 100%;
padding: 0 15px;
}
ion-title {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.navbar-item {
transform: translateZ(0px);
display: none;
&.show-navbar-item {
display: block;
}
}
.navbar-primary-item {
@@ -42,27 +70,7 @@ ion-navbar back-button.navbar-item {
order: map-get($navbar-order, 'secondary');
}
ion-title {
display: block;
overflow: hidden;
// used to notify JS when the title has been rendered
animation-duration: 1ms;
animation-name: nodeInserted;
}
.navbar-inner-title {
width: 100%;
padding: 0 15px;
white-space: nowrap;
text-overflow: ellipsis;
}
.navbar .button {
background: transparent;
border: none;
}
.navbar-title-hide {
opacity: 0;
}

View File

@@ -20,71 +20,76 @@ export class SwipeHandle {
@Parent() pane: Pane,
elementRef: ElementRef
) {
if (!viewCtrl) return;
if (!viewCtrl || !pane) return;
this.viewCtrl = viewCtrl;
const self = this;
let gesture = new Gesture(elementRef.domElement);
self.pane = pane;
self.viewCtrl = viewCtrl;
let gesture = self.gesture = new Gesture(elementRef.domElement);
gesture.listen();
gesture.on('panend', onDragEnd);
gesture.on('panleft', onDragHorizontal);
gesture.on('panright', onDragHorizontal);
function dragHorizontal(ev) {
self.onDragHorizontal(ev);
}
let startX = null;
let swipeableAreaWidth = null;
gesture.on('panend', ev => { self.onDragEnd(ev); });
gesture.on('panleft', dragHorizontal);
gesture.on('panright', dragHorizontal);
function onDragEnd(ev) {
self.startX = null;
self.width = null;
}
onDragEnd(ev) {
ev.preventDefault();
ev.stopPropagation();
// TODO: POLISH THESE NUMBERS WITH GOOD MATHIFICATION
let progress = (ev.gesture.center.x - this.startX) / this.width;
let completeSwipeBack = (progress > 0.5);
let playbackRate = 4;
if (completeSwipeBack) {
// complete swipe back
if (progress > 0.9) {
playbackRate = 1;
} else if (progress > 0.8) {
playbackRate = 2;
} else if (progress > 0.7) {
playbackRate = 3;
}
} else {
// cancel swipe back
if (progress < 0.1) {
playbackRate = 1;
} else if (progress < 0.2) {
playbackRate = 2;
} else if (progress < 0.3) {
playbackRate = 3;
}
}
this.viewCtrl.swipeBackEnd(completeSwipeBack, progress, playbackRate);
this.startX = null;
}
onDragHorizontal(ev) {
if (this.startX === null) {
// starting drag
ev.preventDefault();
ev.stopPropagation();
// TODO: POLISH THESE NUMBERS WITH GOOD MATHIFICATION
this.startX = ev.gesture.center.x;
this.width = this.pane.width() - this.startX;
let progress = (ev.gesture.center.x - startX) / swipeableAreaWidth;
let completeSwipeBack = (progress > 0.5);
let playbackRate = 4;
if (completeSwipeBack) {
// complete swipe back
if (progress > 0.9) {
playbackRate = 1;
} else if (progress > 0.8) {
playbackRate = 2;
} else if (progress > 0.7) {
playbackRate = 3;
}
} else {
// cancel swipe back
if (progress < 0.1) {
playbackRate = 1;
} else if (progress < 0.2) {
playbackRate = 2;
} else if (progress < 0.3) {
playbackRate = 3;
}
}
viewCtrl.swipeBackEnd(completeSwipeBack, progress, playbackRate);
startX = null;
}
function onDragHorizontal(ev) {
if (startX === null) {
// starting drag
ev.preventDefault();
ev.stopPropagation();
startX = ev.gesture.center.x;
swipeableAreaWidth = pane.width() - startX;
viewCtrl.swipeBackStart();
}
viewCtrl.swipeBackProgress( (ev.gesture.center.x - startX) / swipeableAreaWidth );
this.viewCtrl.swipeBackStart();
}
this.viewCtrl.swipeBackProgress( (ev.gesture.center.x - this.startX) / this.width );
}
showHandle() {

View File

@@ -19,6 +19,9 @@ import {ThirdPage} from './third-page';
<p>
<button class="button" (click)="push()">Push (Go to 3rd)</button>
</p>
<p>
Random: {{ val }}
</p>
<div class="green"><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f></div>
</ion-content>
`,
@@ -31,6 +34,7 @@ export class SecondPage {
) {
this.nav = nav;
this.params = params;
this.val = Math.round(Math.random() * 8999) + 1000;
console.log('Second page params:', params);
}

View File

@@ -38,13 +38,9 @@ ion-toolbar {
order: map-get($toolbar-order, 'secondary');
}
ion-title {
ion-toolbar ion-title {
display: block;
overflow: hidden;
// used to notify JS when the title has been rendered
animation-duration: 1ms;
animation-name: nodeInserted;
}
.toolbar-inner-title {

View File

@@ -132,42 +132,35 @@ export class ViewController {
enteringItem.willEnter();
leavingItem.willLeave();
// set that the leaving item is stage to be leaving
leavingItem.state = STAGED_LEAVING_STATE;
// set that the new item pushed on the stack is staged to be entering
// setting staged state is important for the transition logic to find the correct item
// set that the new item pushed on the stack is staged to be entering/leaving
// staged state is important for the transition to find the correct item
enteringItem.state = STAGED_ENTERING_STATE;
leavingItem.state = STAGED_LEAVING_STATE;
// init the transition animation
let transAnimation = Transition.create(this, opts);
if (!opts.animate) {
// force it to not animate the elements, just apply the "to" styles
transAnimation.duration(0);
}
// wait for the items to be fully staged
transAnimation.stage(() => {
// start the transition
transAnimation.play().then(() => {
// update the state that the items are actively entering/leaving
enteringItem.state = ACTIVELY_ENTERING_STATE;
leavingItem.state = ACTIVELY_LEAVING_STATE;
// transition has completed, update each item's state
enteringItem.state = ACTIVE_STATE;
leavingItem.state = CACHED_STATE;
// start the transition
transAnimation.play().then(() => {
// dispose any items that shouldn't stay around
transAnimation.dispose();
// transition has completed, update each item's state
enteringItem.state = ACTIVE_STATE;
leavingItem.state = CACHED_STATE;
enteringItem.didEnter();
leavingItem.didLeave();
// dispose any items that shouldn't stay around
transAnimation.dispose();
enteringItem.didEnter();
leavingItem.didLeave();
// all done!
this.transitionComplete();
callback();
});
// all done!
this.transitionComplete();
callback();
});
});
@@ -206,53 +199,43 @@ export class ViewController {
// wait for the new item to complete setup
enteringItem.stage(() => {
// set that the leaving item is stage to be leaving
leavingItem.state = STAGED_LEAVING_STATE;
// set that the new item pushed on the stack is staged to be entering
// setting staged state is important for the transition logic to find the correct item
// set that the new item pushed on the stack is staged to be entering/leaving
// staged state is important for the transition to find the correct item
enteringItem.state = STAGED_ENTERING_STATE;
leavingItem.state = STAGED_LEAVING_STATE;
// init the transition animation
this.sbTransition = Transition.create(this, opts);
this.sbTransition.easing('linear');
this.sbTransition.stage();
// wait for the items to be fully staged
this.sbTransition.stage(() => {
let swipeBackPromise = new Promise(res => { this.sbResolve = res; });
// update the state that the items are actively entering/leaving
enteringItem.state = ACTIVELY_ENTERING_STATE;
leavingItem.state = ACTIVELY_LEAVING_STATE;
swipeBackPromise.then((completeSwipeBack) => {
console.log('completeSwipeBack', completeSwipeBack)
if (completeSwipeBack) {
// swipe back has completed, update each item's state
enteringItem.state = ACTIVE_STATE;
leavingItem.state = CACHED_STATE;
let swipeBackPromise = new Promise(res => { this.sbResolve = res; });
enteringItem.didEnter();
leavingItem.didLeave();
swipeBackPromise.then((completeSwipeBack) => {
} else {
// cancelled the swipe back, return items to original state
leavingItem.state = ACTIVE_STATE;
enteringItem.state = CACHED_STATE;
if (completeSwipeBack) {
// swipe back has completed, update each item's state
enteringItem.state = ACTIVE_STATE;
leavingItem.state = CACHED_STATE;
leavingItem.willEnter();
leavingItem.didEnter();
enteringItem.didLeave();
enteringItem.didEnter();
leavingItem.didLeave();
leavingItem.shouldDestroy = false;
enteringItem.shouldDestroy = false;
}
} else {
// cancelled the swipe back, return items to original state
leavingItem.state = ACTIVE_STATE;
enteringItem.state = CACHED_STATE;
leavingItem.willEnter();
leavingItem.didEnter();
enteringItem.didLeave();
leavingItem.shouldDestroy = false;
enteringItem.shouldDestroy = false;
}
// all done!
this.transitionComplete();
});
// all done!
this.transitionComplete();
});
@@ -260,6 +243,13 @@ export class ViewController {
}
swipeBackProgress(progress) {
if (this.sbTransition) {
ClickBlock(true, 4000);
this.sbTransition.progress( Math.min(1, Math.max(0, progress)) );
}
}
swipeBackEnd(completeSwipeBack, progress, playbackRate) {
// to reverse the animation use a negative playbackRate
if (this.sbTransition && this.sbActive) {
@@ -285,13 +275,6 @@ export class ViewController {
}
}
swipeBackProgress(progress) {
if (this.sbTransition) {
ClickBlock(true, 4000);
this.sbTransition.progress( Math.min(1, Math.max(0, progress)) );
}
}
swipeBackEnabled() {
if (this.items.length > 1) {
let activeItem = this.getActive();
@@ -332,9 +315,7 @@ export class ViewController {
let state;
for (let i = 0, ii = this.items.length; i < ii; i++) {
state = this.items[i].state;
if (state === ACTIVELY_ENTERING_STATE ||
state === ACTIVELY_LEAVING_STATE ||
state === STAGED_ENTERING_STATE ||
if (state === STAGED_ENTERING_STATE ||
state === STAGED_LEAVING_STATE) {
return true;
}
@@ -460,7 +441,5 @@ const ACTIVE_STATE = 1;
const CACHED_STATE = 2;
const STAGED_ENTERING_STATE = 3;
const STAGED_LEAVING_STATE = 4;
const ACTIVELY_ENTERING_STATE = 5;
const ACTIVELY_LEAVING_STATE = 6;
let itemIds = -1;

View File

@@ -15,15 +15,13 @@ export class ViewItem {
this.params = new NavParams(params);
this.instance = null;
this.state = 0;
this._titleEle = undefined;
this._backBtn = undefined;
this.disposals = [];
// if it's possible to go back from this nav item
this.enableBack = false;
this.protos = {};
this._nbItms = [];
}
addProtoViewRef(name, protoViewRef) {
@@ -92,10 +90,10 @@ export class ViewItem {
// add a navbar view if the pane has a navbar container, and the
// item's instance has a navbar protoview to go to inside of it
if (navbarViewContainer && navbarProtoView) {
this.navbarView = navbarViewContainer.create(navbarProtoView, -1, context, injector);
let navbarView = navbarViewContainer.create(navbarProtoView, -1, context, injector);
this.disposals.push(() => {
navbarViewContainer.remove( navbarViewContainer.indexOf(this.navbarView) );
navbarViewContainer.remove( navbarViewContainer.indexOf(navbarView) );
});
}
@@ -199,44 +197,31 @@ export class ViewItem {
}
navbarElement() {
let navbarView = this.navbarView;
if (navbarView && navbarView._view) {
return navbarView._view.render._view.rootNodes[0];
if (arguments.length) {
this._nbEle = arguments[0];
}
}
contentElement() {
return this.viewEle.querySelector('ion-content');
return this._nbEle;
}
titleElement() {
if (this._titleEle === undefined) {
let navbarElement = this.navbarElement();
if (navbarElement) {
let titleEle = navbarElement.querySelector('ion-title');
if (titleEle) {
this._titleEle = titleEle;
return this._titleEle;
}
}
this._titleEle = null;
if (arguments.length) {
this._ttEle = arguments[0];
}
return this._titleEle;
return this._ttEle;
}
backButtonElement() {
if (this._backBtn === undefined) {
let navbarElement = this.navbarElement();
if (navbarElement) {
let backBtn = navbarElement.querySelector('back-button');
if (backBtn) {
this._backBtn = backBtn;
return this._backBtn;
}
}
this._backBtn = null;
if (arguments.length) {
this._bbEle = arguments[0];
}
return this._backBtn;
return this._bbEle;
}
navbarItemElements() {
if (arguments.length) {
this._nbItms.push(arguments[0]);
}
return this._nbItms;
}

View File

@@ -9,4 +9,6 @@ ion-view {
flex-direction: column;
background-color: white;
transform: translateZ(0px);
}

View File

@@ -2,7 +2,7 @@ import {Transition} from './transition';
import {Animation} from '../animations/animation';
const DURATION = 500;
const DURATION = 1000;
const EASING = 'cubic-bezier(0.36,0.66,0.04,1)';
const OPACITY = 'opacity';
@@ -19,77 +19,63 @@ class IOSTransition extends Transition {
constructor(nav, opts) {
super(nav, opts);
const self = this;
// global duration and easing for all child animations
this.duration(DURATION);
this.easing(EASING);
self.duration(DURATION);
self.easing(EASING);
// entering item moves to center
this.enteringView
self.enteringView
.to(TRANSLATEX, CENTER)
.to(OPACITY, 1);
this.enteringTitle
.from(OPACITY, 0)
.to(OPACITY, 1)
self.enteringTitle
.fadeIn()
.to(TRANSLATEX, CENTER);
// if the back button should show, then fade it in
if (this.entering.enableBack) {
let enteringBackButton = new Animation(this.entering.backButtonElement())
enteringBackButton
.from(OPACITY, 0)
.to(OPACITY, 1);
this.add(enteringBackButton);
}
// leaving view moves off screen
this.leavingView
self.leavingView
.from(TRANSLATEX, CENTER)
.from(OPACITY, 1);
this.leavingTitle
self.leavingTitle
.from(TRANSLATEX, CENTER)
.from(OPACITY, 1);
let leavingBackButton = new Animation(this.leaving.backButtonElement());
leavingBackButton
.from(OPACITY, 1)
.to(OPACITY, 0);
this.add(leavingBackButton);
// set properties depending on direction
if (opts.direction === 'back') {
// back direction
this.enteringView
self.enteringView
.from(TRANSLATEX, OFF_LEFT)
.from(OPACITY, OFF_OPACITY)
.to(OPACITY, 1);
this.enteringTitle
self.enteringTitle
.from(TRANSLATEX, OFF_LEFT);
this.leavingView
self.leavingView
.to(TRANSLATEX, OFF_RIGHT)
.to(OPACITY, 1);
this.leavingTitle
self.leavingTitle
.to(TRANSLATEX, OFF_RIGHT)
.to(OPACITY, 0);
} else {
// forward direction
this.enteringView
self.enteringView
.from(TRANSLATEX, OFF_RIGHT)
.from(OPACITY, 1);
this.enteringTitle
self.enteringTitle
.from(TRANSLATEX, OFF_RIGHT);
this.leavingView
self.leavingView
.to(TRANSLATEX, OFF_LEFT)
.to(OPACITY, OFF_OPACITY);
this.leavingTitle
self.leavingTitle
.to(TRANSLATEX, OFF_LEFT)
.to(OPACITY, 0);
}

View File

@@ -1,67 +1,93 @@
import {Animation} from '../animations/animation';
import {raf} from '../util/dom';
const SHOW_NAVBAR_CSS = 'show-navbar';
const SHOW_VIEW_CSS = 'show-view';
const SHOW_BACK_BUTTON = 'show-back-button';
const SHOW_NAVBAR_ITEM = 'show-navbar-item';
let TransitionRegistry = {};
let registry = {};
export class Transition extends Animation {
constructor(nav, opts) {
super();
const self = this;
// get the entering and leaving items
let enteringItem = this.entering = nav.getStagedEnteringItem();
let leavingItem = this.leaving = nav.getStagedLeavingItem();
let enteringItem = self.entering = nav.getStagedEnteringItem();
let leavingItem = self.leaving = nav.getStagedLeavingItem();
// create animation for the entering item's "ion-view" element
this.enteringView = new Animation(enteringItem.viewElement());
this.enteringView.beforePlay.addClass(SHOW_VIEW_CSS);
this.add(this.enteringView);
self.enteringView = new Animation(enteringItem.viewElement());
self.enteringView.before.addClass(SHOW_VIEW_CSS);
self.add(self.enteringView);
// create animation for the entering item's "ion-navbar" element
if (opts.navbar !== false) {
this.enteringNavbar = new Animation(enteringItem.navbarElement());
this.enteringNavbar.beforePlay.addClass(SHOW_NAVBAR_CSS);
let enteringNavbar = self.enteringNavbar = new Animation(enteringItem.navbarElement());
enteringNavbar.before.addClass(SHOW_NAVBAR_CSS);
if (enteringItem.enableBack) {
// only animate in the back button if the entering view has it enabled
let enteringBackButton = self.enteringBackButton = new Animation(enteringItem.backButtonElement());
enteringBackButton
.before.addClass(SHOW_BACK_BUTTON)
.fadeIn();
enteringNavbar.add(enteringBackButton);
}
// create animation for the entering item's "ion-title" element
this.enteringTitle = new Animation(enteringItem.titleElement());
this.enteringNavbar.add(this.enteringTitle);
this.add(this.enteringNavbar);
self.enteringTitle = new Animation(enteringItem.titleElement());
enteringNavbar.add(self.enteringTitle);
self.add(enteringNavbar);
self.enteringNavbarItems = new Animation(enteringItem.navbarItemElements())
self.enteringNavbarItems
.before.addClass(SHOW_NAVBAR_ITEM)
.fadeIn();
enteringNavbar.add(self.enteringNavbarItems);
}
if (leavingItem) {
// create animation for the entering item's "ion-view" element
this.leavingView = new Animation(leavingItem.viewElement());
this.leavingView.afterFinish.removeClass(SHOW_VIEW_CSS);
self.leavingView = new Animation(leavingItem.viewElement());
self.leavingView.after.removeClass(SHOW_VIEW_CSS);
// create animation for the entering item's "ion-navbar" element
this.leavingNavbar = new Animation(leavingItem.navbarElement());
this.leavingNavbar.afterFinish.removeClass(SHOW_NAVBAR_CSS);
let leavingNavbar = self.leavingNavbar = new Animation(leavingItem.navbarElement());
leavingNavbar.after.removeClass(SHOW_NAVBAR_CSS);
let leavingBackButton = self.leavingBackButton = new Animation(leavingItem.backButtonElement());
leavingBackButton
.after.removeClass(SHOW_BACK_BUTTON)
.fadeOut();
leavingNavbar.add(leavingBackButton);
// create animation for the leaving item's "ion-title" element
this.leavingTitle = new Animation(leavingItem.titleElement());
this.leavingNavbar.add(this.leavingTitle);
self.leavingTitle = new Animation(leavingItem.titleElement());
leavingNavbar.add(self.leavingTitle);
this.add(this.leavingView, this.leavingNavbar);
self.leavingNavbarItems = new Animation(leavingItem.navbarItemElements())
self.leavingNavbarItems
.after.removeClass(SHOW_NAVBAR_ITEM)
.fadeOut();
leavingNavbar.add(self.leavingNavbarItems);
self.add(self.leavingView, leavingNavbar);
}
}
stage(callback) {
raf(callback);
}
/*
STATIC CLASSES
*/
static create(nav, opts = {}) {
let name = opts.animation || 'ios';
const name = opts.animation || 'ios';
let TransitionClass = registry[name];
let TransitionClass = TransitionRegistry[name];
if (!TransitionClass) {
// transition wasn't found, default to a 'none' transition
// which doesn't animate anything, just shows and hides
@@ -72,7 +98,7 @@ export class Transition extends Animation {
}
static register(name, TransitionClass) {
registry[name] = TransitionClass;
TransitionRegistry[name] = TransitionClass;
}
}

View File

@@ -13,6 +13,31 @@
<script src="https://jspm.io/system@0.16.js"></script>
-->
<style>
/* hack to create tall scrollable areas for testing using <f></f> */
f {
display: block;
margin: 15px auto;
max-width: 150px;
height: 150px;
background: blue;
}
f:last-of-type {
background: red;
}
ion-tab:nth-of-type(2) f,
.green f {
background: green;
max-width: 250px;
height: 100px;
}
ion-tab:nth-of-type(3) f,
.yellow f {
background: yellow;
width: 100px;
height: 50px;
}
</style>
</head>
<body>

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -186,11 +186,14 @@
if ((finished || this._idle) && !this._finishedFlag) {
var event = new AnimationEvent(this, this._currentTime, baseTime);
var handlers = this._finishHandlers.concat(this.onfinish ? [this.onfinish] : []);
setTimeout(function() {
handlers.forEach(function(handler) {
handler.call(event.target, event);
});
}, 0);
// setTimeout(function() {
// handlers.forEach(function(handler) {
// handler.call(event.target, event);
// });
// }, 0);
handlers.forEach(function(handler) {
handler.call(event.target, event);
});
}
this._finishedFlag = finished;
},

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long