mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
@ -1,7 +1,123 @@
|
||||
import { writeTask } from '@stencil/core';
|
||||
|
||||
import { createAnimation } from '../../';
|
||||
import { isPlatform } from '../../utils/platform';
|
||||
|
||||
// MD Native Refresher
|
||||
// -----------------------------
|
||||
type RefresherAnimationType = 'scale' | 'translate';
|
||||
|
||||
export const getRefresherAnimationType = (contentEl: HTMLIonContentElement): RefresherAnimationType => {
|
||||
const previousSibling = contentEl.previousElementSibling;
|
||||
const hasHeader = previousSibling !== null && previousSibling.tagName === 'ION-HEADER';
|
||||
|
||||
return hasHeader ? 'translate' : 'scale';
|
||||
};
|
||||
|
||||
export const createPullingAnimation = (type: RefresherAnimationType, pullingSpinner: HTMLElement) => {
|
||||
return type === 'scale' ? createScaleAnimation(pullingSpinner) : createTranslateAnimation(pullingSpinner);
|
||||
};
|
||||
|
||||
const createBaseAnimation = (pullingRefresherIcon: HTMLElement) => {
|
||||
const spinner = pullingRefresherIcon.querySelector('ion-spinner') as HTMLElement;
|
||||
const circle = spinner!.shadowRoot!.querySelector('circle') as any;
|
||||
const spinnerArrowContainer = pullingRefresherIcon.querySelector('.spinner-arrow-container') as HTMLElement;
|
||||
const arrowContainer = pullingRefresherIcon!.querySelector('.arrow-container');
|
||||
const arrow = (arrowContainer) ? arrowContainer!.querySelector('ion-icon') as HTMLElement : null;
|
||||
|
||||
const baseAnimation = createAnimation()
|
||||
.duration(1000)
|
||||
.easing('ease-out');
|
||||
|
||||
const spinnerArrowContainerAnimation = createAnimation()
|
||||
.addElement(spinnerArrowContainer)
|
||||
.keyframes([
|
||||
{ offset: 0, opacity: '0.3' },
|
||||
{ offset: 0.45, opacity: '0.3' },
|
||||
{ offset: 0.55, opacity: '1' },
|
||||
{ offset: 1, opacity: '1' }
|
||||
]);
|
||||
|
||||
const circleInnerAnimation = createAnimation()
|
||||
.addElement(circle)
|
||||
.keyframes([
|
||||
{ offset: 0, 'stroke-dasharray': '1px, 200px' },
|
||||
{ offset: 0.20, 'stroke-dasharray': '1px, 200px' },
|
||||
{ offset: 0.55, 'stroke-dasharray': '100px, 200px' },
|
||||
{ offset: 1, 'stroke-dasharray': '100px, 200px' }
|
||||
]);
|
||||
|
||||
const circleOuterAnimation = createAnimation()
|
||||
.addElement(spinner)
|
||||
.keyframes([
|
||||
{ offset: 0, transform: 'rotate(-90deg)' },
|
||||
{ offset: 1, transform: 'rotate(210deg)' }
|
||||
]);
|
||||
|
||||
/**
|
||||
* Only add arrow animation if present
|
||||
* this allows users to customize the spinners
|
||||
* without errors being thrown
|
||||
*/
|
||||
if (arrowContainer && arrow) {
|
||||
const arrowContainerAnimation = createAnimation()
|
||||
.addElement(arrowContainer)
|
||||
.keyframes([
|
||||
{ offset: 0, transform: 'rotate(0deg)' },
|
||||
{ offset: 0.30, transform: 'rotate(0deg)' },
|
||||
{ offset: 0.55, transform: 'rotate(280deg)' },
|
||||
{ offset: 1, transform: 'rotate(400deg)' }
|
||||
]);
|
||||
|
||||
const arrowAnimation = createAnimation()
|
||||
.addElement(arrow)
|
||||
.keyframes([
|
||||
{ offset: 0, transform: 'translateX(2px) scale(0)' },
|
||||
{ offset: 0.30, transform: 'translateX(2px) scale(0)' },
|
||||
{ offset: 0.55, transform: 'translateX(-1.5px) scale(1)' },
|
||||
{ offset: 1, transform: 'translateX(-1.5px) scale(1)' }
|
||||
]);
|
||||
|
||||
baseAnimation.addAnimation([arrowContainerAnimation, arrowAnimation]);
|
||||
}
|
||||
|
||||
return baseAnimation.addAnimation([spinnerArrowContainerAnimation, circleInnerAnimation, circleOuterAnimation]);
|
||||
};
|
||||
|
||||
const createScaleAnimation = (pullingRefresherIcon: HTMLElement) => {
|
||||
const height = pullingRefresherIcon.clientHeight;
|
||||
const spinnerAnimation = createAnimation()
|
||||
.addElement(pullingRefresherIcon)
|
||||
.keyframes([
|
||||
{ offset: 0, transform: `scale(0) translateY(-${height + 20}px)` },
|
||||
{ offset: 1, transform: 'scale(1) translateY(100px)' }
|
||||
]);
|
||||
|
||||
return createBaseAnimation(pullingRefresherIcon).addAnimation([spinnerAnimation]);
|
||||
};
|
||||
|
||||
const createTranslateAnimation = (pullingRefresherIcon: HTMLElement) => {
|
||||
const height = pullingRefresherIcon.clientHeight;
|
||||
const spinnerAnimation = createAnimation()
|
||||
.addElement(pullingRefresherIcon)
|
||||
.keyframes([
|
||||
{ offset: 0, transform: `translateY(-${height + 20}px)` },
|
||||
{ offset: 1, transform: 'translateY(100px)' }
|
||||
]);
|
||||
|
||||
return createBaseAnimation(pullingRefresherIcon).addAnimation([spinnerAnimation]);
|
||||
};
|
||||
|
||||
export const createSnapBackAnimation = (pullingRefresherIcon: HTMLElement) => {
|
||||
return createAnimation()
|
||||
.duration(125)
|
||||
.addElement(pullingRefresherIcon)
|
||||
.fromTo('transform', 'translateY(var(--ion-pulling-refresher-translate, 100px))', 'translateY(0px)');
|
||||
};
|
||||
|
||||
// iOS Native Refresher
|
||||
// -----------------------------
|
||||
|
||||
export const setSpinnerOpacity = (spinner: HTMLElement, opacity: number) => {
|
||||
spinner.style.setProperty('opacity', opacity.toString());
|
||||
};
|
||||
@ -29,6 +145,27 @@ export const handleScrollWhileRefreshing = (
|
||||
});
|
||||
};
|
||||
|
||||
export const translateElement = (el?: HTMLElement, value?: string) => {
|
||||
if (!el) { return Promise.resolve(); }
|
||||
|
||||
const trans = transitionEndAsync(el);
|
||||
|
||||
writeTask(() => {
|
||||
el.style.setProperty('transition', '0.2s all ease-out');
|
||||
|
||||
if (value === undefined) {
|
||||
el.style.removeProperty('transform');
|
||||
} else {
|
||||
el.style.setProperty('transform', `translate3d(0px, ${value}, 0px)`);
|
||||
}
|
||||
});
|
||||
|
||||
return trans;
|
||||
};
|
||||
|
||||
// Utils
|
||||
// -----------------------------
|
||||
|
||||
export const shouldUseNativeRefresher = (referenceEl: HTMLIonRefresherElement, mode: string) => {
|
||||
const pullingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-pulling ion-spinner');
|
||||
const refreshingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-refreshing ion-spinner');
|
||||
@ -36,26 +173,17 @@ export const shouldUseNativeRefresher = (referenceEl: HTMLIonRefresherElement, m
|
||||
return (
|
||||
pullingSpinner !== null &&
|
||||
refreshingSpinner !== null &&
|
||||
mode === 'ios' &&
|
||||
isPlatform('mobile')
|
||||
(
|
||||
(mode === 'ios' && isPlatform('mobile')) ||
|
||||
mode === 'md'
|
||||
)
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export const translateElement = (el?: HTMLElement, value?: string) => {
|
||||
export const transitionEndAsync = (el: HTMLElement | null) => {
|
||||
return new Promise(resolve => {
|
||||
if (!el) { return resolve(); }
|
||||
|
||||
transitionEnd(el, resolve);
|
||||
|
||||
writeTask(() => {
|
||||
el.style.setProperty('transition', '0.2s all ease-out');
|
||||
|
||||
if (value === undefined) {
|
||||
el.style.removeProperty('transform');
|
||||
} else {
|
||||
el.style.setProperty('transform', `translate3d(0px, ${value}, 0px)`);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user