mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-21 21:15:24 +08:00
fix(nav): flickering
This commit is contained in:
1
packages/core/src/components.d.ts
vendored
1
packages/core/src/components.d.ts
vendored
@ -3059,6 +3059,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
namespace JSXElements {
|
namespace JSXElements {
|
||||||
export interface IonTabAttributes extends HTMLAttributes {
|
export interface IonTabAttributes extends HTMLAttributes {
|
||||||
|
active?: boolean;
|
||||||
badge?: string;
|
badge?: string;
|
||||||
badgeStyle?: string;
|
badgeStyle?: string;
|
||||||
btnId?: string;
|
btnId?: string;
|
||||||
|
@ -22,6 +22,7 @@ export interface Animation {
|
|||||||
beforeClearStyles(propertyNames: string[]): Animation;
|
beforeClearStyles(propertyNames: string[]): Animation;
|
||||||
beforeAddRead(domReadFn: Function): Animation;
|
beforeAddRead(domReadFn: Function): Animation;
|
||||||
beforeAddWrite(domWriteFn: Function): Animation;
|
beforeAddWrite(domWriteFn: Function): Animation;
|
||||||
|
duringAddClass(className: string): Animation;
|
||||||
afterAddClass(className: string): Animation;
|
afterAddClass(className: string): Animation;
|
||||||
afterRemoveClass(className: string): Animation;
|
afterRemoveClass(className: string): Animation;
|
||||||
afterStyles(styles: { [property: string]: any; }): Animation;
|
afterStyles(styles: { [property: string]: any; }): Animation;
|
||||||
|
@ -231,6 +231,15 @@ export class Animator {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a CSS class during the duration of the animation.
|
||||||
|
*/
|
||||||
|
duringAddClass(className: string): Animator {
|
||||||
|
this.beforeAddClass(className);
|
||||||
|
this.afterRemoveClass(className);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set CSS inline styles to this animation's elements
|
* Set CSS inline styles to this animation's elements
|
||||||
* before the animation begins.
|
* before the animation begins.
|
||||||
|
@ -8,6 +8,3 @@ $content-ios-font-family: $font-family-ios-base !default;
|
|||||||
|
|
||||||
/// @prop - Background color of the outer content
|
/// @prop - Background color of the outer content
|
||||||
$content-ios-outer-background: $background-ios-color-step-50 !default;
|
$content-ios-outer-background: $background-ios-color-step-50 !default;
|
||||||
|
|
||||||
/// @prop - Background color of the content when making transition
|
|
||||||
$content-ios-transition-background: #000 !default;
|
|
||||||
|
@ -68,7 +68,7 @@ export function canNavGoBack(nav: Nav, view?: ViewController) {
|
|||||||
if (!nav) {
|
if (!nav) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return nav.getPrevious(view);
|
return !!nav.getPrevious(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transitionFactory(animation: Animation): Transition {
|
export function transitionFactory(animation: Animation): Transition {
|
||||||
@ -127,20 +127,28 @@ export function destroyTransition(transitionId: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getHydratedTransition(name: string, config: Config, transitionId: number, emptyTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions, defaultTransitionFactory: TransitionBuilder): Promise<Transition> {
|
export function getHydratedTransition(name: string, config: Config, transitionId: number, emptyTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions, defaultTransitionFactory: TransitionBuilder): Promise<Transition> {
|
||||||
|
// Let makes sure everything is hydrated and ready to animate
|
||||||
|
const componentReadyPromise: Promise<any>[] = [];
|
||||||
|
if (enteringView && (enteringView.element as any).componentOnReady) {
|
||||||
|
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
||||||
|
}
|
||||||
|
if (leavingView && (leavingView.element as any).componentOnReady) {
|
||||||
|
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
||||||
|
}
|
||||||
const transitionFactory = config.get(name) as TransitionBuilder || defaultTransitionFactory;
|
const transitionFactory = config.get(name) as TransitionBuilder || defaultTransitionFactory;
|
||||||
|
return Promise.all(componentReadyPromise)
|
||||||
return transitionFactory(emptyTransition, enteringView, leavingView, opts).then((hydratedTransition) => {
|
.then(() => transitionFactory(emptyTransition, enteringView, leavingView, opts))
|
||||||
hydratedTransition.transitionId = transitionId;
|
.then((hydratedTransition) => {
|
||||||
if (!activeTransitions.has(transitionId)) {
|
hydratedTransition.transitionId = transitionId;
|
||||||
// sweet, this is the root transition
|
if (!activeTransitions.has(transitionId)) {
|
||||||
activeTransitions.set(transitionId, hydratedTransition);
|
// sweet, this is the root transition
|
||||||
} else {
|
activeTransitions.set(transitionId, hydratedTransition);
|
||||||
// we've got a parent transition going
|
} else {
|
||||||
// just append this transition to the existing one
|
// we've got a parent transition going
|
||||||
activeTransitions.get(transitionId).add(hydratedTransition);
|
// just append this transition to the existing one
|
||||||
}
|
activeTransitions.get(transitionId).add(hydratedTransition);
|
||||||
return hydratedTransition;
|
}
|
||||||
|
return hydratedTransition;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Util
|
|
||||||
// --------------------------------------------------
|
|
||||||
@import "../../themes/util";
|
@import "../../themes/util";
|
||||||
|
|
||||||
|
$navigation-ios-transition-background: #000 !default;
|
||||||
|
|
||||||
ion-nav {
|
ion-nav {
|
||||||
@include position(0);
|
@include position(0);
|
||||||
@ -13,10 +12,7 @@ ion-nav {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
background-color: #000;
|
|
||||||
|
|
||||||
contain: layout size style;
|
contain: layout size style;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ion-page {
|
.ion-page {
|
||||||
@ -31,3 +27,24 @@ ion-nav {
|
|||||||
|
|
||||||
contain: layout size style;
|
contain: layout size style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-decor {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-decor > .nav-decor {
|
||||||
|
@include position(0, null, null, 0);
|
||||||
|
|
||||||
|
// when ios pages transition, the leaving page grays out
|
||||||
|
// this is the black square behind all pages so they gray out
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background: $navigation-ios-transition-background;
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
@ -335,6 +335,9 @@ export class Nav implements PublicNav, NavOutlet {
|
|||||||
attachTo='body'
|
attachTo='body'
|
||||||
></ion-gesture>);
|
></ion-gesture>);
|
||||||
}
|
}
|
||||||
|
if (this.mode === 'ios') {
|
||||||
|
dom.push(<div class='nav-decor'/>);
|
||||||
|
}
|
||||||
dom.push(<slot></slot>);
|
dom.push(<slot></slot>);
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
@ -820,7 +823,8 @@ export function loadViewAndTransition(nav: Nav, enteringView: ViewController, le
|
|||||||
|
|
||||||
|
|
||||||
const emptyTransition = transitionFactory(ti.animation);
|
const emptyTransition = transitionFactory(ti.animation);
|
||||||
return getHydratedTransition(animationOpts.animation, nav.config, nav.transitionId, emptyTransition, enteringView, leavingView, animationOpts, getDefaultTransition(nav.config)).then((transition) => {
|
return getHydratedTransition(animationOpts.animation, nav.config, nav.transitionId, emptyTransition, enteringView, leavingView, animationOpts, getDefaultTransition(nav.config))
|
||||||
|
.then((transition) => {
|
||||||
|
|
||||||
if (nav.sbTrns) {
|
if (nav.sbTrns) {
|
||||||
nav.sbTrns.destroy();
|
nav.sbTrns.destroy();
|
||||||
@ -967,6 +971,7 @@ export function fireViewWillLifecycles(enteringView: ViewController, leavingView
|
|||||||
export function attachViewToDom(nav: Nav, enteringView: ViewController, ti: TransitionInstruction) {
|
export function attachViewToDom(nav: Nav, enteringView: ViewController, ti: TransitionInstruction) {
|
||||||
if (enteringView && enteringView.state === STATE_NEW) {
|
if (enteringView && enteringView.state === STATE_NEW) {
|
||||||
return ti.delegate.attachViewToDom(nav.element, enteringView.component, enteringView.data, [], ti.escapeHatch).then((mountingData) => {
|
return ti.delegate.attachViewToDom(nav.element, enteringView.component, enteringView.data, [], ti.escapeHatch).then((mountingData) => {
|
||||||
|
mountingData.element.classList.add('nav-page');
|
||||||
ti.mountingData = mountingData;
|
ti.mountingData = mountingData;
|
||||||
Object.assign(enteringView, mountingData);
|
Object.assign(enteringView, mountingData);
|
||||||
enteringView.state = STATE_ATTACHED;
|
enteringView.state = STATE_ATTACHED;
|
||||||
|
@ -12,197 +12,193 @@ const OFF_OPACITY = 0.8;
|
|||||||
const SHOW_BACK_BTN_CSS = 'show-back-button';
|
const SHOW_BACK_BTN_CSS = 'show-back-button';
|
||||||
|
|
||||||
export function buildIOSTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
export function buildIOSTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
||||||
const componentReadyPromise: Promise<any>[] = [];
|
// Cool we're all hydrated, and can do deep selector
|
||||||
// Let makes sure everything is hydrated and ready to animate
|
rootTransition.enteringView = enteringView;
|
||||||
if (enteringView && (enteringView.element as any).componentOnReady) {
|
rootTransition.leavingView = leavingView;
|
||||||
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
|
||||||
}
|
const isRTL = document.dir === 'rtl';
|
||||||
if (leavingView && (leavingView.element as any).componentOnReady) {
|
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
|
||||||
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
const OFF_LEFT = isRTL ? '33%' : '-33%';
|
||||||
|
|
||||||
|
rootTransition.duration(isDef(opts.duration) ? opts.duration : DURATION);
|
||||||
|
rootTransition.easing(isDef(opts.easing) ? opts.easing : EASING);
|
||||||
|
|
||||||
|
|
||||||
|
rootTransition.addElement(enteringView.element);
|
||||||
|
rootTransition.beforeRemoveClass('hide-page');
|
||||||
|
|
||||||
|
if (leavingView) {
|
||||||
|
const navEl = leavingView.element.closest('ion-nav');
|
||||||
|
if (navEl) {
|
||||||
|
const navDecor = rootTransition.create();
|
||||||
|
navDecor.addElement(navEl).duringAddClass('show-decor');
|
||||||
|
rootTransition.add(navDecor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(componentReadyPromise).then(() => {
|
const backDirection = (opts.direction === 'back');
|
||||||
// Cool we're all hydrated, and can do deep selector
|
// setting up enter view
|
||||||
rootTransition.enteringView = enteringView;
|
if (enteringView) {
|
||||||
rootTransition.leavingView = leavingView;
|
|
||||||
|
|
||||||
const isRTL = document.dir === 'rtl';
|
const enteringContent = rootTransition.create();
|
||||||
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
|
enteringContent.addElement(enteringView.element.querySelector('ion-content'));
|
||||||
const OFF_LEFT = isRTL ? '33%' : '-33%';
|
enteringContent.addElement(enteringView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
||||||
|
rootTransition.add(enteringContent);
|
||||||
|
|
||||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : DURATION);
|
if (backDirection) {
|
||||||
rootTransition.easing(isDef(opts.easing) ? opts.easing : EASING);
|
enteringContent
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
|
.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true)
|
||||||
|
.fromTo(OPACITY, OFF_OPACITY, 1, true);
|
||||||
|
} else {
|
||||||
|
// entering content, forward direction
|
||||||
|
enteringContent
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
|
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
rootTransition.addElement(enteringView.element);
|
const enteringToolBarEle = enteringView.element.querySelector('ion-toolbar');
|
||||||
rootTransition.beforeRemoveClass('hide-page');
|
if (enteringToolBarEle) {
|
||||||
|
const enteringToolBar = rootTransition.create();
|
||||||
|
enteringToolBar.addElement(enteringToolBarEle);
|
||||||
|
rootTransition.add(enteringToolBar);
|
||||||
|
|
||||||
const backDirection = (opts.direction === 'back');
|
const enteringTitle = rootTransition.create();
|
||||||
|
enteringTitle.addElement(enteringToolBarEle.querySelector('ion-title'));
|
||||||
|
|
||||||
// setting up enter view
|
const enteringToolBarItems = rootTransition.create();
|
||||||
if (enteringView) {
|
enteringToolBarItems.addElement(enteringToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||||
|
|
||||||
const enteringContent = rootTransition.create();
|
const enteringToolBarBg = rootTransition.create();
|
||||||
enteringContent.addElement(enteringView.element.querySelector('ion-content'));
|
enteringToolBarBg.addElement(enteringToolBarEle.querySelector('.toolbar-background'));
|
||||||
enteringContent.addElement(enteringView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
|
||||||
rootTransition.add(enteringContent);
|
const enteringBackButton = rootTransition.create();
|
||||||
|
enteringBackButton.addElement(enteringToolBarEle.querySelector('.back-button'));
|
||||||
|
|
||||||
|
enteringToolBar
|
||||||
|
.add(enteringTitle)
|
||||||
|
.add(enteringToolBarItems)
|
||||||
|
.add(enteringToolBarBg)
|
||||||
|
.add(enteringBackButton);
|
||||||
|
|
||||||
|
enteringTitle.fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
enteringToolBarItems.fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
enteringContent
|
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
||||||
.beforeClearStyles([OPACITY])
|
|
||||||
.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true)
|
if (canNavGoBack(enteringView.nav, enteringView)) {
|
||||||
.fromTo(OPACITY, OFF_OPACITY, 1, true);
|
// back direction, entering page has a back button
|
||||||
|
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// entering content, forward direction
|
// entering toolbar, forward direction
|
||||||
enteringContent
|
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
|
enteringToolBarBg
|
||||||
.beforeClearStyles([OPACITY])
|
.beforeClearStyles([OPACITY])
|
||||||
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
}
|
if (canNavGoBack(enteringView.nav, enteringView)) {
|
||||||
|
|
||||||
const enteringToolBarEle = enteringView.element.querySelector('ion-toolbar');
|
// forward direction, entering page has a back button
|
||||||
if (enteringToolBarEle) {
|
enteringBackButton
|
||||||
const enteringToolBar = rootTransition.create();
|
.beforeAddClass(SHOW_BACK_BTN_CSS)
|
||||||
enteringToolBar.addElement(enteringToolBarEle);
|
.fromTo(OPACITY, 0.01, 1, true);
|
||||||
rootTransition.add(enteringToolBar);
|
|
||||||
|
|
||||||
const enteringTitle = rootTransition.create();
|
|
||||||
enteringTitle.addElement(enteringToolBarEle.querySelector('ion-title'));
|
|
||||||
|
|
||||||
const enteringToolBarItems = rootTransition.create();
|
const enteringBackBtnText = rootTransition.create();
|
||||||
enteringToolBarItems.addElement(enteringToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
enteringBackBtnText.addElement(enteringToolBarEle.querySelector('.back-button .button-text'));
|
||||||
|
|
||||||
const enteringToolBarBg = rootTransition.create();
|
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
||||||
enteringToolBarBg.addElement(enteringToolBarEle.querySelector('.toolbar-background'));
|
enteringToolBar.add(enteringBackBtnText);
|
||||||
|
|
||||||
const enteringBackButton = rootTransition.create();
|
|
||||||
enteringBackButton.addElement(enteringToolBarEle.querySelector('.back-button'));
|
|
||||||
|
|
||||||
enteringToolBar
|
|
||||||
.add(enteringTitle)
|
|
||||||
.add(enteringToolBarItems)
|
|
||||||
.add(enteringToolBarBg)
|
|
||||||
.add(enteringBackButton);
|
|
||||||
|
|
||||||
enteringTitle.fromTo(OPACITY, 0.01, 1, true);
|
|
||||||
enteringToolBarItems.fromTo(OPACITY, 0.01, 1, true);
|
|
||||||
|
|
||||||
if (backDirection) {
|
|
||||||
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
|
||||||
|
|
||||||
if (canNavGoBack(enteringView.nav, enteringView)) {
|
|
||||||
// back direction, entering page has a back button
|
|
||||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// entering toolbar, forward direction
|
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
||||||
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
|
||||||
|
|
||||||
enteringToolBarBg
|
|
||||||
.beforeClearStyles([OPACITY])
|
|
||||||
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
|
||||||
|
|
||||||
if (canNavGoBack(enteringView.nav, enteringView)) {
|
|
||||||
|
|
||||||
// forward direction, entering page has a back button
|
|
||||||
enteringBackButton
|
|
||||||
.beforeAddClass(SHOW_BACK_BTN_CSS)
|
|
||||||
.fromTo(OPACITY, 0.01, 1, true);
|
|
||||||
|
|
||||||
|
|
||||||
const enteringBackBtnText = rootTransition.create();
|
|
||||||
enteringBackBtnText.addElement(enteringToolBarEle.querySelector('.back-button .button-text'));
|
|
||||||
|
|
||||||
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
|
||||||
enteringToolBar.add(enteringBackBtnText);
|
|
||||||
} else {
|
|
||||||
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setup leaving view
|
// setup leaving view
|
||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
|
|
||||||
const leavingContent = rootTransition.create();
|
const leavingContent = rootTransition.create();
|
||||||
leavingContent.addElement(leavingView.element.querySelector('ion-content'));
|
leavingContent.addElement(leavingView.element.querySelector('ion-content'));
|
||||||
leavingContent.addElement(leavingView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
leavingContent.addElement(leavingView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
||||||
rootTransition.add(leavingContent);
|
rootTransition.add(leavingContent);
|
||||||
|
|
||||||
|
if (backDirection) {
|
||||||
|
// leaving content, back direction
|
||||||
|
leavingContent
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
|
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// leaving content, forward direction
|
||||||
|
leavingContent
|
||||||
|
.fromTo(TRANSLATEX, CENTER, OFF_LEFT, true)
|
||||||
|
.fromTo(OPACITY, 1, OFF_OPACITY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const leavingToolBarEle = leavingView.element.querySelector('ion-toolbar');
|
||||||
|
if (leavingToolBarEle) {
|
||||||
|
const leavingToolBar = rootTransition.create();
|
||||||
|
leavingToolBar.addElement(leavingToolBarEle);
|
||||||
|
|
||||||
|
const leavingTitle = rootTransition.create();
|
||||||
|
leavingTitle.addElement(leavingToolBarEle.querySelector('ion-title'));
|
||||||
|
|
||||||
|
const leavingToolBarItems = rootTransition.create();
|
||||||
|
leavingToolBarItems.addElement(leavingToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||||
|
|
||||||
|
const leavingToolBarBg = rootTransition.create();
|
||||||
|
leavingToolBarBg.addElement(leavingToolBarEle.querySelector('.toolbar-background'));
|
||||||
|
|
||||||
|
const leavingBackButton = rootTransition.create();
|
||||||
|
leavingBackButton.addElement(leavingToolBarEle.querySelector('.back-button'));
|
||||||
|
|
||||||
|
leavingToolBar
|
||||||
|
.add(leavingTitle)
|
||||||
|
.add(leavingToolBarItems)
|
||||||
|
.add(leavingBackButton)
|
||||||
|
.add(leavingToolBarBg);
|
||||||
|
|
||||||
|
rootTransition.add(leavingToolBar);
|
||||||
|
|
||||||
|
// fade out leaving toolbar items
|
||||||
|
leavingBackButton.fromTo(OPACITY, 0.99, 0, true);
|
||||||
|
leavingTitle.fromTo(OPACITY, 0.99, 0, true);
|
||||||
|
leavingToolBarItems.fromTo(OPACITY, 0.99, 0, true);
|
||||||
|
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
// leaving content, back direction
|
// leaving toolbar, back direction
|
||||||
leavingContent
|
leavingTitle.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
|
// leaving toolbar, back direction, and there's no entering toolbar
|
||||||
|
// should just slide out, no fading out
|
||||||
|
leavingToolBarBg
|
||||||
.beforeClearStyles([OPACITY])
|
.beforeClearStyles([OPACITY])
|
||||||
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
|
const leavingBackBtnText = rootTransition.create();
|
||||||
|
leavingBackBtnText.addElement(leavingToolBarEle.querySelector('.back-button .button-text'));
|
||||||
|
leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (isRTL ? -300 : 300) + 'px');
|
||||||
|
leavingToolBar.add(leavingBackBtnText);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// leaving content, forward direction
|
// leaving toolbar, forward direction
|
||||||
leavingContent
|
leavingTitle
|
||||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT, true)
|
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
||||||
.fromTo(OPACITY, 1, OFF_OPACITY, true);
|
.afterClearStyles([TRANSFORM]);
|
||||||
}
|
|
||||||
|
|
||||||
const leavingToolBarEle = leavingView.element.querySelector('ion-toolbar');
|
leavingBackButton.afterClearStyles([OPACITY]);
|
||||||
if (leavingToolBarEle) {
|
leavingTitle.afterClearStyles([OPACITY]);
|
||||||
const leavingToolBar = rootTransition.create();
|
leavingToolBarItems.afterClearStyles([OPACITY]);
|
||||||
leavingToolBar.addElement(leavingToolBarEle);
|
|
||||||
|
|
||||||
const leavingTitle = rootTransition.create();
|
|
||||||
leavingTitle.addElement(leavingToolBarEle.querySelector('ion-title'));
|
|
||||||
|
|
||||||
const leavingToolBarItems = rootTransition.create();
|
|
||||||
leavingToolBarItems.addElement(leavingToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
|
||||||
|
|
||||||
const leavingToolBarBg = rootTransition.create();
|
|
||||||
leavingToolBarBg.addElement(leavingToolBarEle.querySelector('.toolbar-background'));
|
|
||||||
|
|
||||||
const leavingBackButton = rootTransition.create();
|
|
||||||
leavingBackButton.addElement(leavingToolBarEle.querySelector('.back-button'));
|
|
||||||
|
|
||||||
leavingToolBar
|
|
||||||
.add(leavingTitle)
|
|
||||||
.add(leavingToolBarItems)
|
|
||||||
.add(leavingBackButton)
|
|
||||||
.add(leavingToolBarBg);
|
|
||||||
|
|
||||||
rootTransition.add(leavingToolBar);
|
|
||||||
|
|
||||||
// fade out leaving toolbar items
|
|
||||||
leavingBackButton.fromTo(OPACITY, 0.99, 0, true);
|
|
||||||
leavingTitle.fromTo(OPACITY, 0.99, 0, true);
|
|
||||||
leavingToolBarItems.fromTo(OPACITY, 0.99, 0, true);
|
|
||||||
|
|
||||||
if (backDirection) {
|
|
||||||
// leaving toolbar, back direction
|
|
||||||
leavingTitle.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
|
||||||
|
|
||||||
// leaving toolbar, back direction, and there's no entering toolbar
|
|
||||||
// should just slide out, no fading out
|
|
||||||
leavingToolBarBg
|
|
||||||
.beforeClearStyles([OPACITY])
|
|
||||||
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
|
||||||
|
|
||||||
const leavingBackBtnText = rootTransition.create();
|
|
||||||
leavingBackBtnText.addElement(leavingToolBarEle.querySelector('.back-button .button-text'));
|
|
||||||
leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (isRTL ? -300 : 300) + 'px');
|
|
||||||
leavingToolBar.add(leavingBackBtnText);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// leaving toolbar, forward direction
|
|
||||||
leavingTitle
|
|
||||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
|
||||||
.afterClearStyles([TRANSFORM]);
|
|
||||||
|
|
||||||
leavingBackButton.afterClearStyles([OPACITY]);
|
|
||||||
leavingTitle.afterClearStyles([OPACITY]);
|
|
||||||
leavingToolBarItems.afterClearStyles([OPACITY]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Return the rootTransition promise
|
// Return the rootTransition promise
|
||||||
return rootTransition;
|
return Promise.resolve(rootTransition);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,6 @@ const SHOW_BACK_BTN_CSS = 'show-back-button';
|
|||||||
|
|
||||||
export function buildMdTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
export function buildMdTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
||||||
|
|
||||||
const componentReadyPromise: Promise<any>[] = [];
|
|
||||||
if (enteringView && (enteringView.element as any).componentOnReady) {
|
|
||||||
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
|
||||||
}
|
|
||||||
if (leavingView && (leavingView.element as any).componentOnReady) {
|
|
||||||
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(componentReadyPromise).then(() => {
|
|
||||||
rootTransition.enteringView = enteringView;
|
rootTransition.enteringView = enteringView;
|
||||||
rootTransition.leavingView = leavingView;
|
rootTransition.leavingView = leavingView;
|
||||||
|
|
||||||
@ -68,10 +59,7 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
|||||||
rootTransition.add(leavingPage.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM).fromTo('opacity', 1, 0));
|
rootTransition.add(leavingPage.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM).fromTo('opacity', 1, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootTransition;
|
return Promise.resolve(rootTransition);
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIonPageElement(element: HTMLElement) {
|
function getIonPageElement(element: HTMLElement) {
|
||||||
|
@ -32,7 +32,7 @@ export class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Listen('window:popstate')
|
@Listen('window:popstate')
|
||||||
protected onURLHashChanged() {
|
protected onPopState() {
|
||||||
if (window.history.state === null) {
|
if (window.history.state === null) {
|
||||||
this.state++;
|
this.state++;
|
||||||
window.history.replaceState(this.state, document.title, document.location.href);
|
window.history.replaceState(this.state, document.title, document.location.href);
|
||||||
|
@ -50,6 +50,11 @@ export class Tabs {
|
|||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
|
#### active
|
||||||
|
|
||||||
|
boolean
|
||||||
|
|
||||||
|
|
||||||
#### badge
|
#### badge
|
||||||
|
|
||||||
string
|
string
|
||||||
@ -131,6 +136,11 @@ The title of the tab button.
|
|||||||
|
|
||||||
## Attributes
|
## Attributes
|
||||||
|
|
||||||
|
#### active
|
||||||
|
|
||||||
|
boolean
|
||||||
|
|
||||||
|
|
||||||
#### badge
|
#### badge
|
||||||
|
|
||||||
string
|
string
|
||||||
@ -222,7 +232,7 @@ Emitted when the current tab is selected.
|
|||||||
#### getRouteId()
|
#### getRouteId()
|
||||||
|
|
||||||
|
|
||||||
#### setActive()
|
#### prepareActive()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ export class Tab {
|
|||||||
@Element() el: HTMLElement;
|
@Element() el: HTMLElement;
|
||||||
|
|
||||||
@State() init = false;
|
@State() init = false;
|
||||||
@State() active = false;
|
@Prop() active = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the root page for this tab.
|
* Set the root page for this tab.
|
||||||
@ -79,11 +79,7 @@ export class Tab {
|
|||||||
@Event() ionSelect: EventEmitter<void>;
|
@Event() ionSelect: EventEmitter<void>;
|
||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
setActive(active: boolean): Promise<any> {
|
prepareActive(): Promise<any> {
|
||||||
this.active = active;
|
|
||||||
if (!active) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
if (this.loaded) {
|
if (this.loaded) {
|
||||||
return this.configChildgNav();
|
return this.configChildgNav();
|
||||||
}
|
}
|
||||||
@ -140,13 +136,13 @@ export class Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostData() {
|
hostData() {
|
||||||
const visible = this.active && this.selected;
|
const hidden = !this.active || !this.selected;
|
||||||
return {
|
return {
|
||||||
'aria-hidden': !visible,
|
'aria-hidden': hidden,
|
||||||
'aria-labelledby': this.btnId,
|
'aria-labelledby': this.btnId,
|
||||||
'role': 'tabpanel',
|
'role': 'tabpanel',
|
||||||
class: {
|
class: {
|
||||||
'show-tab': visible
|
'show-tab': this.active
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
||||||
import { Config, NavEventDetail, NavOutlet } from '../../index';
|
import { Config, NavEventDetail, NavOutlet } from '../../index';
|
||||||
|
|
||||||
import { ensureExternalRounterController } from '../../utils/helpers';
|
import { asyncRaf, ensureExternalRounterController } from '../../utils/helpers';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -121,15 +121,17 @@ export class Tabs implements NavOutlet {
|
|||||||
const leavingTab = this.selectedTab;
|
const leavingTab = this.selectedTab;
|
||||||
this.selectedTab = selectedTab;
|
this.selectedTab = selectedTab;
|
||||||
|
|
||||||
let promise = selectedTab.setActive(true);
|
|
||||||
if (leavingTab && leavingTab !== selectedTab) {
|
|
||||||
promise = promise.then(() => leavingTab.setActive(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
return promise.then(() => {
|
return selectedTab.prepareActive()
|
||||||
this.ionChange.emit(selectedTab);
|
.then(() => selectedTab.active = true)
|
||||||
this.ionNavChanged.emit({isPop: false});
|
.then(() => asyncRaf())
|
||||||
});
|
.then(() => {
|
||||||
|
if (leavingTab) {
|
||||||
|
leavingTab.active = false;
|
||||||
|
}
|
||||||
|
this.ionChange.emit(selectedTab);
|
||||||
|
this.ionNavChanged.emit({isPop: false});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,7 +211,7 @@ export class Tabs implements NavOutlet {
|
|||||||
tab.selected = false;
|
tab.selected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const promise = selectedTab ? selectedTab.setActive(true) : Promise.resolve();
|
const promise = selectedTab ? selectedTab.prepareActive() : Promise.resolve();
|
||||||
return promise.then(() => {
|
return promise.then(() => {
|
||||||
this.selectedTab = selectedTab;
|
this.selectedTab = selectedTab;
|
||||||
if (selectedTab) {
|
if (selectedTab) {
|
||||||
|
@ -295,6 +295,10 @@ export function debounce(func: Function, wait = 0) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function asyncRaf(): Promise<number> {
|
||||||
|
return new Promise(resolve => requestAnimationFrame(resolve));
|
||||||
|
}
|
||||||
|
|
||||||
export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null {
|
export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null {
|
||||||
for (let i = 0; i < element.children.length; i++) {
|
for (let i = 0; i < element.children.length; i++) {
|
||||||
if (element.children[i].tagName.toLowerCase() === 'ion-nav') {
|
if (element.children[i].tagName.toLowerCase() === 'ion-nav') {
|
||||||
|
Reference in New Issue
Block a user