fix(vue): hide layout shift on ion-page components (#22254)

resolves #22052
This commit is contained in:
Liam DeBeasi
2020-10-06 15:00:48 -04:00
committed by GitHub
parent 6849dd3483
commit 2bad1bb82e
11 changed files with 240 additions and 29 deletions

View File

@@ -7,7 +7,7 @@ export const IonPage = defineComponent({
return h(
'div',
{
['class']: 'ion-page',
['class']: 'ion-page ion-page-invisible',
...attrs,
ref: 'ionPage'
},

View File

@@ -125,24 +125,32 @@ export const IonRouterOutlet = defineComponent({
progressAnimation: boolean,
animationBuilder?: AnimationBuilder
) => {
if (skipTransition) {
skipTransition = false;
return Promise.resolve(false);
}
return new Promise(resolve => {
if (skipTransition) {
skipTransition = false;
return resolve(false);
}
if (enteringEl === leavingEl) {
return Promise.resolve(false);
}
if (enteringEl === leavingEl) {
return resolve(false);
}
enteringEl.classList.add('ion-page-invisible');
requestAnimationFrame(() => {
requestAnimationFrame(async () => {
enteringEl.classList.add('ion-page-invisible');
return ionRouterOutlet.value.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined || direction === 'root' || direction === 'none' ? 0 : undefined,
direction,
showGoBack,
progressAnimation,
animationBuilder
const result = await ionRouterOutlet.value.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined || direction === 'root' || direction === 'none' ? 0 : undefined,
direction,
showGoBack,
progressAnimation,
animationBuilder
});
return resolve(result);
});
});
});
}
@@ -152,6 +160,9 @@ export const IonRouterOutlet = defineComponent({
const enteringViewItem = viewStacks.findViewItemByRouteInfo(routeInfo, id);
const leavingViewItem = viewStacks.findLeavingViewItemByRouteInfo(routeInfo, id);
const enteringEl = enteringViewItem.ionPageElement;
if (enteringViewItem === leavingViewItem) return;
if (enteringViewItem === leavingViewItem) return;
@@ -159,7 +170,6 @@ export const IonRouterOutlet = defineComponent({
if (leavingViewItem) {
let animationBuilder = routerAnimation;
const enteringEl = enteringViewItem.ionPageElement;
const leavingEl = leavingViewItem.ionPageElement;
fireLifecycle(leavingViewItem.vueComponent, LIFECYCLE_WILL_LEAVE);
@@ -207,6 +217,14 @@ export const IonRouterOutlet = defineComponent({
}
fireLifecycle(leavingViewItem.vueComponent, LIFECYCLE_DID_LEAVE);
} else {
/**
* If there is no leaving element, just show
* the entering element. Wrap it in an raf
* in case ion-content's fullscreen callback
* is running. Otherwise we'd have a flicker.
*/
requestAnimationFrame(() => enteringEl.classList.remove('ion-page-invisible'));
}
fireLifecycle(enteringViewItem.vueComponent, LIFECYCLE_DID_ENTER);

View File

@@ -1306,27 +1306,27 @@
}
},
"@ionic/core": {
"version": "0.6.0-dev.202010051922.f5d657c",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-0.6.0-dev.202010051922.f5d657c.tgz",
"integrity": "sha512-sIwN9lV4Fep+vTYfeso+D4BL6APKiYuQ6J2H5+0pV0Azep59TwOompR/SB9U3AYsHKMD+MQj9i2cxYFXIPtd3Q==",
"version": "0.6.0-dev.202010051957.440e9e7",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-0.6.0-dev.202010051957.440e9e7.tgz",
"integrity": "sha512-WeMzpVFwNSK0M6tMAfYKr63OSB80x3JJbusF0bieT3Owh4c+zt25S0pfm6Tu8a11iYTn4vteBEYuKeu67Qv+rg==",
"requires": {
"ionicons": "^5.1.2",
"tslib": "^1.10.0"
}
},
"@ionic/vue": {
"version": "0.6.0-dev.202010051922.f5d657c",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-0.6.0-dev.202010051922.f5d657c.tgz",
"integrity": "sha512-HuMk3JUqt8DkVbgJYWgmmB+G5kALUoABl8n6fSFwFk8+vzqF0mJMoHcyspbL2hCGviTjHKQ9R2/WAbdq0Q/fNQ==",
"version": "0.6.0-dev.202010051957.440e9e7",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-0.6.0-dev.202010051957.440e9e7.tgz",
"integrity": "sha512-+tCCF1IC1dyz5mmItMtWo8z6UWbRwoS8UsPCX6dAdIGkALGYY1bjqcOrkj+GK4n8Cu8slpwSoesow/No2jCXog==",
"requires": {
"@ionic/core": "0.6.0-dev.202010051922.f5d657c",
"@ionic/core": "0.6.0-dev.202010051957.440e9e7",
"ionicons": "^5.1.2"
}
},
"@ionic/vue-router": {
"version": "0.6.0-dev.202010051922.f5d657c",
"resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-0.6.0-dev.202010051922.f5d657c.tgz",
"integrity": "sha512-524eNmjFuEJh3OpuLBZXd1+k3R4/jruNxLt53Ro/Ux8SsI4FgPgm/Igv8SRjd1knvx9LFN+nlA8JTdHNB58SrA=="
"version": "0.6.0-dev.202010051957.440e9e7",
"resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-0.6.0-dev.202010051957.440e9e7.tgz",
"integrity": "sha512-4bRlHoLZdwsU4VwEP8WPudHkZy4uX/rqUvXg5Hn4YMxEGR/AfYm0TAyeQgh2I18GIcnu2Vjff3B46UkZj/5OjA=="
},
"@jest/console": {
"version": "24.9.0",

View File

@@ -11,8 +11,8 @@
"cypress": "node_modules/.bin/cypress run --headless --browser chrome"
},
"dependencies": {
"@ionic/vue": "0.6.0-dev.202010051922.f5d657c",
"@ionic/vue-router": "0.6.0-dev.202010051922.f5d657c",
"@ionic/vue": "0.6.0-dev.202010051957.440e9e7",
"@ionic/vue-router": "0.6.0-dev.202010051957.440e9e7",
"core-js": "^3.6.5",
"vue": "^3.0.0-0",
"vue-router": "^4.0.0-0"

View File

@@ -0,0 +1,43 @@
<template>
<div id="container">
<strong>{{ name }}</strong>
<p>Explore <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
</div>
</template>
<script lang="ts">
export default {
name: 'ExploreContainer',
props: {
name: String
}
}
</script>
<style scoped>
#container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
#container strong {
font-size: 20px;
line-height: 26px;
}
#container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
#container a {
text-decoration: none;
}
</style>

View File

@@ -46,6 +46,28 @@ const routes: Array<RouteRecordRaw> = [
component: () => import('@/views/NestedChildTwo.vue')
}
]
},
{
path: '/tabs/',
component: () => import('@/views/Tabs.vue'),
children: [
{
path: '',
redirect: '/tabs/tab1'
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue')
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue')
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue')
}
]
}
]

View File

@@ -29,6 +29,9 @@
<ion-item router-link="/nested" id="nested">
<ion-label>Nested Router Outlet</ion-label>
</ion-item>
<ion-item router-link="/tabs" id="tabs">
<ion-label>Tabs</ion-label>
</ion-item>
</ion-list>
</ion-content>

View File

@@ -0,0 +1,28 @@
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Tab 1</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 1</ion-title>
</ion-toolbar>
</ion-header>
<ExploreContainer name="Tab 1 page" />
</ion-content>
</ion-page>
</template>
<script>
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue';
import ExploreContainer from '@/components/ExploreContainer.vue';
export default {
name: 'Tab1',
components: { ExploreContainer, IonHeader, IonToolbar, IonTitle, IonContent, IonPage }
}
</script>

View File

@@ -0,0 +1,28 @@
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Tab 2</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 2</ion-title>
</ion-toolbar>
</ion-header>
<ExploreContainer name="Tab 2 page" />
</ion-content>
</ion-page>
</template>
<script>
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue';
import ExploreContainer from '@/components/ExploreContainer.vue';
export default {
name: 'Tab2',
components: { ExploreContainer, IonHeader, IonToolbar, IonTitle, IonContent, IonPage }
}
</script>

View File

@@ -0,0 +1,28 @@
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Tab 3</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 3</ion-title>
</ion-toolbar>
</ion-header>
<ExploreContainer name="Tab 3 page" />
</ion-content>
</ion-page>
</template>
<script>
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue';
import ExploreContainer from '@/components/ExploreContainer.vue';
export default {
name: 'Tab3',
components: { ExploreContainer, IonHeader, IonToolbar, IonTitle, IonContent, IonPage }
}
</script>

View File

@@ -0,0 +1,41 @@
<template>
<ion-page>
<ion-content>
<ion-tabs>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1" href="/tabs/tab1">
<ion-icon :icon="triangle" />
<ion-label>Tab 1</ion-label>
</ion-tab-button>
<ion-tab-button tab="tab2" href="/tabs/tab2">
<ion-icon :icon="ellipse" />
<ion-label>Tab 2</ion-label>
</ion-tab-button>
<ion-tab-button tab="tab3" href="/tabs/tab3">
<ion-icon :icon="square" />
<ion-label>Tab 3</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage } from '@ionic/vue';
import { ellipse, square, triangle } from 'ionicons/icons';
export default {
name: 'Tabs',
components: { IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage },
setup() {
return {
ellipse,
square,
triangle,
}
}
}
</script>