mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat: remove css animations support for ionic animations (#29123)
Issue number: Internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Ionic Framework provides a small utility wrapper around the Web Animations API. Historically not all browsers that Ionic Framework supported, had support for the Web Animations API. To offer backwards compatibility, Ionic Framework provided fallback behaviors for the different wrapped APIs. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - Removes the legacy CSS animations fallback behavior from the Web Animations API animation utility. Maintaining a few no-op behaviors for test environments. - Resolved a few internal type usages that were casting to any - Removed spec tests that were testing the fallback CSS animations behavior and/or already had test coverage from other unit tests. ## Does this introduce a breaking change? - [x] Yes - [ ] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer for more information. --> All modern browsers support the Web Animations API today. If a developer needs to target an older browser that does not support Web Animations, they should either use [a polyfill](https://github.com/web-animations/web-animations-js), or implement the fallback behavior themselves. ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> --------- Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
@@ -60,8 +60,11 @@ 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-mode">Dark Mode</h2>
|
||||
|
||||
|
||||
In previous versions, it was recommended to define the dark palette in the following way:
|
||||
|
||||
```css
|
||||
|
||||
@@ -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,99 +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
|
||||
const rootNode = element.getRootNode !== undefined ? (element.getRootNode() as any) : element;
|
||||
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,5 +1,4 @@
|
||||
import { win } from '../browser';
|
||||
import { raf } from '../helpers';
|
||||
|
||||
import type {
|
||||
Animation,
|
||||
@@ -12,16 +11,7 @@ import type {
|
||||
AnimationLifecycle,
|
||||
AnimationPlayOptions,
|
||||
} from './animation-interface';
|
||||
import {
|
||||
addClassToArray,
|
||||
animationEnd,
|
||||
createKeyframeStylesheet,
|
||||
generateKeyframeName,
|
||||
generateKeyframeRules,
|
||||
processKeyframes,
|
||||
removeStyleProperty,
|
||||
setStyleProperty,
|
||||
} from './animation-utils';
|
||||
import { addClassToArray, setStyleProperty } from './animation-utils';
|
||||
|
||||
// TODO(FW-2832): types
|
||||
|
||||
@@ -64,7 +54,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;
|
||||
|
||||
@@ -83,11 +72,17 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
const supportsAnimationEffect =
|
||||
typeof (AnimationEffect as any) === 'function' ||
|
||||
(win !== undefined && typeof (win as any).AnimationEffect === 'function');
|
||||
/**
|
||||
* This is a feature detection for Web Animations.
|
||||
*
|
||||
* Certain environments such as emulated browser environments for testing,
|
||||
* do not support Web Animations. As a result, we need to check for support
|
||||
* and provide a fallback to test certain functionality related to Web Animations.
|
||||
*/
|
||||
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;
|
||||
@@ -198,21 +193,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -533,8 +513,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
animation.effect = newEffect;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
initializeCSSAnimation();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -643,39 +621,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,14 +645,12 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
}
|
||||
};
|
||||
|
||||
const initializeAnimation = (toggleAnimationName = true) => {
|
||||
const initializeAnimation = () => {
|
||||
beforeAnimation();
|
||||
|
||||
if (_keyframes.length > 0) {
|
||||
if (supportsWebAnimations) {
|
||||
initializeWebAnimation();
|
||||
} else {
|
||||
initializeCSSAnimation(toggleAnimationName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,15 +665,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -751,35 +685,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) => {
|
||||
@@ -789,8 +694,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
|
||||
if (supportsWebAnimations) {
|
||||
updateWebAnimation(step);
|
||||
} else {
|
||||
updateCSSAnimation(toggleAnimationName, step);
|
||||
}
|
||||
|
||||
return ani;
|
||||
@@ -892,11 +795,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
return ani;
|
||||
};
|
||||
|
||||
const onAnimationEndFallback = () => {
|
||||
cssAnimationsTimerFallback = undefined;
|
||||
animationFinish();
|
||||
};
|
||||
|
||||
const clearCSSAnimationsTimeout = () => {
|
||||
if (cssAnimationsTimerFallback) {
|
||||
clearTimeout(cssAnimationsTimerFallback);
|
||||
@@ -906,63 +804,7 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
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');
|
||||
});
|
||||
animationFinish();
|
||||
};
|
||||
|
||||
const playWebAnimations = () => {
|
||||
@@ -979,8 +821,6 @@ export const createAnimation = (animationId?: string): Animation => {
|
||||
if (supportsWebAnimations) {
|
||||
setAnimationStep(0);
|
||||
updateWebAnimation();
|
||||
} else {
|
||||
updateCSSAnimation();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
@@ -53,104 +52,6 @@ describe('Animation Class', () => {
|
||||
animation.play();
|
||||
expect(animation.isRunning()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should be running', () => {
|
||||
const el = document.createElement('div');
|
||||
animation.addElement(el);
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 },
|
||||
]);
|
||||
animation.duration(250);
|
||||
|
||||
animation.play();
|
||||
expect(animation.isRunning()).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not be running after finishing the animation', async () => {
|
||||
const el = document.createElement('div');
|
||||
animation.addElement(el);
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 },
|
||||
]);
|
||||
animation.duration(250);
|
||||
|
||||
await animation.play();
|
||||
|
||||
expect(animation.isRunning()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not be running after calling pause', () => {
|
||||
const el = document.createElement('div');
|
||||
animation.addElement(el);
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 },
|
||||
]);
|
||||
animation.duration(250);
|
||||
|
||||
animation.play();
|
||||
expect(animation.isRunning()).toEqual(true);
|
||||
|
||||
animation.pause();
|
||||
expect(animation.isRunning()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not be running when doing progress steps', () => {
|
||||
const el = document.createElement('div');
|
||||
animation.addElement(el);
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 },
|
||||
]);
|
||||
animation.duration(250);
|
||||
|
||||
animation.play();
|
||||
|
||||
animation.progressStart();
|
||||
|
||||
expect(animation.isRunning()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should be running after calling progressEnd', () => {
|
||||
const el = document.createElement('div');
|
||||
animation.addElement(el);
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 },
|
||||
]);
|
||||
animation.duration(250);
|
||||
|
||||
animation.play();
|
||||
|
||||
animation.progressStart();
|
||||
animation.progressEnd(1, 0);
|
||||
|
||||
expect(animation.isRunning()).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not be running after playing to beginning', async () => {
|
||||
const el = document.createElement('div');
|
||||
animation.addElement(el);
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 },
|
||||
]);
|
||||
animation.duration(250);
|
||||
|
||||
await animation.play();
|
||||
|
||||
animation.progressStart();
|
||||
animation.progressEnd(0, 0);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
animation.onFinish(() => {
|
||||
expect(animation.isRunning()).toEqual(false);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addElement()', () => {
|
||||
@@ -228,18 +129,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