mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Merge branch 'feat-animation' of https://github.com/ionic-team/ionic into feat-animation
merget.
This commit is contained in:
64
core/src/utils/animation-old/animation-interface.ts
Normal file
64
core/src/utils/animation-old/animation-interface.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
export interface AnimationController {
|
||||
create(animationBuilder?: AnimationBuilder, baseEl?: any, opts?: any): Promise<Animation>;
|
||||
}
|
||||
|
||||
export interface Animation {
|
||||
new (): any;
|
||||
parent: Animation | undefined;
|
||||
hasChildren: boolean;
|
||||
addElement(el: Node | Node[] | NodeList): Animation;
|
||||
add(childAnimation: Animation): Animation;
|
||||
duration(milliseconds: number): Animation;
|
||||
easing(name: string): Animation;
|
||||
easingReverse(name: string): Animation;
|
||||
getDuration(opts?: PlayOptions): number;
|
||||
getEasing(): string;
|
||||
from(prop: string, val: any): Animation;
|
||||
to(prop: string, val: any, clearProperyAfterTransition?: boolean): Animation;
|
||||
fromTo(prop: string, fromVal: any, toVal: any, clearProperyAfterTransition?: boolean): Animation;
|
||||
beforeAddClass(className: string): Animation;
|
||||
beforeRemoveClass(className: string): Animation;
|
||||
beforeStyles(styles: { [property: string]: any; }): Animation;
|
||||
beforeClearStyles(propertyNames: string[]): Animation;
|
||||
beforeAddRead(domReadFn: () => void): Animation;
|
||||
beforeAddWrite(domWriteFn: () => void): Animation;
|
||||
afterAddClass(className: string): Animation;
|
||||
afterRemoveClass(className: string): Animation;
|
||||
afterStyles(styles: { [property: string]: any; }): Animation;
|
||||
afterClearStyles(propertyNames: string[]): Animation;
|
||||
play(opts?: PlayOptions): void;
|
||||
playSync(): void;
|
||||
playAsync(opts?: PlayOptions): Promise<Animation>;
|
||||
reverse(shouldReverse?: boolean): Animation;
|
||||
stop(stepValue?: number): void;
|
||||
progressStart(): void;
|
||||
progressStep(stepValue: number): void;
|
||||
progressEnd(shouldComplete: boolean, currentStepValue: number, dur: number): void;
|
||||
onFinish(callback: (animation?: Animation) => void, opts?: {oneTimeCallback?: boolean, clearExistingCallbacks?: boolean}): Animation;
|
||||
destroy(): void;
|
||||
isRoot(): boolean;
|
||||
hasCompleted: boolean;
|
||||
}
|
||||
|
||||
export type AnimationBuilder = (Animation: Animation, baseEl: any, opts?: any) => Promise<Animation>;
|
||||
|
||||
export interface PlayOptions {
|
||||
duration?: number;
|
||||
promise?: boolean;
|
||||
}
|
||||
|
||||
export interface EffectProperty {
|
||||
effectName: string;
|
||||
trans: boolean;
|
||||
wc?: string;
|
||||
to?: EffectState;
|
||||
from?: EffectState;
|
||||
[state: string]: any;
|
||||
}
|
||||
|
||||
export interface EffectState {
|
||||
val: any;
|
||||
num: number;
|
||||
effectUnit: string;
|
||||
}
|
||||
1253
core/src/utils/animation-old/animator.ts
Normal file
1253
core/src/utils/animation-old/animator.ts
Normal file
File diff suppressed because it is too large
Load Diff
10
core/src/utils/animation-old/index.ts
Normal file
10
core/src/utils/animation-old/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Animation, AnimationBuilder } from '../../interface';
|
||||
|
||||
import { Animator } from './animator';
|
||||
|
||||
export function create(animationBuilder?: AnimationBuilder, baseEl?: any, opts?: any): Promise<Animation> {
|
||||
if (animationBuilder) {
|
||||
return animationBuilder(Animator as any, baseEl, opts);
|
||||
}
|
||||
return Promise.resolve(new Animator() as any);
|
||||
}
|
||||
30
core/src/utils/animation-old/transition-end.ts
Normal file
30
core/src/utils/animation-old/transition-end.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
export function transitionEnd(el: HTMLElement | null, callback: (ev?: TransitionEvent) => void) {
|
||||
let unRegTrans: (() => void) | undefined;
|
||||
const opts: any = { passive: true };
|
||||
|
||||
function unregister() {
|
||||
if (unRegTrans) {
|
||||
unRegTrans();
|
||||
}
|
||||
}
|
||||
|
||||
function onTransitionEnd(ev: Event) {
|
||||
if (el === ev.target) {
|
||||
unregister();
|
||||
callback(ev as TransitionEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (el) {
|
||||
el.addEventListener('webkitTransitionEnd', onTransitionEnd, opts);
|
||||
el.addEventListener('transitionend', onTransitionEnd, opts);
|
||||
|
||||
unRegTrans = () => {
|
||||
el.removeEventListener('webkitTransitionEnd', onTransitionEnd, opts);
|
||||
el.removeEventListener('transitionend', onTransitionEnd, opts);
|
||||
};
|
||||
}
|
||||
|
||||
return unregister;
|
||||
}
|
||||
630
core/src/utils/animation/animation.ts
Normal file
630
core/src/utils/animation/animation.ts
Normal file
@@ -0,0 +1,630 @@
|
||||
// TODO: Add validation
|
||||
// TODO: More tests
|
||||
|
||||
export interface Animation {
|
||||
parentAnimation: Animation | undefined;
|
||||
elements: HTMLElement[];
|
||||
childAnimations: Animation[];
|
||||
beforeAddClasses: string[];
|
||||
beforeRemoveClasses: string[];
|
||||
beforeStylesValue: { [property: string]: any };
|
||||
afterAddClasses: string[];
|
||||
afterRemoveClasses: string[];
|
||||
afterStylesValue: { [property: string]: any };
|
||||
|
||||
parent(animation: Animation): Animation;
|
||||
play(): Animation;
|
||||
pause(): Animation;
|
||||
stop(): Animation;
|
||||
playStep(step: number): Animation;
|
||||
destroy(): Animation;
|
||||
keyframes(keyframes: any[]): Animation;
|
||||
addAnimation(animationToADd: Animation | Animation[] | undefined | null): Animation;
|
||||
addTarget(target: string): Animation;
|
||||
addElement(el: Node | Node[] | NodeList | undefined | null): Animation;
|
||||
iterations(iterations: number): Animation;
|
||||
duration(duration: number): Animation;
|
||||
easing(easing: string): Animation;
|
||||
delay(delay: number): Animation;
|
||||
name(name: string): Animation;
|
||||
getKeyframes(): any[];
|
||||
getDelay(): number | undefined;
|
||||
getIterations(): number | undefined;
|
||||
getEasing(): string | undefined;
|
||||
getDuration(): number | undefined;
|
||||
afterClearStyles(propertyNames: string[]): Animation;
|
||||
afterStyles(styles: { [property: string]: any }): Animation;
|
||||
afterRemoveClass(className: string | string[] | undefined): Animation;
|
||||
afterAddClass(className: string | string[] | undefined): Animation;
|
||||
beforeClearStyles(propertyNames: string[]): Animation;
|
||||
beforeStyles(styles: { [property: string]: any }): Animation;
|
||||
beforeRemoveClass(className: string | string[] | undefined): Animation;
|
||||
beforeAddClass(className: string | string[] | undefined): Animation;
|
||||
}
|
||||
|
||||
const animationEnd = (el: HTMLElement | null, callback: (ev?: TransitionEvent) => void) => {
|
||||
let unRegTrans: (() => void) | undefined;
|
||||
const opts: any = { passive: true };
|
||||
|
||||
function unregister() {
|
||||
if (unRegTrans) {
|
||||
unRegTrans();
|
||||
}
|
||||
}
|
||||
|
||||
function 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;
|
||||
};
|
||||
|
||||
const supportsWebAnimations = (): boolean => {
|
||||
return !!(window as any).Animation;
|
||||
};
|
||||
|
||||
const generateKeyframeString = (name: string | undefined, keyframes: any[] = []): string => {
|
||||
if (name === undefined) { console.warn('A name is required to generate keyframes'); }
|
||||
|
||||
const keyframeString = [`@keyframes ${name} {`];
|
||||
|
||||
keyframes.forEach(keyframe => {
|
||||
const offset = keyframe.offset;
|
||||
delete keyframe.offset;
|
||||
|
||||
const frameString = [];
|
||||
for (const property in keyframe) {
|
||||
if (keyframe.hasOwnProperty(property)) {
|
||||
frameString.push(`${property}: ${keyframe[property]};`);
|
||||
}
|
||||
}
|
||||
|
||||
keyframeString.push(`${offset * 100}% { ${frameString.join(' ')} }`);
|
||||
});
|
||||
|
||||
return keyframeString.join(' ');
|
||||
};
|
||||
|
||||
const createKeyframeStylesheet = (keyframeString: string): HTMLElement => {
|
||||
const stylesheet = document.createElement('style');
|
||||
stylesheet.appendChild(document.createTextNode(keyframeString));
|
||||
document.querySelector('head')!.appendChild(stylesheet);
|
||||
|
||||
return stylesheet;
|
||||
};
|
||||
|
||||
const addClassToArray = (classes: string[] = [], className: string | string[] | undefined): string[] => {
|
||||
if (className !== undefined) {
|
||||
const classNameToAppend = (Array.isArray(className)) ? className : [className];
|
||||
|
||||
return [...classes, ...classNameToAppend];
|
||||
}
|
||||
|
||||
return classes;
|
||||
};
|
||||
|
||||
export const createAnimation = (animationNameValue: string | undefined): Animation => {
|
||||
let elements: HTMLElement[] = [];
|
||||
let childAnimations: Animation[] = [];
|
||||
let _name: string | undefined;
|
||||
let _delay: number | undefined;
|
||||
let _duration: number | undefined;
|
||||
let _easing: string | undefined;
|
||||
let _iterations: number | undefined;
|
||||
|
||||
let _keyframes: any[] = [];
|
||||
let _keyframeString = '';
|
||||
|
||||
let initialized = false;
|
||||
|
||||
let stylesheet: HTMLElement | undefined;
|
||||
|
||||
let parentAnimation: Animation | undefined;
|
||||
|
||||
let beforeAddClasses: string[] = [];
|
||||
let beforeRemoveClasses: string[] = [];
|
||||
let beforeStylesValue: { [property: string]: any } = {};
|
||||
|
||||
let afterAddClasses: string[] = [];
|
||||
let afterRemoveClasses: string[] = [];
|
||||
let afterStylesValue: { [property: string]: any } = {};
|
||||
|
||||
let webAnimations: any[] = [];
|
||||
|
||||
/**
|
||||
* Destroy this animation and all child animations.
|
||||
*/
|
||||
const destroy = (): Animation => {
|
||||
childAnimations.forEach(childAnimation => {
|
||||
childAnimation.destroy();
|
||||
});
|
||||
|
||||
cleanUp();
|
||||
|
||||
elements = [];
|
||||
childAnimations = [];
|
||||
|
||||
initialized = false;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const cleanUp = () => {
|
||||
cleanUpElements();
|
||||
cleanUpStyleSheets();
|
||||
};
|
||||
|
||||
const cleanUpElements = () => {
|
||||
if (supportsWebAnimations()) {
|
||||
webAnimations.forEach(animation => {
|
||||
animation.cancel();
|
||||
});
|
||||
|
||||
webAnimations = [];
|
||||
} else {
|
||||
elements.forEach(element => {
|
||||
element.style.removeProperty('animation-name');
|
||||
element.style.removeProperty('animation-duration');
|
||||
element.style.removeProperty('animation-timing-function');
|
||||
element.style.removeProperty('animation-iteration-count');
|
||||
element.style.removeProperty('animation-delay');
|
||||
element.style.removeProperty('animation-play-state');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const cleanUpStyleSheets = () => {
|
||||
if (stylesheet) {
|
||||
stylesheet.parentNode!.removeChild(stylesheet);
|
||||
stylesheet = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add CSS class to this animation's elements
|
||||
* before the animation begins.
|
||||
*/
|
||||
const beforeAddClass = (className: string | string[] | undefined): Animation => {
|
||||
beforeAddClasses = addClassToArray(beforeAddClasses, className);
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove CSS class from this animation's elements
|
||||
* before the animation begins.
|
||||
*/
|
||||
const beforeRemoveClass = (className: string | string[] | undefined): Animation => {
|
||||
beforeRemoveClasses = addClassToArray(beforeRemoveClasses, className);
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set CSS inline styles to this animation's elements
|
||||
* before the animation begins.
|
||||
*/
|
||||
const beforeStyles = (styles: { [property: string]: any } = {}): Animation => {
|
||||
beforeStylesValue = styles;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear CSS inline styles from this animation's elements
|
||||
* before the animation begins.
|
||||
*/
|
||||
const beforeClearStyles = (propertyNames: string[] = []): Animation => {
|
||||
for (const property of propertyNames) {
|
||||
beforeStylesValue[property] = '';
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add CSS class to this animation's elements
|
||||
* after the animation ends.
|
||||
*/
|
||||
const afterAddClass = (className: string | string[] | undefined): Animation => {
|
||||
afterAddClasses = addClassToArray(afterAddClasses, className);
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove CSS class from this animation's elements
|
||||
* after the animation ends.
|
||||
*/
|
||||
const afterRemoveClass = (className: string | string[] | undefined): Animation => {
|
||||
afterRemoveClasses = addClassToArray(afterRemoveClasses, className);
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set CSS inline styles to this animation's elements
|
||||
* after the animation ends.
|
||||
*/
|
||||
const afterStyles = (styles: { [property: string]: any } = {}): Animation => {
|
||||
afterStylesValue = styles;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear CSS inline styles from this animation's elements
|
||||
* after the animation ends.
|
||||
*/
|
||||
const afterClearStyles = (propertyNames: string[] = []): Animation => {
|
||||
for (const property of propertyNames) {
|
||||
afterStylesValue[property] = '';
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const getEasing = (): string | undefined => {
|
||||
if (_easing !== undefined) { return _easing; }
|
||||
if (parentAnimation) { return parentAnimation.getEasing(); }
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getDuration = (): number | undefined => {
|
||||
if (_duration !== undefined) { return _duration; }
|
||||
if (parentAnimation) { return parentAnimation.getDuration(); }
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getIterations = (): number | undefined => {
|
||||
if (_iterations !== undefined) { return _iterations; }
|
||||
if (parentAnimation) { return parentAnimation.getIterations(); }
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getDelay = (): number | undefined => {
|
||||
if (_delay !== undefined) { return _delay; }
|
||||
if (parentAnimation) { return parentAnimation.getDelay(); }
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getKeyframes = (): any[] => {
|
||||
return _keyframes;
|
||||
};
|
||||
|
||||
const name = (animationName: string): Animation => {
|
||||
_name = animationName;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const delay = (animationDelay: number): Animation => {
|
||||
_delay = animationDelay;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const easing = (animationEasing: string): Animation => {
|
||||
_easing = animationEasing;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const duration = (animationDuration: number): Animation => {
|
||||
_duration = animationDuration;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const iterations = (animationIterations: number): Animation => {
|
||||
_iterations = animationIterations;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const parent = (animation: Animation): Animation => {
|
||||
parentAnimation = animation;
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const addElement = (el: Node | Node[] | NodeList | undefined | null): Animation => {
|
||||
if (el != null) {
|
||||
const nodeList = el as NodeList;
|
||||
if (nodeList.length >= 0) {
|
||||
for (let i = 0; i < nodeList.length; i++) {
|
||||
elements.push((el as any)[i]);
|
||||
}
|
||||
} else {
|
||||
elements.push(el as any);
|
||||
}
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const addTarget = (target: string): Animation => {
|
||||
const els = document.querySelectorAll(target);
|
||||
|
||||
return addElement(els);
|
||||
};
|
||||
|
||||
const addAnimation = (animationToAdd: Animation | Animation[] | undefined | null): Animation => {
|
||||
if (animationToAdd != null) {
|
||||
const parentAnim = generatePublicAPI();
|
||||
const animationsToAdd = animationToAdd as Animation[];
|
||||
if (animationsToAdd.length >= 0) {
|
||||
for (const animation of animationsToAdd) {
|
||||
animation.parent(parentAnim);
|
||||
childAnimations.push(animation);
|
||||
}
|
||||
} else {
|
||||
(animationToAdd as Animation).parent(parentAnim);
|
||||
childAnimations.push(animationToAdd as Animation);
|
||||
}
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const keyframes = (keyframeValues: any[]) => {
|
||||
_keyframes = keyframeValues;
|
||||
|
||||
if (!supportsWebAnimations()) {
|
||||
_keyframeString = generateKeyframeString(_name, keyframeValues);
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const beforeAnimation = () => {
|
||||
const addClasses = beforeAddClasses;
|
||||
const removeClasses = beforeRemoveClasses;
|
||||
const styles = beforeStylesValue;
|
||||
|
||||
elements.forEach((el: HTMLElement) => {
|
||||
const elementClassList = el.classList;
|
||||
|
||||
elementClassList.add(...addClasses);
|
||||
elementClassList.remove(...removeClasses);
|
||||
|
||||
for (const property in styles) {
|
||||
if (styles.hasOwnProperty(property)) {
|
||||
el.style.setProperty(property, styles[property]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const afterAnimation = () => {
|
||||
const addClasses = afterAddClasses;
|
||||
const removeClasses = afterRemoveClasses;
|
||||
const styles = afterStylesValue;
|
||||
|
||||
elements.forEach((el: HTMLElement) => {
|
||||
const elementClassList = el.classList;
|
||||
|
||||
elementClassList.add(...addClasses);
|
||||
elementClassList.remove(...removeClasses);
|
||||
|
||||
for (const property in styles) {
|
||||
if (styles.hasOwnProperty(property)) {
|
||||
el.style.setProperty(property, styles[property]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cleanUpElements();
|
||||
};
|
||||
|
||||
const initializeAnimation = () => {
|
||||
beforeAnimation();
|
||||
|
||||
if (supportsWebAnimations()) {
|
||||
elements.forEach((element, i) => {
|
||||
const animation = element.animate(getKeyframes(), {
|
||||
delay: getDelay(),
|
||||
duration: getDuration(),
|
||||
easing: getEasing(),
|
||||
iterations: getIterations()
|
||||
});
|
||||
|
||||
if (i === 0) {
|
||||
animation.onfinish = () => {
|
||||
afterAnimation();
|
||||
};
|
||||
}
|
||||
|
||||
animation.pause();
|
||||
|
||||
webAnimations.push(animation);
|
||||
});
|
||||
|
||||
} else {
|
||||
if (!stylesheet) {
|
||||
stylesheet = createKeyframeStylesheet(_keyframeString);
|
||||
}
|
||||
|
||||
const animationDuration = getDuration();
|
||||
const animationEasing = getEasing();
|
||||
const animationIterationCount = getIterations();
|
||||
const animationDelay = getDelay();
|
||||
|
||||
elements.forEach(element => {
|
||||
if (_name !== undefined) {
|
||||
(element as HTMLElement).style.animationName = _name;
|
||||
}
|
||||
|
||||
if (animationDuration !== undefined) {
|
||||
(element as HTMLElement).style.animationDuration = `${animationDuration}ms`;
|
||||
}
|
||||
|
||||
if (animationEasing !== undefined) {
|
||||
(element as HTMLElement).style.animationTimingFunction = animationEasing;
|
||||
}
|
||||
|
||||
if (animationIterationCount !== undefined) {
|
||||
(element as HTMLElement).style.animationIterationCount = (animationIterationCount === Infinity) ? 'infinite' : animationIterationCount.toString();
|
||||
}
|
||||
|
||||
if (animationDelay !== undefined) {
|
||||
(element as HTMLElement).style.animationDelay = `${animationDelay}ms`;
|
||||
}
|
||||
});
|
||||
|
||||
animationEnd(elements[0], () => {
|
||||
afterAnimation();
|
||||
});
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
};
|
||||
|
||||
const playStep = (step: number): Animation => {
|
||||
childAnimations.forEach(animation => {
|
||||
animation.playStep(step);
|
||||
});
|
||||
|
||||
if (!initialized) {
|
||||
initializeAnimation();
|
||||
}
|
||||
|
||||
pause();
|
||||
|
||||
if (getDuration() !== undefined) {
|
||||
if (supportsWebAnimations()) {
|
||||
webAnimations.forEach(animation => {
|
||||
animation.currentTime = animation.effect.getComputedTiming().delay + (getDuration()! * step);
|
||||
});
|
||||
} else {
|
||||
const animationDuration = `-${getDuration()! * step}ms`;
|
||||
|
||||
elements.forEach(element => {
|
||||
(element as HTMLElement).style.animationDelay = animationDuration;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const pause = (): Animation => {
|
||||
childAnimations.forEach(animation => {
|
||||
animation.pause();
|
||||
});
|
||||
|
||||
if (initialized) {
|
||||
if (supportsWebAnimations()) {
|
||||
webAnimations.forEach(animation => {
|
||||
animation.pause();
|
||||
});
|
||||
} else {
|
||||
elements.forEach(element => {
|
||||
(element as HTMLElement).style.animationPlayState = 'paused';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const play = (): Animation => {
|
||||
childAnimations.forEach(animation => {
|
||||
animation.play();
|
||||
});
|
||||
|
||||
if (!initialized) {
|
||||
initializeAnimation();
|
||||
}
|
||||
|
||||
if (supportsWebAnimations()) {
|
||||
webAnimations.forEach(animation => {
|
||||
animation.play();
|
||||
});
|
||||
} else {
|
||||
elements.forEach(element => {
|
||||
(element as HTMLElement).style.animationPlayState = 'running';
|
||||
});
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const stop = (): Animation => {
|
||||
childAnimations.forEach(animation => {
|
||||
animation.stop();
|
||||
});
|
||||
|
||||
if (initialized) {
|
||||
cleanUp();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
|
||||
const generatePublicAPI = (): Animation => {
|
||||
return {
|
||||
parentAnimation,
|
||||
elements,
|
||||
childAnimations,
|
||||
beforeAddClasses,
|
||||
beforeRemoveClasses,
|
||||
beforeStylesValue,
|
||||
afterAddClasses,
|
||||
afterRemoveClasses,
|
||||
afterStylesValue,
|
||||
parent,
|
||||
play,
|
||||
pause,
|
||||
stop,
|
||||
playStep,
|
||||
destroy,
|
||||
keyframes,
|
||||
addAnimation,
|
||||
addTarget,
|
||||
addElement,
|
||||
iterations,
|
||||
duration,
|
||||
easing,
|
||||
delay,
|
||||
name,
|
||||
getKeyframes,
|
||||
getDelay,
|
||||
getIterations,
|
||||
getEasing,
|
||||
getDuration,
|
||||
afterClearStyles,
|
||||
afterStyles,
|
||||
afterRemoveClass,
|
||||
afterAddClass,
|
||||
beforeClearStyles,
|
||||
beforeStyles,
|
||||
beforeRemoveClass,
|
||||
beforeAddClass,
|
||||
};
|
||||
};
|
||||
|
||||
if (animationNameValue !== undefined) {
|
||||
name(animationNameValue);
|
||||
}
|
||||
|
||||
return generatePublicAPI();
|
||||
};
|
||||
361
core/src/utils/animation/test/animation.spec.ts
Normal file
361
core/src/utils/animation/test/animation.spec.ts
Normal file
@@ -0,0 +1,361 @@
|
||||
import { createAnimation } from '../animation';
|
||||
|
||||
describe('Animation Class', () => {
|
||||
|
||||
describe('addElement()', () => {
|
||||
let animation;
|
||||
beforeEach(() => {
|
||||
animation = createAnimation();
|
||||
});
|
||||
|
||||
it('should add 1 element', () => {
|
||||
const el = document.createElement('p');
|
||||
animation.addElement(el);
|
||||
|
||||
expect(animation.elements.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should add multiple elements', () => {
|
||||
const els = [
|
||||
document.createElement('p'),
|
||||
document.createElement('p'),
|
||||
document.createElement('p')
|
||||
];
|
||||
|
||||
animation.addElement(els);
|
||||
|
||||
expect(animation.elements.length).toEqual(els.length);
|
||||
});
|
||||
|
||||
it('should not error when trying to add null or undefined', () => {
|
||||
const el = document.createElement('p');
|
||||
animation.addElement(el);
|
||||
|
||||
animation.addElement(null);
|
||||
animation.addElement(undefined);
|
||||
|
||||
expect(animation.elements.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addTarget()', () => {
|
||||
let animation;
|
||||
beforeEach(() => {
|
||||
animation = createAnimation();
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('should add a target', () => {
|
||||
document.body.appendChild(document.createElement('p'));
|
||||
|
||||
animation.addTarget('p');
|
||||
expect(animation.elements.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should add multiple targets of the same type', () => {
|
||||
document.body.appendChild(document.createElement('p'));
|
||||
document.body.appendChild(document.createElement('p'));
|
||||
|
||||
animation.addTarget('p');
|
||||
expect(animation.elements.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should add multiple targets of different types', () => {
|
||||
document.body.appendChild(document.createElement('p'));
|
||||
document.body.appendChild(document.createElement('p'));
|
||||
document.body.appendChild(document.createElement('span'));
|
||||
|
||||
animation.addTarget('p, span');
|
||||
expect(animation.elements.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should not error when trying to add null or undefined', () => {
|
||||
animation.addTarget('p');
|
||||
expect(animation.elements.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addAnimation()', () => {
|
||||
let animation;
|
||||
beforeEach(() => {
|
||||
animation = createAnimation();
|
||||
});
|
||||
|
||||
it('should add 1 animation', () => {
|
||||
const newAnimation = createAnimation();
|
||||
animation.addAnimation(newAnimation);
|
||||
|
||||
expect(animation.childAnimations.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should add multiple animations', () => {
|
||||
animation.addAnimation([createAnimation(), createAnimation(), createAnimation()]);
|
||||
|
||||
expect(animation.childAnimations.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should not error when trying to add null or undefined', () => {
|
||||
animation.addAnimation(null);
|
||||
animation.addAnimation(undefined);
|
||||
|
||||
expect(animation.childAnimations.length).toEqual(0);
|
||||
})
|
||||
});
|
||||
|
||||
describe('keyframes()', () => {
|
||||
let animation;
|
||||
beforeEach(() => {
|
||||
animation = createAnimation('my-animation');
|
||||
});
|
||||
|
||||
it('should generate a keyframe', () => {
|
||||
animation.keyframes([
|
||||
{ transform: 'scale(1)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0.5)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(0)', opacity: 0, offset: 1 }
|
||||
]);
|
||||
|
||||
expect(animation.getKeyframes().length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Before and After Animation Methods', () => {
|
||||
let animation;
|
||||
beforeEach(() => {
|
||||
animation = createAnimation();
|
||||
});
|
||||
|
||||
it('should register all "before" styles', () => {
|
||||
animation = animation.beforeStyles({ 'background': 'red', 'opacity': 1 });
|
||||
expect(Object.keys(animation.beforeStylesValue).length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should register all "before" classes given arrays', () => {
|
||||
const classesToAdd = ['my-class', 'hello-world'];
|
||||
const classesToRemove = ['ionic-framework'];
|
||||
|
||||
animation = animation.beforeAddClass(classesToAdd);
|
||||
animation = animation.beforeRemoveClass(classesToRemove);
|
||||
|
||||
expect(animation.beforeAddClasses.length).toEqual(classesToAdd.length);
|
||||
expect(animation.beforeRemoveClasses.length).toEqual(classesToRemove.length);
|
||||
});
|
||||
|
||||
it('should register all "before" classes given strings', () => {
|
||||
const classesToAdd = 'my-class';
|
||||
const classesToRemove = 'ionic-framework';
|
||||
|
||||
animation = animation.beforeAddClass(classesToAdd);
|
||||
animation = animation.beforeRemoveClass(classesToRemove);
|
||||
|
||||
expect(animation.beforeAddClasses.length).toEqual(1);
|
||||
expect(animation.beforeRemoveClasses.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not register "before" classes given undefined', () => {
|
||||
animation = animation.beforeAddClass(undefined);
|
||||
animation = animation.beforeRemoveClass(undefined);
|
||||
|
||||
expect(animation.beforeAddClasses.length).toEqual(0);
|
||||
expect(animation.beforeRemoveClasses.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should apply all "before" styles', () => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('hello', 'world');
|
||||
el.style.setProperty('opacity', "0.5");
|
||||
|
||||
animation
|
||||
.addElement(el)
|
||||
.beforeAddClass(['ionic', 'framework'])
|
||||
.beforeStyles({ 'background': 'blue' })
|
||||
.beforeClearStyles(['opacity'])
|
||||
.beforeRemoveClass('hello');
|
||||
|
||||
expect(el.style.getPropertyValue('opacity')).toEqual("0.5");
|
||||
expect(el.classList.contains('hello')).toEqual(true);
|
||||
expect(el.classList.contains('world')).toEqual(true);
|
||||
|
||||
animation.play();
|
||||
|
||||
expect(el.style.getPropertyValue('opacity')).toEqual("");
|
||||
expect(el.style.getPropertyValue('background')).toEqual('blue');
|
||||
expect(el.classList.contains('hello')).toEqual(false);
|
||||
expect(el.classList.contains('world')).toEqual(true);
|
||||
expect(el.classList.contains('ionic')).toEqual(true);
|
||||
expect(el.classList.contains('framework')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should register all "after" styles', () => {
|
||||
animation = animation.afterStyles({ 'background': 'red', 'opacity': 1 });
|
||||
expect(Object.keys(animation.afterStylesValue).length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should register all "after" classes given arrays', () => {
|
||||
const classesToAdd = ['my-class', 'hello-world'];
|
||||
const classesToRemove = ['ionic-framework'];
|
||||
|
||||
animation = animation.afterAddClass(classesToAdd);
|
||||
animation = animation.afterRemoveClass(classesToRemove);
|
||||
|
||||
expect(animation.afterAddClasses.length).toEqual(classesToAdd.length);
|
||||
expect(animation.afterRemoveClasses.length).toEqual(classesToRemove.length);
|
||||
});
|
||||
|
||||
it('should register all "after" classes given strings', () => {
|
||||
const classesToAdd = 'my-class';
|
||||
const classesToRemove = 'ionic-framework';
|
||||
|
||||
animation = animation.afterAddClass(classesToAdd);
|
||||
animation = animation.afterRemoveClass(classesToRemove);
|
||||
|
||||
expect(animation.afterAddClasses.length).toEqual(1);
|
||||
expect(animation.afterRemoveClasses.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not register "after" classes given undefined', () => {
|
||||
animation = animation.afterAddClass(undefined);
|
||||
animation = animation.afterRemoveClass(undefined);
|
||||
|
||||
expect(animation.afterAddClasses.length).toEqual(0);
|
||||
expect(animation.afterRemoveClasses.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should apply all "after" styles', async () => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('hello', 'world');
|
||||
el.style.setProperty('opacity', "0.5");
|
||||
|
||||
animation
|
||||
.name('my-animation')
|
||||
.addElement(el)
|
||||
.duration(500)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0.5) rotate(-45deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 1 }
|
||||
])
|
||||
.afterAddClass(['ionic', 'framework'])
|
||||
.afterStyles({ 'background': 'blue' })
|
||||
.afterClearStyles(['opacity'])
|
||||
.afterRemoveClass('hello');
|
||||
|
||||
expect(el.style.getPropertyValue('opacity')).toEqual("0.5");
|
||||
expect(el.classList.contains('hello')).toEqual(true);
|
||||
expect(el.classList.contains('world')).toEqual(true);
|
||||
|
||||
animation.play();
|
||||
|
||||
/**
|
||||
* Animations don't run in spec tests
|
||||
* so we have to fake the end of the animation
|
||||
*/
|
||||
const ev = new CustomEvent('animationend');
|
||||
el.dispatchEvent(ev);
|
||||
|
||||
expect(el.style.getPropertyValue('opacity')).toEqual("");
|
||||
expect(el.style.getPropertyValue('background')).toEqual('blue');
|
||||
expect(el.classList.contains('hello')).toEqual(false);
|
||||
expect(el.classList.contains('world')).toEqual(true);
|
||||
expect(el.classList.contains('ionic')).toEqual(true);
|
||||
expect(el.classList.contains('framework')).toEqual(true);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('Animation Config Methods', () => {
|
||||
let animation;
|
||||
beforeEach(() => {
|
||||
animation = createAnimation();
|
||||
});
|
||||
|
||||
it('should get undefined when easing not set', () => {
|
||||
expect(animation.getEasing()).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should get parent easing when child easing is not set', () => {
|
||||
const childAnimation = createAnimation();
|
||||
animation.addAnimation(childAnimation);
|
||||
childAnimation.easing('linear');
|
||||
|
||||
expect(childAnimation.getEasing()).toEqual('linear');
|
||||
});
|
||||
|
||||
it('should get prefer child easing over parent easing', () => {
|
||||
const childAnimation = createAnimation();
|
||||
childAnimation.easing('linear');
|
||||
|
||||
animation.addAnimation(childAnimation);
|
||||
animation.easing('ease-in-out');
|
||||
|
||||
expect(childAnimation.getEasing()).toEqual('linear');
|
||||
});
|
||||
|
||||
it('should get undefined when duration not set', () => {
|
||||
expect(animation.getDuration()).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should get parent duration when child duration is not set', () => {
|
||||
const childAnimation = createAnimation();
|
||||
animation.addAnimation(childAnimation);
|
||||
childAnimation.duration(500);
|
||||
|
||||
expect(childAnimation.getDuration()).toEqual(500);
|
||||
});
|
||||
|
||||
it('should get prefer child duration over parent duration', () => {
|
||||
const childAnimation = createAnimation();
|
||||
childAnimation.duration(500);
|
||||
|
||||
animation.addAnimation(childAnimation);
|
||||
animation.duration(1000);
|
||||
|
||||
expect(childAnimation.getDuration()).toEqual(500);
|
||||
});
|
||||
|
||||
it('should get undefined when delay not set', () => {
|
||||
expect(animation.getDelay()).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should get parent delay when child delay is not set', () => {
|
||||
const childAnimation = createAnimation();
|
||||
animation.addAnimation(childAnimation);
|
||||
childAnimation.delay(500);
|
||||
|
||||
expect(childAnimation.getDelay()).toEqual(500);
|
||||
});
|
||||
|
||||
it('should get prefer child delay over parent delay', () => {
|
||||
const childAnimation = createAnimation();
|
||||
childAnimation.delay(500);
|
||||
|
||||
animation.addAnimation(childAnimation);
|
||||
animation.delay(1000);
|
||||
|
||||
expect(childAnimation.getDelay()).toEqual(500);
|
||||
});
|
||||
|
||||
it('should get undefined when iterations not set', () => {
|
||||
expect(animation.getIterations()).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should get parent iterations when child iterations is not set', () => {
|
||||
const childAnimation = createAnimation();
|
||||
animation.addAnimation(childAnimation);
|
||||
childAnimation.iterations(2);
|
||||
|
||||
expect(childAnimation.getIterations()).toEqual(2);
|
||||
});
|
||||
|
||||
it('should get prefer child iterations over parent iterations', () => {
|
||||
const childAnimation = createAnimation();
|
||||
childAnimation.iterations(2);
|
||||
|
||||
animation.addAnimation(childAnimation);
|
||||
animation.iterations(1);
|
||||
|
||||
expect(childAnimation.getIterations()).toEqual(2);
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
171
core/src/utils/animation/test/basic/index.html
Normal file
171
core/src/utils/animation/test/basic/index.html
Normal file
@@ -0,0 +1,171 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Animation - Basic</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
import { createAnimation } from '../../../../dist/collection/utils/animation/animation.js';
|
||||
import { createGesture } from '../../../../dist/ionic/index-a098653b.js';
|
||||
|
||||
function clamp(val) {
|
||||
if (val > 1) {
|
||||
return 1;
|
||||
} else if (val < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
const squareA = document.querySelectorAll('.square-a');
|
||||
const squareB = document.querySelectorAll('.square-b');
|
||||
const squareC = document.querySelectorAll('.square-c');
|
||||
|
||||
const track = document.querySelector('.track');
|
||||
const cursor = document.querySelector('.track .cursor');
|
||||
|
||||
const rootAnimation = createAnimation();
|
||||
const animationA = createAnimation('animation-a');
|
||||
const animationB = createAnimation('animation-b');
|
||||
const animationC = createAnimation('animation-c');
|
||||
|
||||
const gesture = createGesture({
|
||||
el: track,
|
||||
gestureName: 'drag',
|
||||
gesturePriority: 100,
|
||||
treshold: 5,
|
||||
onStart: () => {},
|
||||
onMove: (ev) => {
|
||||
const start = ev.startX;
|
||||
const width = track.clientWidth;
|
||||
|
||||
const lower = start;
|
||||
const upper = width - start;
|
||||
|
||||
const current = clamp((ev.currentX - lower) / upper);
|
||||
|
||||
rootAnimation.playStep(current);
|
||||
cursor.style.transform = `translateX(${ev.currentX - 10}px)`;
|
||||
},
|
||||
onEnd: (ev) => {
|
||||
rootAnimation.playStep(0);
|
||||
cursor.style.transform = `translateX(${0}px)`;
|
||||
}
|
||||
});
|
||||
|
||||
gesture.setDisabled(false);
|
||||
|
||||
animationA
|
||||
.addElement(squareA)
|
||||
.duration(2000)
|
||||
.delay(5000)
|
||||
.easing('linear')
|
||||
.iterations(Infinity)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(1.5) rotate(45deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 1 }
|
||||
]);
|
||||
|
||||
animationB
|
||||
.addElement(squareB)
|
||||
.duration(500)
|
||||
.delay(2000)
|
||||
.easing('ease-in-out')
|
||||
.iterations(Infinity)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0.5) rotate(-45deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 1 }
|
||||
]);
|
||||
|
||||
animationC
|
||||
.addElement(squareC)
|
||||
.duration(2000)
|
||||
.delay(500)
|
||||
.easing('ease-in-out')
|
||||
.iterations(Infinity)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) skew(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(1.5) skew(15deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) skew(0deg)', opacity: 1, offset: 1 }
|
||||
]);
|
||||
|
||||
rootAnimation.addAnimation([animationA, animationB, animationC]);
|
||||
//rootAnimation.play();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.square {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: rgba(0, 0, 255, 0.5);
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
margin-left: 25px;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.track {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: rgba(0, 255, 0, 0.5);
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
user-select: none;
|
||||
cursor: ew-resize;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.track .cursor {
|
||||
pointer-events: none;
|
||||
width: 10px;
|
||||
height: 100px;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Animations</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<div class="ion-padding">
|
||||
<div class="track">
|
||||
Drag along the track to animate the elements
|
||||
<div class="cursor"></div>
|
||||
</div>
|
||||
<div class="square square-a">
|
||||
Hello
|
||||
</div>
|
||||
|
||||
<div class="square square-b">
|
||||
Hello
|
||||
</div>
|
||||
|
||||
<div class="square square-c">
|
||||
Hello
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
110
core/src/utils/animation/test/grid/index.html
Normal file
110
core/src/utils/animation/test/grid/index.html
Normal file
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Animation - Grid</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
import { createAnimation } from '../../../../dist/collection/utils/animation/animation.js';
|
||||
|
||||
const grid = document.querySelector('.grid-container');
|
||||
|
||||
let dotsString = '';
|
||||
for (let i = 0; i < 360; i++) {
|
||||
dotsString += '<div class="dot"></div>';
|
||||
}
|
||||
grid.innerHTML = dotsString;
|
||||
|
||||
const dots = document.querySelectorAll('.dot');
|
||||
const rootAnimation = createAnimation();
|
||||
|
||||
rootAnimation
|
||||
.duration(500)
|
||||
.easing('ease-in-out')
|
||||
.iterations(Infinity);
|
||||
|
||||
dots.forEach((dot, i) => {
|
||||
const animation = createAnimation(`dot-${i}`);
|
||||
animation
|
||||
.addElement(dot)
|
||||
.delay(i * 10)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1)', offset: 0 },
|
||||
{ transform: 'scale(2)', offset: 0.5 },
|
||||
{ transform: 'scale(1)', offset: 1 }
|
||||
]);
|
||||
|
||||
rootAnimation.addAnimation(animation);
|
||||
});
|
||||
|
||||
document.querySelector('.pause').addEventListener('click', () => {
|
||||
rootAnimation.pause();
|
||||
});
|
||||
|
||||
document.querySelector('.play').addEventListener('click', () => {
|
||||
rootAnimation.play();
|
||||
console.log('polkay',rootAnimation)
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.grid-container {
|
||||
width: 330px;
|
||||
height: 470px;
|
||||
background: rgba(0, 0, 255, 0.5);
|
||||
}
|
||||
|
||||
.dot {
|
||||
--width: 8px;
|
||||
width: var(--width);
|
||||
height: var(--width);
|
||||
border-radius: var(--width);
|
||||
background: rgba(0, 255, 0, 1);
|
||||
float: left;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.dot:nth-of-type(2n) {
|
||||
background: rgba(255, 0, 0, 1);
|
||||
}
|
||||
|
||||
.dot:nth-of-type(3n) {
|
||||
background: rgba(0, 0, 255, 1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Animations</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button class="pause">Pause</ion-button>
|
||||
<ion-button class="play">Play</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<div class="ion-padding">
|
||||
<div class="grid-container">
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
145
core/src/utils/animation/test/multiple/index.html
Normal file
145
core/src/utils/animation/test/multiple/index.html
Normal file
@@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Animation - Basic</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
import { createAnimation } from '../../../../dist/collection/utils/animation/animation.js';
|
||||
|
||||
const squareA = document.querySelectorAll('.square-a');
|
||||
const squareB = document.querySelectorAll('.square-b');
|
||||
const squareC = document.querySelectorAll('.square-c');
|
||||
|
||||
const rootAnimation = createAnimation();
|
||||
const animationA = createAnimation('animation-a');
|
||||
const animationB = createAnimation('animation-b');
|
||||
const animationC = createAnimation('animation-c');
|
||||
|
||||
animationA
|
||||
.addElement(squareA)
|
||||
.duration(2000)
|
||||
.delay(5000)
|
||||
.easing('linear')
|
||||
.iterations(1)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(1.5) rotate(45deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 1 }
|
||||
])
|
||||
.beforeStyles({
|
||||
'background': 'rgba(0, 0, 255, 0.5'
|
||||
})
|
||||
.afterStyles({
|
||||
'background': 'rgba(0, 255, 0, 0.5)'
|
||||
});
|
||||
|
||||
animationB
|
||||
.addElement(squareB)
|
||||
.duration(500)
|
||||
.delay(2000)
|
||||
.easing('ease-in-out')
|
||||
.iterations(1)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(0.5) rotate(-45deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 1 }
|
||||
])
|
||||
.beforeStyles({
|
||||
'background': 'rgba(0, 0, 255, 0.5'
|
||||
})
|
||||
.afterStyles({
|
||||
'background': 'rgba(0, 255, 0, 0.5)'
|
||||
});
|
||||
|
||||
animationC
|
||||
.addElement(squareC)
|
||||
.duration(2000)
|
||||
.delay(500)
|
||||
.easing('ease-in-out')
|
||||
.iterations(1)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) skew(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(1.5) skew(15deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) skew(0deg)', opacity: 1, offset: 1 }
|
||||
])
|
||||
.beforeStyles({
|
||||
'background': 'rgba(0, 0, 255, 0.5'
|
||||
})
|
||||
.afterStyles({
|
||||
'background': 'rgba(0, 255, 0, 0.5)'
|
||||
});
|
||||
|
||||
rootAnimation.addAnimation([animationA, animationB, animationC]);
|
||||
|
||||
document.querySelector('.play').addEventListener('click', () => {
|
||||
rootAnimation.play();
|
||||
});
|
||||
|
||||
document.querySelector('.pause').addEventListener('click', () => {
|
||||
rootAnimation.pause();
|
||||
});
|
||||
|
||||
document.querySelector('.destroy').addEventListener('click', () => {
|
||||
rootAnimation.destroy();
|
||||
});
|
||||
|
||||
document.querySelector('.stop').addEventListener('click', () => {
|
||||
rootAnimation.stop();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.square {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: rgba(0, 0, 255, 0.5);
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
margin-left: 25px;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Animations</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<div class="ion-padding">
|
||||
<ion-button class="play">Play</ion-button>
|
||||
<ion-button class="pause">Pause</ion-button>
|
||||
<ion-button class="stop">Stop</ion-button>
|
||||
<ion-button class="destroy">Destroy</ion-button>
|
||||
|
||||
<div class="square square-a">
|
||||
Hello
|
||||
</div>
|
||||
|
||||
<div class="square square-b">
|
||||
Hello
|
||||
</div>
|
||||
|
||||
<div class="square square-c">
|
||||
Hello
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
76
core/src/utils/animation/test/simple/index.html
Normal file
76
core/src/utils/animation/test/simple/index.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Animation - Basic</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
import { createAnimation } from '../../../../dist/collection/utils/animation/animation.js';
|
||||
|
||||
const square = document.querySelectorAll('.square');
|
||||
const animation = createAnimation('animation-a');
|
||||
|
||||
animation
|
||||
.addElement(square)
|
||||
.duration(2000)
|
||||
.easing('linear')
|
||||
.iterations(1)
|
||||
.keyframes([
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 0 },
|
||||
{ transform: 'scale(1.5) rotate(45deg)', opacity: 0.5, offset: 0.5 },
|
||||
{ transform: 'scale(1) rotate(0deg)', opacity: 1, offset: 1 }
|
||||
])
|
||||
.beforeAddClass(['hello', 'ionic', 'world'])
|
||||
.beforeRemoveClass('liam-was-here')
|
||||
.beforeStyles({
|
||||
'background': 'rgba(255, 0, 0, 0.5)',
|
||||
'color': 'white'
|
||||
})
|
||||
.afterStyles({
|
||||
'background': 'rgba(0, 255, 0, 0.5)',
|
||||
});
|
||||
|
||||
animation.play();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.square {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: rgba(0, 0, 255, 0.5);
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
margin-left: 25px;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Animations</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<div class="ion-padding">
|
||||
<div class="square liam-was-here">
|
||||
Hello
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user