fix(header): support collapsible header with multiple toolbars (#19909)

* begin fix collapsible title with multi toolbars

* fix back button

* adjust large title positioning

* a few adjustments

* remove whitespace

* add types

* fix type
This commit is contained in:
Liam DeBeasi
2019-11-18 08:57:56 -05:00
committed by GitHub
parent 3298fddc09
commit fc4bb2db5c
4 changed files with 60 additions and 27 deletions

View File

@@ -101,7 +101,10 @@ export class Header implements ComponentInterface {
if (!mainHeaderIndex || !scrollHeaderIndex) { return; }
setHeaderActive(mainHeaderIndex, false);
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0], 0);
mainHeaderIndex.toolbars.forEach(toolbar => {
setToolbarBackgroundOpacity(toolbar, 0);
});
readTask(() => {
const mainHeaderHeight = mainHeaderIndex.el.clientHeight;
@@ -114,7 +117,7 @@ export class Header implements ComponentInterface {
*/
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex); };
this.intersectionObserver = new IntersectionObserver(toolbarIntersection, { threshold: [0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1], rootMargin: `-${mainHeaderHeight}px 0px 0px 0px` });
this.intersectionObserver.observe(scrollHeaderIndex.toolbars[0].el);
this.intersectionObserver.observe(scrollHeaderIndex.toolbars[scrollHeaderIndex.toolbars.length - 1].el);
/**
* Handle scaling of large iOS titles and

View File

@@ -72,7 +72,10 @@ const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex)
if (!ev[0].isIntersecting) { return; }
const scale = ((1 - ev[0].intersectionRatio) * 100) / 75;
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0], (scale === 1) ? undefined : scale);
mainHeaderIndex.toolbars.forEach(toolbar => {
setToolbarBackgroundOpacity(toolbar, (scale === 1) ? undefined : scale);
});
};
/**

View File

@@ -82,6 +82,28 @@
this.innerHTML = `
<ion-header class="main-header" translucent>
<ion-toolbar class="main-toolbar">
<ion-segment>
<ion-segment-button value="all">
All
</ion-segment-button>
<ion-segment-button value="favorites">
Favorites
</ion-segment-button>
</ion-segment>
</ion-toolbar>
<ion-toolbar class="main-toolbar">
<ion-segment>
<ion-segment-button value="all">
All
</ion-segment-button>
<ion-segment-button value="favorites">
Favorites
</ion-segment-button>
</ion-segment>
</ion-toolbar>
<ion-toolbar class="main-toolbar">
<ion-buttons collapse>
<ion-back-button default-href="/"></ion-back-button>
@@ -139,7 +161,7 @@
<ion-buttons slot="end" collapse>
<ion-button>Hello Page 3</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-toolbar>
</ion-header>
<ion-content id="content" class="three-content" fullscreen color="dark">

View File

@@ -48,11 +48,17 @@ const createLargeTitleTransition = (rootAnimation: Animation, rtl: boolean, back
const shouldAnimationBackward = enteringLargeTitle !== null && leavingBackButton !== null && backDirection;
if (shouldAnimationForward) {
animateLargeTitle(rootAnimation, rtl, backDirection, leavingLargeTitle);
animateBackButton(rootAnimation, rtl, backDirection, enteringBackButton);
const leavingLargeTitleBox = leavingLargeTitle.getBoundingClientRect();
const enteringBackButtonBox = enteringBackButton.getBoundingClientRect();
animateLargeTitle(rootAnimation, rtl, backDirection, leavingLargeTitle, leavingLargeTitleBox, enteringBackButtonBox);
animateBackButton(rootAnimation, rtl, backDirection, enteringBackButton, leavingLargeTitleBox, enteringBackButtonBox);
} else if (shouldAnimationBackward) {
animateLargeTitle(rootAnimation, rtl, backDirection, enteringLargeTitle);
animateBackButton(rootAnimation, rtl, backDirection, leavingBackButton);
const enteringLargeTitleBox = enteringLargeTitle.getBoundingClientRect();
const leavingBackButtonBox = leavingBackButton.getBoundingClientRect();
animateLargeTitle(rootAnimation, rtl, backDirection, enteringLargeTitle, enteringLargeTitleBox, leavingBackButtonBox);
animateBackButton(rootAnimation, rtl, backDirection, leavingBackButton, enteringLargeTitleBox, leavingBackButtonBox);
}
return {
@@ -61,9 +67,8 @@ const createLargeTitleTransition = (rootAnimation: Animation, rtl: boolean, back
};
};
const animateBackButton = (rootAnimation: Animation, rtl: boolean, backDirection: boolean, backButtonEl: any) => {
const backButtonBounds = backButtonEl.getBoundingClientRect();
const BACK_BUTTON_START_OFFSET = (rtl) ? `calc(100% - ${backButtonBounds.right + 4}px)` : `${backButtonBounds.left - 4}px`;
const animateBackButton = (rootAnimation: Animation, rtl: boolean, backDirection: boolean, backButtonEl: any, largeTitleBox: DOMRect, backButtonBox: DOMRect) => {
const BACK_BUTTON_START_OFFSET = (rtl) ? `calc(100% - ${backButtonBox.right + 4}px)` : `${backButtonBox.left - 4}px`;
const START_TEXT_TRANSLATE = (rtl) ? '7px' : '-7px';
const END_TEXT_TRANSLATE = (rtl) ? '-4px' : '4px';
@@ -73,24 +78,24 @@ const animateBackButton = (rootAnimation: Animation, rtl: boolean, backDirection
const ICON_ORIGIN_X = (rtl) ? 'left' : 'right';
const FORWARD_TEXT_KEYFRAMES = [
{ offset: 0, opacity: 0, transform: `translate(${START_TEXT_TRANSLATE}, ${addSafeArea(8)}) scale(2.1)` },
{ offset: 1, opacity: 1, transform: `translate(${END_TEXT_TRANSLATE}, ${addSafeArea(-40)}) scale(1)` }
{ offset: 0, opacity: 0, transform: `translate(${START_TEXT_TRANSLATE}, ${addSafeArea(largeTitleBox.top - 40)}) scale(2.1)` },
{ offset: 1, opacity: 1, transform: `translate(${END_TEXT_TRANSLATE}, ${addSafeArea(backButtonBox.top - 46)}) scale(1)` }
];
const BACKWARD_TEXT_KEYFRAMES = [
{ offset: 0, opacity: 1, transform: `translate(${END_TEXT_TRANSLATE}, ${addSafeArea(-40)}) scale(1)` },
{ offset: 0, opacity: 1, transform: `translate(${END_TEXT_TRANSLATE}, ${addSafeArea(backButtonBox.top - 46)}) scale(1)` },
{ offset: 0.6, opacity: 0 },
{ offset: 1, opacity: 0, transform: `translate(${START_TEXT_TRANSLATE}, ${addSafeArea(8)}) scale(2.1)` }
{ offset: 1, opacity: 0, transform: `translate(${START_TEXT_TRANSLATE}, ${addSafeArea(largeTitleBox.top - 40)}) scale(2.1)` }
];
const TEXT_KEYFRAMES = (backDirection) ? BACKWARD_TEXT_KEYFRAMES : FORWARD_TEXT_KEYFRAMES;
const FORWARD_ICON_KEYFRAMES = [
{ offset: 0, opacity: 0, transform: `translate3d(${ICON_TRANSLATE}, ${addSafeArea(-35)}, 0) scale(0.6)` },
{ offset: 1, opacity: 1, transform: `translate3d(${ICON_TRANSLATE}, ${addSafeArea(-40)}, 0) scale(1)` }
{ offset: 0, opacity: 0, transform: `translate3d(${ICON_TRANSLATE}, ${addSafeArea(backButtonBox.top - 41)}, 0) scale(0.6)` },
{ offset: 1, opacity: 1, transform: `translate3d(${ICON_TRANSLATE}, ${addSafeArea(backButtonBox.top - 46)}, 0) scale(1)` }
];
const BACKWARD_ICON_KEYFRAMES = [
{ offset: 0, opacity: 1, transform: `translate(${ICON_TRANSLATE}, ${addSafeArea(-40)}) scale(1)` },
{ offset: 0.2, opacity: 0, transform: `translate(${ICON_TRANSLATE}, ${addSafeArea(-35)}) scale(0.6)` },
{ offset: 1, opacity: 0, transform: `translate(${ICON_TRANSLATE}, ${addSafeArea(-35)}) scale(0.6)` }
{ offset: 0, opacity: 1, transform: `translate(${ICON_TRANSLATE}, ${addSafeArea(backButtonBox.top - 46)}) scale(1)` },
{ offset: 0.2, opacity: 0, transform: `translate(${ICON_TRANSLATE}, ${addSafeArea(backButtonBox.top - 41)}) scale(0.6)` },
{ offset: 1, opacity: 0, transform: `translate(${ICON_TRANSLATE}, ${addSafeArea(backButtonBox.top - 41)}) scale(0.6)` }
];
const ICON_KEYFRAMES = (backDirection) ? BACKWARD_ICON_KEYFRAMES : FORWARD_ICON_KEYFRAMES;
@@ -138,22 +143,22 @@ const animateBackButton = (rootAnimation: Animation, rtl: boolean, backDirection
rootAnimation.addAnimation([enteringBackButtonTextAnimation, enteringBackButtonIconAnimation]);
};
const animateLargeTitle = (rootAnimation: Animation, rtl: boolean, backDirection: boolean, largeTitleEl: any) => {
const largeTitleBounds = largeTitleEl.getBoundingClientRect();
const TITLE_START_OFFSET = (rtl) ? `calc(100% - ${largeTitleBounds.right}px)` : `${largeTitleBounds.left}px`;
const animateLargeTitle = (rootAnimation: Animation, rtl: boolean, backDirection: boolean, largeTitleEl: any, largeTitleBox: DOMRect, backButtonBox: DOMRect) => {
const TITLE_START_OFFSET = (rtl) ? `calc(100% - ${largeTitleEl.right}px)` : `${largeTitleEl.left}px`;
const START_TRANSLATE = (rtl) ? '-18px' : '18px';
const ORIGIN_X = (rtl) ? 'right' : 'left';
const BACKWARDS_KEYFRAMES = [
{ offset: 0, opacity: 0, transform: `translate(${START_TRANSLATE}, ${addSafeArea(0)}) scale(0.49)` },
{ offset: 0, opacity: 0, transform: `translate(${START_TRANSLATE}, ${addSafeArea(backButtonBox.top - 4)}) scale(0.49)` },
{ offset: 0.1, opacity: 0 },
{ offset: 1, opacity: 1, transform: `translate(0, ${addSafeArea(49)}) scale(1)` }
{ offset: 1, opacity: 1, transform: `translate(0, ${addSafeArea(largeTitleBox.top - 2)}) scale(1)` }
];
const FORWARDS_KEYFRAMES = [
{ offset: 0, opacity: 0.99, transform: `translate(0, ${addSafeArea(49)}) scale(1)` },
{ offset: 0, opacity: 0.99, transform: `translate(0, ${addSafeArea(largeTitleBox.top - 2)}) scale(1)` },
{ offset: 0.6, opacity: 0 },
{ offset: 1, opacity: 0, transform: `translate(${START_TRANSLATE}, ${addSafeArea(0)}) scale(0.5)` }
{ offset: 1, opacity: 0, transform: `translate(${START_TRANSLATE}, ${addSafeArea(backButtonBox.top - 4)}) scale(0.5)` }
];
const KEYFRAMES = (backDirection) ? BACKWARDS_KEYFRAMES : FORWARDS_KEYFRAMES;
const clonedTitleEl = getClonedElement('ion-title');