mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
3 Commits
ionic-modu
...
sp/FW-5870
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74a33e7b8e | ||
|
|
c91bd63ed5 | ||
|
|
905e98c546 |
@@ -51,6 +51,8 @@ This section details the desktop browser, JavaScript framework, and mobile platf
|
||||
| iOS | 15+ |
|
||||
| Android | 5.1+ with Chromium 89+ |
|
||||
|
||||
Ionic Framework v8 removes backwards support for CSS Animations in favor of the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API). All minimum browser versions listed above support the Web Animations API.
|
||||
|
||||
<h2 id="version-8x-dark-theme">Dark Theme</h2>
|
||||
|
||||
In previous versions, it was recommended to define the dark theme in the following way:
|
||||
|
||||
@@ -1,41 +1,5 @@
|
||||
import type { AnimationKeyFrames } from './animation-interface';
|
||||
|
||||
let animationPrefix: string | undefined;
|
||||
|
||||
/**
|
||||
* Web Animations requires hyphenated CSS properties
|
||||
* to be written in camelCase when animating
|
||||
*/
|
||||
export const processKeyframes = (keyframes: AnimationKeyFrames) => {
|
||||
keyframes.forEach((keyframe) => {
|
||||
for (const key in keyframe) {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (keyframe.hasOwnProperty(key)) {
|
||||
const value = keyframe[key];
|
||||
|
||||
if (key === 'easing') {
|
||||
const newKey = 'animation-timing-function';
|
||||
keyframe[newKey] = value;
|
||||
delete keyframe[key];
|
||||
} else {
|
||||
const newKey = convertCamelCaseToHypen(key);
|
||||
|
||||
if (newKey !== key) {
|
||||
keyframe[newKey] = value;
|
||||
delete keyframe[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return keyframes;
|
||||
};
|
||||
|
||||
const convertCamelCaseToHypen = (str: string) => {
|
||||
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
};
|
||||
|
||||
export const getAnimationPrefix = (el: HTMLElement): string => {
|
||||
if (animationPrefix === undefined) {
|
||||
const supportsUnprefixed = el.style.animationName !== undefined;
|
||||
@@ -50,70 +14,6 @@ export const setStyleProperty = (element: HTMLElement, propertyName: string, val
|
||||
element.style.setProperty(prefix + propertyName, value);
|
||||
};
|
||||
|
||||
export const removeStyleProperty = (element: HTMLElement, propertyName: string) => {
|
||||
const prefix = propertyName.startsWith('animation') ? getAnimationPrefix(element) : '';
|
||||
element.style.removeProperty(prefix + propertyName);
|
||||
};
|
||||
|
||||
export const animationEnd = (el: HTMLElement | null, callback: (ev?: TransitionEvent) => void) => {
|
||||
let unRegTrans: (() => void) | undefined;
|
||||
const opts: AddEventListenerOptions = { passive: true };
|
||||
|
||||
const unregister = () => {
|
||||
if (unRegTrans) {
|
||||
unRegTrans();
|
||||
}
|
||||
};
|
||||
|
||||
const onTransitionEnd = (ev: Event) => {
|
||||
if (el === ev.target) {
|
||||
unregister();
|
||||
callback(ev as TransitionEvent);
|
||||
}
|
||||
};
|
||||
|
||||
if (el) {
|
||||
el.addEventListener('webkitAnimationEnd', onTransitionEnd, opts);
|
||||
el.addEventListener('animationend', onTransitionEnd, opts);
|
||||
|
||||
unRegTrans = () => {
|
||||
el.removeEventListener('webkitAnimationEnd', onTransitionEnd, opts);
|
||||
el.removeEventListener('animationend', onTransitionEnd, opts);
|
||||
};
|
||||
}
|
||||
|
||||
return unregister;
|
||||
};
|
||||
|
||||
// TODO(FW-2832): type
|
||||
export const generateKeyframeRules = (keyframes: any[] = []) => {
|
||||
return keyframes
|
||||
.map((keyframe) => {
|
||||
const offset = keyframe.offset;
|
||||
|
||||
const frameString = [];
|
||||
for (const property in keyframe) {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (keyframe.hasOwnProperty(property) && property !== 'offset') {
|
||||
frameString.push(`${property}: ${keyframe[property]};`);
|
||||
}
|
||||
}
|
||||
|
||||
return `${offset * 100}% { ${frameString.join(' ')} }`;
|
||||
})
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
const keyframeIds: string[] = [];
|
||||
|
||||
export const generateKeyframeName = (keyframeRules: string) => {
|
||||
let index = keyframeIds.indexOf(keyframeRules);
|
||||
if (index < 0) {
|
||||
index = keyframeIds.push(keyframeRules) - 1;
|
||||
}
|
||||
return `ion-animation-${index}`;
|
||||
};
|
||||
|
||||
export const getStyleContainer = (element: HTMLElement) => {
|
||||
// getRootNode is not always available in SSR environments.
|
||||
// TODO(FW-2832): types
|
||||
@@ -121,28 +21,6 @@ export const getStyleContainer = (element: HTMLElement) => {
|
||||
return rootNode.head || rootNode;
|
||||
};
|
||||
|
||||
export const createKeyframeStylesheet = (
|
||||
keyframeName: string,
|
||||
keyframeRules: string,
|
||||
element: HTMLElement
|
||||
): HTMLElement => {
|
||||
const styleContainer = getStyleContainer(element);
|
||||
const keyframePrefix = getAnimationPrefix(element);
|
||||
|
||||
const existingStylesheet = styleContainer.querySelector('#' + keyframeName);
|
||||
if (existingStylesheet) {
|
||||
return existingStylesheet;
|
||||
}
|
||||
|
||||
const stylesheet = (element.ownerDocument ?? document).createElement('style');
|
||||
stylesheet.id = keyframeName;
|
||||
stylesheet.textContent = `@${keyframePrefix}keyframes ${keyframeName} { ${keyframeRules} } @${keyframePrefix}keyframes ${keyframeName}-alt { ${keyframeRules} }`;
|
||||
|
||||
styleContainer.appendChild(stylesheet);
|
||||
|
||||
return stylesheet;
|
||||
};
|
||||
|
||||
export const addClassToArray = (classes: string[] = [], className: string | string[] | undefined): string[] => {
|
||||
if (className !== undefined) {
|
||||
const classNameToAppend = Array.isArray(className) ? className : [className];
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { win } from '../browser';
|
||||
import { raf } from '../helpers';
|
||||
|
||||
import type {
|
||||
Animation,
|
||||
AnimationCallbackOptions,
|
||||
@@ -12,18 +9,7 @@ import type {
|
||||
AnimationLifecycle,
|
||||
AnimationPlayOptions,
|
||||
} from './animation-interface';
|
||||
import {
|
||||
addClassToArray,
|
||||
animationEnd,
|
||||
createKeyframeStylesheet,
|
||||
generateKeyframeName,
|
||||
generateKeyframeRules,
|
||||
processKeyframes,
|
||||
removeStyleProperty,
|
||||
setStyleProperty,
|
||||
} from './animation-utils';
|
||||
|
||||
// TODO(FW-2832): types
|
||||
import { addClassToArray, setStyleProperty } from './animation-utils';
|
||||
|
||||
interface AnimationOnFinishCallback {
|
||||
c: AnimationLifecycle;
|
||||
@@ -64,7 +50,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
let willComplete = true;
|
||||
let finished = false;
|
||||
let shouldCalculateNumAnimations = true;
|
||||
let keyframeName: string | undefined;
|
||||
let ani: Animation;
|
||||
let paused = false;
|
||||
|
||||
@@ -80,14 +65,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
const _afterAddReadFunctions: AnimationReadWriteCallback[] = [];
|
||||
const _afterAddWriteFunctions: AnimationReadWriteCallback[] = [];
|
||||
const webAnimations: globalThis.Animation[] = [];
|
||||
const supportsAnimationEffect =
|
||||
typeof (AnimationEffect as any) === 'function' ||
|
||||
(win !== undefined && typeof (win as any).AnimationEffect === 'function');
|
||||
const supportsWebAnimations =
|
||||
typeof (Element as any) === 'function' &&
|
||||
typeof (Element as any).prototype!.animate === 'function' &&
|
||||
supportsAnimationEffect;
|
||||
const ANIMATION_END_FALLBACK_PADDING_MS = 100;
|
||||
|
||||
const getWebAnimations = () => {
|
||||
return webAnimations;
|
||||
@@ -192,28 +169,11 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
* the animation's elements.
|
||||
*/
|
||||
const cleanUpElements = () => {
|
||||
if (supportsWebAnimations) {
|
||||
webAnimations.forEach((animation) => {
|
||||
animation.cancel();
|
||||
});
|
||||
webAnimations.forEach((animation) => {
|
||||
animation.cancel();
|
||||
});
|
||||
|
||||
webAnimations.length = 0;
|
||||
} else {
|
||||
const elementsArray = elements.slice();
|
||||
|
||||
raf(() => {
|
||||
elementsArray.forEach((element) => {
|
||||
removeStyleProperty(element, 'animation-name');
|
||||
removeStyleProperty(element, 'animation-duration');
|
||||
removeStyleProperty(element, 'animation-timing-function');
|
||||
removeStyleProperty(element, 'animation-iteration-count');
|
||||
removeStyleProperty(element, 'animation-delay');
|
||||
removeStyleProperty(element, 'animation-play-state');
|
||||
removeStyleProperty(element, 'animation-fill-mode');
|
||||
removeStyleProperty(element, 'animation-direction');
|
||||
});
|
||||
});
|
||||
}
|
||||
webAnimations.length = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -436,15 +396,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
};
|
||||
|
||||
const duration = (animationDuration: number) => {
|
||||
/**
|
||||
* CSS Animation Durations of 0ms work fine on Chrome
|
||||
* but do not run on Safari, so force it to 1ms to
|
||||
* get it to run on both platforms.
|
||||
*/
|
||||
if (!supportsWebAnimations && animationDuration === 0) {
|
||||
animationDuration = 1;
|
||||
}
|
||||
|
||||
_duration = animationDuration;
|
||||
|
||||
update(true);
|
||||
@@ -469,10 +420,10 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
const addElement = (el: Element | Element[] | Node | Node[] | NodeList | undefined | null) => {
|
||||
if (el != null) {
|
||||
if ((el as Node).nodeType === 1) {
|
||||
elements.push(el as any);
|
||||
elements.push(el as HTMLElement);
|
||||
} else if ((el as NodeList).length >= 0) {
|
||||
for (let i = 0; i < (el as NodeList).length; i++) {
|
||||
elements.push((el as any)[i]);
|
||||
elements.push((el as HTMLElement[])[i]);
|
||||
}
|
||||
} else {
|
||||
console.error('Invalid addElement value');
|
||||
@@ -483,7 +434,7 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
};
|
||||
|
||||
const addAnimation = (animationToAdd: Animation | Animation[]) => {
|
||||
if ((animationToAdd as any) != null) {
|
||||
if (animationToAdd != null) {
|
||||
if (Array.isArray(animationToAdd)) {
|
||||
for (const animation of animationToAdd) {
|
||||
animation.parent(ani);
|
||||
@@ -509,33 +460,29 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
};
|
||||
|
||||
const updateKeyframes = (keyframeValues: AnimationKeyFrames) => {
|
||||
if (supportsWebAnimations) {
|
||||
getWebAnimations().forEach((animation) => {
|
||||
/**
|
||||
* animation.effect's type is AnimationEffect.
|
||||
* However, in this case we have a more specific
|
||||
* type of AnimationEffect called KeyframeEffect which
|
||||
* inherits from AnimationEffect. As a result,
|
||||
* we cast animation.effect to KeyframeEffect.
|
||||
*/
|
||||
const keyframeEffect = animation.effect as KeyframeEffect;
|
||||
getWebAnimations().forEach((animation) => {
|
||||
/**
|
||||
* animation.effect's type is AnimationEffect.
|
||||
* However, in this case we have a more specific
|
||||
* type of AnimationEffect called KeyframeEffect which
|
||||
* inherits from AnimationEffect. As a result,
|
||||
* we cast animation.effect to KeyframeEffect.
|
||||
*/
|
||||
const keyframeEffect = animation.effect as KeyframeEffect;
|
||||
|
||||
/**
|
||||
* setKeyframes is not supported in all browser
|
||||
* versions that Ionic supports, so we need to
|
||||
* check for support before using it.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (keyframeEffect.setKeyframes) {
|
||||
keyframeEffect.setKeyframes(keyframeValues);
|
||||
} else {
|
||||
const newEffect = new KeyframeEffect(keyframeEffect.target, keyframeValues, keyframeEffect.getTiming());
|
||||
animation.effect = newEffect;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
initializeCSSAnimation();
|
||||
}
|
||||
/**
|
||||
* setKeyframes is not supported in all browser
|
||||
* versions that Ionic supports, so we need to
|
||||
* check for support before using it.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (keyframeEffect.setKeyframes) {
|
||||
keyframeEffect.setKeyframes(keyframeValues);
|
||||
} else {
|
||||
const newEffect = new KeyframeEffect(keyframeEffect.target, keyframeValues, keyframeEffect.getTiming());
|
||||
animation.effect = newEffect;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -643,39 +590,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
}
|
||||
};
|
||||
|
||||
const initializeCSSAnimation = (toggleAnimationName = true) => {
|
||||
cleanUpStyleSheets();
|
||||
|
||||
const processedKeyframes = processKeyframes(_keyframes);
|
||||
elements.forEach((element) => {
|
||||
if (processedKeyframes.length > 0) {
|
||||
const keyframeRules = generateKeyframeRules(processedKeyframes);
|
||||
keyframeName = animationId !== undefined ? animationId : generateKeyframeName(keyframeRules);
|
||||
const stylesheet = createKeyframeStylesheet(keyframeName, keyframeRules, element);
|
||||
stylesheets.push(stylesheet);
|
||||
|
||||
setStyleProperty(element, 'animation-duration', `${getDuration()}ms`);
|
||||
setStyleProperty(element, 'animation-timing-function', getEasing());
|
||||
setStyleProperty(element, 'animation-delay', `${getDelay()}ms`);
|
||||
setStyleProperty(element, 'animation-fill-mode', getFill());
|
||||
setStyleProperty(element, 'animation-direction', getDirection());
|
||||
|
||||
const iterationsCount = getIterations() === Infinity ? 'infinite' : getIterations().toString();
|
||||
|
||||
setStyleProperty(element, 'animation-iteration-count', iterationsCount);
|
||||
setStyleProperty(element, 'animation-play-state', 'paused');
|
||||
|
||||
if (toggleAnimationName) {
|
||||
setStyleProperty(element, 'animation-name', `${stylesheet.id}-alt`);
|
||||
}
|
||||
|
||||
raf(() => {
|
||||
setStyleProperty(element, 'animation-name', stylesheet.id || null);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const initializeWebAnimation = () => {
|
||||
elements.forEach((element) => {
|
||||
const animation = element.animate(_keyframes, {
|
||||
@@ -700,15 +614,11 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
}
|
||||
};
|
||||
|
||||
const initializeAnimation = (toggleAnimationName = true) => {
|
||||
const initializeAnimation = () => {
|
||||
beforeAnimation();
|
||||
|
||||
if (_keyframes.length > 0) {
|
||||
if (supportsWebAnimations) {
|
||||
initializeWebAnimation();
|
||||
} else {
|
||||
initializeCSSAnimation(toggleAnimationName);
|
||||
}
|
||||
initializeWebAnimation();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
@@ -716,22 +626,11 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
|
||||
const setAnimationStep = (step: number) => {
|
||||
step = Math.min(Math.max(step, 0), 0.9999);
|
||||
if (supportsWebAnimations) {
|
||||
webAnimations.forEach((animation) => {
|
||||
// When creating the animation the delay is guaranteed to be set to a number.
|
||||
animation.currentTime = animation.effect!.getComputedTiming().delay! + getDuration() * step;
|
||||
animation.pause();
|
||||
});
|
||||
} else {
|
||||
const animationDuration = `-${getDuration() * step}ms`;
|
||||
|
||||
elements.forEach((element) => {
|
||||
if (_keyframes.length > 0) {
|
||||
setStyleProperty(element, 'animation-delay', animationDuration);
|
||||
setStyleProperty(element, 'animation-play-state', 'paused');
|
||||
}
|
||||
});
|
||||
}
|
||||
webAnimations.forEach((animation) => {
|
||||
// When creating the animation the delay is guaranteed to be set to a number.
|
||||
animation.currentTime = animation.effect!.getComputedTiming().delay! + getDuration() * step;
|
||||
animation.pause();
|
||||
});
|
||||
};
|
||||
|
||||
const updateWebAnimation = (step?: number) => {
|
||||
@@ -751,35 +650,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
}
|
||||
};
|
||||
|
||||
const updateCSSAnimation = (toggleAnimationName = true, step?: number) => {
|
||||
raf(() => {
|
||||
elements.forEach((element) => {
|
||||
setStyleProperty(element, 'animation-name', keyframeName || null);
|
||||
setStyleProperty(element, 'animation-duration', `${getDuration()}ms`);
|
||||
setStyleProperty(element, 'animation-timing-function', getEasing());
|
||||
setStyleProperty(
|
||||
element,
|
||||
'animation-delay',
|
||||
step !== undefined ? `-${step! * getDuration()}ms` : `${getDelay()}ms`
|
||||
);
|
||||
setStyleProperty(element, 'animation-fill-mode', getFill() || null);
|
||||
setStyleProperty(element, 'animation-direction', getDirection() || null);
|
||||
|
||||
const iterationsCount = getIterations() === Infinity ? 'infinite' : getIterations().toString();
|
||||
|
||||
setStyleProperty(element, 'animation-iteration-count', iterationsCount);
|
||||
|
||||
if (toggleAnimationName) {
|
||||
setStyleProperty(element, 'animation-name', `${keyframeName}-alt`);
|
||||
}
|
||||
|
||||
raf(() => {
|
||||
setStyleProperty(element, 'animation-name', keyframeName || null);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const update = (deep = false, toggleAnimationName = true, step?: number) => {
|
||||
if (deep) {
|
||||
childAnimations.forEach((animation) => {
|
||||
@@ -787,11 +657,7 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
});
|
||||
}
|
||||
|
||||
if (supportsWebAnimations) {
|
||||
updateWebAnimation(step);
|
||||
} else {
|
||||
updateCSSAnimation(toggleAnimationName, step);
|
||||
}
|
||||
updateWebAnimation(step);
|
||||
|
||||
return ani;
|
||||
};
|
||||
@@ -842,21 +708,11 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
willComplete = false;
|
||||
}
|
||||
|
||||
if (supportsWebAnimations) {
|
||||
update();
|
||||
setAnimationStep(1 - step);
|
||||
} else {
|
||||
forceDelayValue = (1 - step) * getDuration() * -1;
|
||||
update(false, false);
|
||||
}
|
||||
update();
|
||||
setAnimationStep(1 - step);
|
||||
} else if (playTo === 1) {
|
||||
if (supportsWebAnimations) {
|
||||
update();
|
||||
setAnimationStep(step);
|
||||
} else {
|
||||
forceDelayValue = step * getDuration() * -1;
|
||||
update(false, false);
|
||||
}
|
||||
update();
|
||||
setAnimationStep(step);
|
||||
}
|
||||
|
||||
if (playTo !== undefined && !parentAnimation) {
|
||||
@@ -868,15 +724,9 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
|
||||
const pauseAnimation = () => {
|
||||
if (initialized) {
|
||||
if (supportsWebAnimations) {
|
||||
webAnimations.forEach((animation) => {
|
||||
animation.pause();
|
||||
});
|
||||
} else {
|
||||
elements.forEach((element) => {
|
||||
setStyleProperty(element, 'animation-play-state', 'paused');
|
||||
});
|
||||
}
|
||||
webAnimations.forEach((animation) => {
|
||||
animation.pause();
|
||||
});
|
||||
|
||||
paused = true;
|
||||
}
|
||||
@@ -892,79 +742,12 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
return ani;
|
||||
};
|
||||
|
||||
const onAnimationEndFallback = () => {
|
||||
cssAnimationsTimerFallback = undefined;
|
||||
animationFinish();
|
||||
};
|
||||
|
||||
const clearCSSAnimationsTimeout = () => {
|
||||
if (cssAnimationsTimerFallback) {
|
||||
clearTimeout(cssAnimationsTimerFallback);
|
||||
}
|
||||
};
|
||||
|
||||
const playCSSAnimations = () => {
|
||||
clearCSSAnimationsTimeout();
|
||||
|
||||
raf(() => {
|
||||
elements.forEach((element) => {
|
||||
if (_keyframes.length > 0) {
|
||||
setStyleProperty(element, 'animation-play-state', 'running');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (_keyframes.length === 0 || elements.length === 0) {
|
||||
animationFinish();
|
||||
} else {
|
||||
/**
|
||||
* This is a catchall in the event that a CSS Animation did not finish.
|
||||
* The Web Animations API has mechanisms in place for preventing this.
|
||||
* CSS Animations will not fire an `animationend` event
|
||||
* for elements with `display: none`. The Web Animations API
|
||||
* accounts for this, but using raw CSS Animations requires
|
||||
* this workaround.
|
||||
*/
|
||||
const animationDelay = getDelay() || 0;
|
||||
const animationDuration = getDuration() || 0;
|
||||
const animationIterations = getIterations() || 1;
|
||||
|
||||
// No need to set a timeout when animation has infinite iterations
|
||||
if (isFinite(animationIterations)) {
|
||||
cssAnimationsTimerFallback = setTimeout(
|
||||
onAnimationEndFallback,
|
||||
animationDelay + animationDuration * animationIterations + ANIMATION_END_FALLBACK_PADDING_MS
|
||||
);
|
||||
}
|
||||
|
||||
animationEnd(elements[0], () => {
|
||||
clearCSSAnimationsTimeout();
|
||||
|
||||
/**
|
||||
* Ensure that clean up
|
||||
* is always done a frame
|
||||
* before the onFinish handlers
|
||||
* are fired. Otherwise, there
|
||||
* may be flickering if a new
|
||||
* animation is started on the same
|
||||
* element too quickly
|
||||
*/
|
||||
raf(() => {
|
||||
clearCSSAnimationPlayState();
|
||||
raf(animationFinish);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const clearCSSAnimationPlayState = () => {
|
||||
elements.forEach((element) => {
|
||||
removeStyleProperty(element, 'animation-duration');
|
||||
removeStyleProperty(element, 'animation-delay');
|
||||
removeStyleProperty(element, 'animation-play-state');
|
||||
});
|
||||
};
|
||||
|
||||
const playWebAnimations = () => {
|
||||
webAnimations.forEach((animation) => {
|
||||
animation.play();
|
||||
@@ -976,12 +759,8 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
};
|
||||
|
||||
const resetAnimation = () => {
|
||||
if (supportsWebAnimations) {
|
||||
setAnimationStep(0);
|
||||
updateWebAnimation();
|
||||
} else {
|
||||
updateCSSAnimation();
|
||||
}
|
||||
setAnimationStep(0);
|
||||
updateWebAnimation();
|
||||
};
|
||||
|
||||
const play = (opts?: AnimationPlayOptions) => {
|
||||
@@ -1038,11 +817,7 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
animation.play();
|
||||
});
|
||||
|
||||
if (supportsWebAnimations) {
|
||||
playWebAnimations();
|
||||
} else {
|
||||
playCSSAnimations();
|
||||
}
|
||||
playWebAnimations();
|
||||
|
||||
paused = false;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createAnimation } from '../animation';
|
||||
import type { Animation } from '../animation-interface';
|
||||
import { processKeyframes } from '../animation-utils';
|
||||
import { getTimeGivenProgression } from '../cubic-bezier';
|
||||
|
||||
describe('Animation Class', () => {
|
||||
@@ -228,18 +227,6 @@ describe('Animation Class', () => {
|
||||
expect(animation.getKeyframes().length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should convert properties for CSS Animations', () => {
|
||||
const processedKeyframes = processKeyframes([
|
||||
{ borderRadius: '0px', easing: 'ease-in', offset: 0 },
|
||||
{ borderRadius: '4px', easing: 'ease-out', offset: 1 },
|
||||
]);
|
||||
|
||||
expect(processedKeyframes).toEqual([
|
||||
{ 'border-radius': '0px', 'animation-timing-function': 'ease-in', offset: 0 },
|
||||
{ 'border-radius': '4px', 'animation-timing-function': 'ease-out', offset: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should set the from keyframe properly', () => {
|
||||
animation.from('opacity', 0).from('background', 'red').from('color', 'purple');
|
||||
|
||||
|
||||
@@ -12,11 +12,6 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
await page.goto('/src/utils/animation/test/animationbuilder', config);
|
||||
await testNavigation(page);
|
||||
});
|
||||
|
||||
test('ios-transition css', async ({ page }) => {
|
||||
await page.goto('/src/utils/animation/test/animationbuilder?ionic:_forceCSSAnimations=true', config);
|
||||
await testNavigation(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script>
|
||||
const forceCSSAnimations = new URLSearchParams(window.location.search).get('ionic:_forceCSSAnimations');
|
||||
if (forceCSSAnimations) {
|
||||
Element.prototype.animate = null;
|
||||
}
|
||||
|
||||
class PageRoot extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
|
||||
@@ -7,11 +7,6 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await page.goto('/src/utils/animation/test/basic', config);
|
||||
await testPage(page);
|
||||
});
|
||||
|
||||
test(`should resolve using css animations`, async ({ page }) => {
|
||||
await page.goto('/src/utils/animation/test/basic?ionic:_forceCSSAnimations=true', config);
|
||||
await testPage(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
const forceCSSAnimations = new URLSearchParams(window.location.search).get('ionic:_forceCSSAnimations');
|
||||
if (forceCSSAnimations) {
|
||||
Element.prototype.animate = null;
|
||||
}
|
||||
|
||||
import { createAnimation } from '../../../../dist/ionic/index.esm.js';
|
||||
|
||||
const squareA = document.querySelector('.square-a');
|
||||
|
||||
@@ -8,11 +8,6 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await page.goto('/src/utils/animation/test/display', config);
|
||||
await testDisplay(page);
|
||||
});
|
||||
|
||||
test(`should resolve using css animations`, async ({ page }) => {
|
||||
await page.goto('/src/utils/animation/test/display?ionic:_forceCSSAnimations=true', config);
|
||||
await testDisplay(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
const forceCSSAnimations = new URLSearchParams(window.location.search).get('ionic:_forceCSSAnimations');
|
||||
if (forceCSSAnimations) {
|
||||
Element.prototype.animate = null;
|
||||
}
|
||||
|
||||
import { createAnimation } from '../../../../dist/ionic/index.esm.js';
|
||||
|
||||
const squareA = document.querySelector('.square-a');
|
||||
|
||||
@@ -8,11 +8,6 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await page.goto('/src/utils/animation/test/hooks', config);
|
||||
await testHooks(page);
|
||||
});
|
||||
|
||||
test(`should fire hooks using css animations`, async ({ page }) => {
|
||||
await page.goto('/src/utils/animation/test/hooks?ionic:_forceCSSAnimations=true', config);
|
||||
await testHooks(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
const forceCSSAnimations = new URLSearchParams(window.location.search).get('ionic:_forceCSSAnimations');
|
||||
if (forceCSSAnimations) {
|
||||
Element.prototype.animate = null;
|
||||
}
|
||||
|
||||
import { createAnimation } from '../../../../dist/ionic/index.esm.js';
|
||||
|
||||
const squareA = document.querySelector('.square-a');
|
||||
|
||||
@@ -8,14 +8,6 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await page.goto('/src/utils/animation/test/multiple', config);
|
||||
await testMultiple(page);
|
||||
});
|
||||
|
||||
/**
|
||||
* CSS animations will occasionally resolve out of order, so we skip for now
|
||||
*/
|
||||
test.skip(`should resolve grouped animations using css animations`, async ({ page }) => {
|
||||
await page.goto('/src/utils/animation/test/multiple?ionic:_forceCSSAnimations=true', config);
|
||||
await testMultiple(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
const forceCSSAnimations = new URLSearchParams(window.location.search).get('ionic:_forceCSSAnimations');
|
||||
if (forceCSSAnimations) {
|
||||
Element.prototype.animate = null;
|
||||
}
|
||||
|
||||
import { createAnimation } from '../../../../dist/ionic/index.esm.js';
|
||||
|
||||
const squareA = document.querySelector('.square-a');
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
const forceCSSAnimations = new URLSearchParams(window.location.search).get('ionic:_forceCSSAnimations');
|
||||
if (forceCSSAnimations) {
|
||||
Element.prototype.animate = null;
|
||||
}
|
||||
|
||||
import { createAnimation } from '../../../../dist/ionic/index.esm.js';
|
||||
|
||||
createAnimation()
|
||||
|
||||
Reference in New Issue
Block a user