mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
@@ -5,4 +5,8 @@
|
||||
document.documentElement.setAttribute('dir', 'rtl');
|
||||
}
|
||||
|
||||
window.Ionic = window.Ionic || {};
|
||||
window.Ionic.config = window.Ionic.config || {};
|
||||
window.Ionic.config.experimentalTransitionShadow = true;
|
||||
|
||||
})();
|
||||
@@ -118,3 +118,47 @@
|
||||
:host(.content-sizing) .inner-scroll {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.transition-effect {
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
left: -100%;
|
||||
/* stylelint-enable property-blacklist */
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.transition-cover {
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: black;
|
||||
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.transition-shadow {
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
||||
/* stylelint-disable property-blacklist */
|
||||
right: 0;
|
||||
/* stylelint-enable property-blacklist */
|
||||
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAgCAYAAAAIXrg4AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTE3MDgzRkQ5QTkyMTFFOUEwNzQ5MkJFREE1NUY2MjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTE3MDgzRkU5QTkyMTFFOUEwNzQ5MkJFREE1NUY2MjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMTcwODNGQjlBOTIxMUU5QTA3NDkyQkVEQTU1RjYyNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxMTcwODNGQzlBOTIxMUU5QTA3NDkyQkVEQTU1RjYyNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PmePEuQAAABNSURBVHjaYvz//z8DIxAwMDAwATGMhmFmPDQuOSZks0AMmoJBaQHjkPfB0Lfg/2gQjVow+HPy/yHvg9GiYjQfjMbBqAWjFgy/4hogwADYqwdzxy5BuwAAAABJRU5ErkJggg==);
|
||||
background-repeat: repeat-y;
|
||||
background-size: 10px 16px;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Listen, Method, Prop, h, readTask } from '@stencil/core';
|
||||
|
||||
import { config } from '../../global/config';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import { Color, ScrollBaseDetail, ScrollDetail } from '../../interface';
|
||||
import { isPlatform } from '../../utils/platform';
|
||||
@@ -301,6 +302,8 @@ export class Content implements ComponentInterface {
|
||||
const mode = getIonMode(this);
|
||||
const { scrollX, scrollY, forceOverscroll } = this;
|
||||
|
||||
const transitionShadow = (mode === 'ios' && config.getBoolean('experimentalTransitionShadow', false));
|
||||
|
||||
this.resize();
|
||||
|
||||
return (
|
||||
@@ -328,6 +331,12 @@ export class Content implements ComponentInterface {
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
{transitionShadow ? (
|
||||
<div class="transition-effect">
|
||||
<div class="transition-cover"></div>
|
||||
<div class="transition-shadow"></div>
|
||||
</div>
|
||||
) : null}
|
||||
<slot name="fixed"></slot>
|
||||
</Host>
|
||||
);
|
||||
@@ -370,6 +379,8 @@ const updateScrollDetail = (
|
||||
const prevT = detail.timeStamp;
|
||||
const currentX = el.scrollLeft;
|
||||
const currentY = el.scrollTop;
|
||||
const timeDelta = timestamp - prevT;
|
||||
|
||||
if (shouldStart) {
|
||||
// remember the start positions
|
||||
detail.startTimeStamp = timestamp;
|
||||
@@ -383,7 +394,6 @@ const updateScrollDetail = (
|
||||
detail.deltaX = currentX - detail.startX;
|
||||
detail.deltaY = currentY - detail.startY;
|
||||
|
||||
const timeDelta = timestamp - prevT;
|
||||
if (timeDelta > 0 && timeDelta < 100) {
|
||||
const velocityX = (currentX - prevX) / timeDelta;
|
||||
const velocityY = (currentY - prevY) / timeDelta;
|
||||
|
||||
@@ -154,4 +154,4 @@
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -165,6 +165,11 @@ export interface IonicConfig {
|
||||
*/
|
||||
pickerLeave?: AnimationBuilder;
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: Adds a page shadow to transitioning pages on iOS. Disabled by default.
|
||||
*/
|
||||
experimentalTransitionShadow?: boolean;
|
||||
|
||||
// PRIVATE configs
|
||||
keyboardHeight?: number;
|
||||
inputShims?: boolean;
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
import { Animation } from '../../interface';
|
||||
import { TransitionOptions } from '../transition';
|
||||
|
||||
const DURATION = 500;
|
||||
const EASING = 'cubic-bezier(0.36,0.66,0.04,1)';
|
||||
const OPACITY = 'opacity';
|
||||
const TRANSFORM = 'transform';
|
||||
const TRANSLATEX = 'translateX';
|
||||
const CENTER = '0%';
|
||||
const OFF_OPACITY = 0.8;
|
||||
|
||||
export const shadow = <T extends Element>(el: T): ShadowRoot | T => {
|
||||
return el.shadowRoot || el;
|
||||
};
|
||||
|
||||
export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement, opts: TransitionOptions): Promise<Animation> => {
|
||||
|
||||
const DURATION = 540;
|
||||
const EASING = 'cubic-bezier(0.32,0.72,0,1)';
|
||||
const OPACITY = 'opacity';
|
||||
const TRANSFORM = 'transform';
|
||||
const TRANSLATEX = 'translateX';
|
||||
const CENTER = '0%';
|
||||
const OFF_OPACITY = 0.8;
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
const isRTL = (navEl.ownerDocument as any).dir === 'rtl';
|
||||
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
|
||||
const OFF_LEFT = isRTL ? '33%' : '-33%';
|
||||
|
||||
const enteringEl = opts.enteringEl;
|
||||
const leavingEl = opts.leavingEl;
|
||||
const contentEl = enteringEl.querySelector(':scope > ion-content');
|
||||
const headerEls = enteringEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *');
|
||||
const enteringToolBarEls = enteringEl.querySelectorAll(':scope > ion-header > ion-toolbar');
|
||||
const enteringContent = new AnimationC();
|
||||
|
||||
const rootTransition = new AnimationC();
|
||||
rootTransition
|
||||
@@ -37,18 +42,12 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
rootTransition.add(navDecor);
|
||||
}
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
// setting up enter view
|
||||
const contentEl = enteringEl.querySelector(':scope > ion-content');
|
||||
const headerEls = enteringEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *');
|
||||
const enteringToolBarEls = enteringEl.querySelectorAll(':scope > ion-header > ion-toolbar');
|
||||
const enteringContent = new AnimationC();
|
||||
|
||||
if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) {
|
||||
enteringContent.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs'));
|
||||
} else {
|
||||
enteringContent.addElement(contentEl);
|
||||
enteringContent.addElement(headerEls);
|
||||
enteringContent
|
||||
.addElement(contentEl)
|
||||
.addElement(headerEls);
|
||||
}
|
||||
|
||||
rootTransition.add(enteringContent);
|
||||
@@ -63,28 +62,61 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
enteringContent
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||
|
||||
if (contentEl) {
|
||||
const enteringTransitionEffectEl = shadow(contentEl).querySelector('.transition-effect');
|
||||
|
||||
if (enteringTransitionEffectEl) {
|
||||
const enteringTransitionCoverEl = enteringTransitionEffectEl.querySelector('.transition-cover');
|
||||
const enteringTransitionShadowEl = enteringTransitionEffectEl.querySelector('.transition-shadow');
|
||||
|
||||
const enteringTransitionEffect = new AnimationC();
|
||||
const enteringTransitionCover = new AnimationC();
|
||||
const enteringTransitionShadow = new AnimationC();
|
||||
|
||||
enteringTransitionEffect
|
||||
.addElement(enteringTransitionEffectEl)
|
||||
.beforeStyles({ opacity: '1' })
|
||||
.afterStyles({ opacity: '' });
|
||||
|
||||
enteringTransitionCover
|
||||
.addElement(enteringTransitionCoverEl)
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(OPACITY, 0, 0.1, true);
|
||||
|
||||
enteringTransitionShadow
|
||||
.addElement(enteringTransitionShadowEl)
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(OPACITY, 0.70, 0.03, true);
|
||||
|
||||
enteringContent
|
||||
.add(enteringTransitionEffect)
|
||||
.add(enteringTransitionCover)
|
||||
.add(enteringTransitionShadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enteringToolBarEls.forEach(enteringToolBarEl => {
|
||||
const enteringToolBar = new AnimationC();
|
||||
const enteringTitle = new AnimationC();
|
||||
const enteringToolBarButtons = new AnimationC();
|
||||
const enteringToolBarItems = new AnimationC();
|
||||
const enteringToolBarBg = new AnimationC();
|
||||
const enteringBackButton = new AnimationC();
|
||||
const backButtonEl = enteringToolBarEl.querySelector('ion-back-button');
|
||||
|
||||
enteringToolBar.addElement(enteringToolBarEl);
|
||||
rootTransition.add(enteringToolBar);
|
||||
|
||||
const enteringTitle = new AnimationC();
|
||||
enteringTitle.addElement(enteringToolBarEl.querySelector('ion-title'));
|
||||
|
||||
const enteringToolBarButtons = new AnimationC();
|
||||
enteringToolBarButtons.addElement(enteringToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||
|
||||
const enteringToolBarItems = new AnimationC();
|
||||
enteringToolBarItems.addElement(enteringToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])'));
|
||||
|
||||
const enteringToolBarBg = new AnimationC();
|
||||
enteringToolBarBg.addElement(shadow(enteringToolBarEl).querySelector('.toolbar-background'));
|
||||
|
||||
const enteringBackButton = new AnimationC();
|
||||
const backButtonEl = enteringToolBarEl.querySelector('ion-back-button');
|
||||
|
||||
if (backButtonEl) {
|
||||
enteringBackButton.addElement(backButtonEl);
|
||||
}
|
||||
@@ -133,10 +165,13 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
|
||||
// setup leaving view
|
||||
if (leavingEl) {
|
||||
|
||||
const leavingContent = new AnimationC();
|
||||
leavingContent.addElement(leavingEl.querySelector(':scope > ion-content'));
|
||||
leavingContent.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'));
|
||||
const leavingContentEl = leavingEl.querySelector(':scope > ion-content');
|
||||
|
||||
leavingContent
|
||||
.addElement(leavingContentEl)
|
||||
.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'));
|
||||
|
||||
rootTransition.add(leavingContent);
|
||||
|
||||
if (backDirection) {
|
||||
@@ -145,6 +180,38 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||
|
||||
if (leavingContentEl) {
|
||||
const leavingTransitionEffectEl = shadow(leavingContentEl).querySelector('.transition-effect');
|
||||
if (leavingTransitionEffectEl) {
|
||||
const leavingTransitionCoverEl = leavingTransitionEffectEl.querySelector('.transition-cover');
|
||||
const leavingTransitionShadowEl = leavingTransitionEffectEl.querySelector('.transition-shadow');
|
||||
|
||||
const leavingTransitionEffect = new AnimationC();
|
||||
const leavingTransitionCover = new AnimationC();
|
||||
const leavingTransitionShadow = new AnimationC();
|
||||
|
||||
leavingTransitionEffect
|
||||
.addElement(leavingTransitionEffectEl)
|
||||
.beforeStyles({ opacity: '1' })
|
||||
.afterStyles({ opacity: '' });
|
||||
|
||||
leavingTransitionCover
|
||||
.addElement(leavingTransitionCoverEl)
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(OPACITY, 0.1, 0, true);
|
||||
|
||||
leavingTransitionShadow
|
||||
.addElement(leavingTransitionShadowEl)
|
||||
.beforeClearStyles([OPACITY])
|
||||
.fromTo(OPACITY, 0.70, 0.03, true);
|
||||
|
||||
leavingContent
|
||||
.add(leavingTransitionEffect)
|
||||
.add(leavingTransitionCover)
|
||||
.add(leavingTransitionShadow);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// leaving content, forward direction
|
||||
leavingContent
|
||||
@@ -155,25 +222,26 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
const leavingToolBarEls = leavingEl.querySelectorAll(':scope > ion-header > ion-toolbar');
|
||||
leavingToolBarEls.forEach(leavingToolBarEl => {
|
||||
const leavingToolBar = new AnimationC();
|
||||
leavingToolBar.addElement(leavingToolBarEl);
|
||||
|
||||
const leavingTitle = new AnimationC();
|
||||
leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title'));
|
||||
|
||||
const leavingToolBarButtons = new AnimationC();
|
||||
leavingToolBarButtons.addElement(leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||
|
||||
const leavingToolBarItems = new AnimationC();
|
||||
const leavingToolBarItemEls = leavingToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])');
|
||||
const leavingToolBarBg = new AnimationC();
|
||||
const leavingBackButton = new AnimationC();
|
||||
const backButtonEl = leavingToolBarEl.querySelector('ion-back-button');
|
||||
|
||||
leavingToolBar.addElement(leavingToolBarEl);
|
||||
|
||||
leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title'));
|
||||
|
||||
leavingToolBarButtons.addElement(leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||
|
||||
if (leavingToolBarItemEls.length > 0) {
|
||||
leavingToolBarItems.addElement(leavingToolBarItemEls);
|
||||
}
|
||||
|
||||
const leavingToolBarBg = new AnimationC();
|
||||
leavingToolBarBg.addElement(shadow(leavingToolBarEl).querySelector('.toolbar-background'));
|
||||
|
||||
const leavingBackButton = new AnimationC();
|
||||
const backButtonEl = leavingToolBarEl.querySelector('ion-back-button');
|
||||
if (backButtonEl) {
|
||||
leavingBackButton.addElement(backButtonEl);
|
||||
}
|
||||
@@ -216,6 +284,7 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
leavingTitle
|
||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
||||
.afterClearStyles([TRANSFORM]);
|
||||
|
||||
leavingToolBarItems
|
||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
||||
.afterClearStyles([TRANSFORM, OPACITY]);
|
||||
@@ -226,6 +295,7 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Return the rootTransition promise
|
||||
return Promise.resolve(rootTransition);
|
||||
};
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { Animation } from '../../interface';
|
||||
import { TransitionOptions } from '../transition';
|
||||
|
||||
const TRANSLATEY = 'translateY';
|
||||
const OFF_BOTTOM = '40px';
|
||||
const CENTER = '0px';
|
||||
|
||||
export const mdTransitionAnimation = (AnimationC: Animation, _: HTMLElement, opts: TransitionOptions): Promise<Animation> => {
|
||||
const TRANSLATEY = 'translateY';
|
||||
const OFF_BOTTOM = '40px';
|
||||
const CENTER = '0px';
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
const enteringEl = opts.enteringEl;
|
||||
const leavingEl = opts.leavingEl;
|
||||
const ionPageElement = getIonPageElement(enteringEl);
|
||||
|
||||
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
|
||||
const rootTransition = new AnimationC();
|
||||
|
||||
rootTransition
|
||||
.addElement(ionPageElement)
|
||||
.beforeRemoveClass('ion-page-invisible');
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
|
||||
// animate the component itself
|
||||
if (backDirection) {
|
||||
rootTransition
|
||||
@@ -33,7 +32,6 @@ export const mdTransitionAnimation = (AnimationC: Animation, _: HTMLElement, opt
|
||||
}
|
||||
|
||||
// Animate toolbar if it's there
|
||||
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
|
||||
if (enteringToolbarEle) {
|
||||
const enteringToolBar = new AnimationC();
|
||||
enteringToolBar.addElement(enteringToolbarEle);
|
||||
@@ -63,6 +61,7 @@ const getIonPageElement = (element: HTMLElement) => {
|
||||
if (element.classList.contains('ion-page')) {
|
||||
return element;
|
||||
}
|
||||
|
||||
const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
|
||||
if (ionPage) {
|
||||
return ionPage;
|
||||
|
||||
Reference in New Issue
Block a user