123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /*!
- * (C) Ionic http://ionicframework.com - MIT License
- */
- import { writeTask } from '@stencil/core/internal/client';
- import { c as createAnimation } from './animation.js';
- import { t as transitionEndAsync, c as componentOnReady, k as clamp } from './helpers.js';
- const getRefresherAnimationType = (contentEl) => {
- const previousSibling = contentEl.previousElementSibling;
- const hasHeader = previousSibling !== null && previousSibling.tagName === 'ION-HEADER';
- return hasHeader ? 'translate' : 'scale';
- };
- const createPullingAnimation = (type, pullingSpinner, refresherEl) => {
- return type === 'scale'
- ? createScaleAnimation(pullingSpinner, refresherEl)
- : createTranslateAnimation(pullingSpinner, refresherEl);
- };
- const createBaseAnimation = (pullingRefresherIcon) => {
- const spinner = pullingRefresherIcon.querySelector('ion-spinner');
- const circle = spinner.shadowRoot.querySelector('circle');
- const spinnerArrowContainer = pullingRefresherIcon.querySelector('.spinner-arrow-container');
- const arrowContainer = pullingRefresherIcon.querySelector('.arrow-container');
- const arrow = arrowContainer ? arrowContainer.querySelector('ion-icon') : 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, strokeDasharray: '1px, 200px' },
- { offset: 0.2, strokeDasharray: '1px, 200px' },
- { offset: 0.55, strokeDasharray: '100px, 200px' },
- { offset: 1, strokeDasharray: '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.3, 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.3, 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, refresherEl) => {
- /**
- * Do not take the height of the refresher icon
- * because at this point the DOM has not updated,
- * so the refresher icon is still hidden with
- * display: none.
- * The `ion-refresher` container height
- * is roughly the amount we need to offset
- * the icon by when pulling down.
- */
- const height = refresherEl.clientHeight;
- const spinnerAnimation = createAnimation()
- .addElement(pullingRefresherIcon)
- .keyframes([
- { offset: 0, transform: `scale(0) translateY(-${height}px)` },
- { offset: 1, transform: 'scale(1) translateY(100px)' },
- ]);
- return createBaseAnimation(pullingRefresherIcon).addAnimation([spinnerAnimation]);
- };
- const createTranslateAnimation = (pullingRefresherIcon, refresherEl) => {
- /**
- * Do not take the height of the refresher icon
- * because at this point the DOM has not updated,
- * so the refresher icon is still hidden with
- * display: none.
- * The `ion-refresher` container height
- * is roughly the amount we need to offset
- * the icon by when pulling down.
- */
- const height = refresherEl.clientHeight;
- const spinnerAnimation = createAnimation()
- .addElement(pullingRefresherIcon)
- .keyframes([
- { offset: 0, transform: `translateY(-${height}px)` },
- { offset: 1, transform: 'translateY(100px)' },
- ]);
- return createBaseAnimation(pullingRefresherIcon).addAnimation([spinnerAnimation]);
- };
- const createSnapBackAnimation = (pullingRefresherIcon) => {
- return createAnimation()
- .duration(125)
- .addElement(pullingRefresherIcon)
- .fromTo('transform', 'translateY(var(--ion-pulling-refresher-translate, 100px))', 'translateY(0px)');
- };
- // iOS Native Refresher
- // -----------------------------
- const setSpinnerOpacity = (spinner, opacity) => {
- spinner.style.setProperty('opacity', opacity.toString());
- };
- const handleScrollWhilePulling = (ticks, numTicks, pullAmount) => {
- const max = 1;
- writeTask(() => {
- ticks.forEach((el, i) => {
- /**
- * Compute the opacity of each tick
- * mark as a percentage of the pullAmount
- * offset by max / numTicks so
- * the tick marks are shown staggered.
- */
- const min = i * (max / numTicks);
- const range = max - min;
- const start = pullAmount - min;
- const progression = clamp(0, start / range, 1);
- el.style.setProperty('opacity', progression.toString());
- });
- });
- };
- const handleScrollWhileRefreshing = (spinner, lastVelocityY) => {
- writeTask(() => {
- // If user pulls down quickly, the spinner should spin faster
- spinner.style.setProperty('--refreshing-rotation-duration', lastVelocityY >= 1.0 ? '0.5s' : '2s');
- spinner.style.setProperty('opacity', '1');
- });
- };
- const translateElement = (el, value, duration = 200) => {
- if (!el) {
- return Promise.resolve();
- }
- const trans = transitionEndAsync(el, duration);
- writeTask(() => {
- el.style.setProperty('transition', `${duration}ms all ease-out`);
- if (value === undefined) {
- el.style.removeProperty('transform');
- }
- else {
- el.style.setProperty('transform', `translate3d(0px, ${value}, 0px)`);
- }
- });
- return trans;
- };
- // Utils
- // -----------------------------
- /**
- * In order to use the native iOS refresher the device must support rubber band scrolling.
- * As part of this, we need to exclude Desktop Safari because it has a slightly different rubber band effect that is not compatible with the native refresher in Ionic.
- *
- * We also need to be careful not to include devices that spoof their user agent.
- * For example, when using iOS emulation in Chrome the user agent will be spoofed such that
- * navigator.maxTouchPointer > 0. To work around this,
- * we check to see if the apple-pay-logo is supported as a named image which is only
- * true on Apple devices.
- *
- * We previously checked referencEl.style.webkitOverflowScrolling to explicitly check
- * for rubber band support. However, this property was removed on iPadOS and it's possible
- * that this will be removed on iOS in the future too.
- *
- */
- const supportsRubberBandScrolling = () => {
- return navigator.maxTouchPoints > 0 && CSS.supports('background: -webkit-named-image(apple-pay-logo-black)');
- };
- const shouldUseNativeRefresher = async (referenceEl, mode) => {
- const refresherContent = referenceEl.querySelector('ion-refresher-content');
- if (!refresherContent) {
- return Promise.resolve(false);
- }
- await new Promise((resolve) => componentOnReady(refresherContent, resolve));
- const pullingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-pulling ion-spinner');
- const refreshingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-refreshing ion-spinner');
- return (pullingSpinner !== null &&
- refreshingSpinner !== null &&
- ((mode === 'ios' && supportsRubberBandScrolling()) || mode === 'md'));
- };
- export { setSpinnerOpacity as a, handleScrollWhilePulling as b, createPullingAnimation as c, createSnapBackAnimation as d, supportsRubberBandScrolling as e, getRefresherAnimationType as g, handleScrollWhileRefreshing as h, shouldUseNativeRefresher as s, translateElement as t };
|