mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
add base before and after hooks
This commit is contained in:
@@ -1,6 +1,36 @@
|
||||
// TODO: Add validation
|
||||
// TODO: More tests
|
||||
|
||||
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 addElement = (animationElements: any[], el: Node | Node[] | NodeList | undefined | null) => {
|
||||
if (el != null) {
|
||||
const nodeList = el as NodeList;
|
||||
@@ -75,7 +105,7 @@ const addClassToArray = (classes: string[] = [], className: string | string[] |
|
||||
};
|
||||
|
||||
export class Animation {
|
||||
private elements: Element[] = [];
|
||||
private elements: HTMLElement[] = [];
|
||||
private childAnimations: Animation[] = [];
|
||||
|
||||
private _delay: number | undefined;
|
||||
@@ -287,7 +317,77 @@ export class Animation {
|
||||
return this;
|
||||
}
|
||||
|
||||
private beforeAnimation() {
|
||||
this.childAnimations.forEach(childAnimation => {
|
||||
childAnimation.beforeAnimation();
|
||||
});
|
||||
|
||||
const elements = this.elements;
|
||||
const addClasses = this.beforeAddClasses;
|
||||
const removeClasses = this.beforeRemoveClasses;
|
||||
const styles = this.beforeStylesValue;
|
||||
|
||||
elements.forEach(el => {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
animationEnd(elements[0], () => {
|
||||
this.afterAnimation();
|
||||
});
|
||||
}
|
||||
|
||||
private afterAnimation() {
|
||||
const elements = this.elements;
|
||||
const addClasses = this.afterAddClasses;
|
||||
const removeClasses = this.afterRemoveClasses;
|
||||
const styles = this.afterStylesValue;
|
||||
|
||||
elements.forEach(el => {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.cleanUpDOM();
|
||||
}
|
||||
|
||||
private cleanUpDOM() {
|
||||
if (this.stylesheet) {
|
||||
this.stylesheet.parentNode!.removeChild(this.stylesheet);
|
||||
this.stylesheet = undefined;
|
||||
}
|
||||
|
||||
this.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');
|
||||
});
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
private initializeAnimation(): void {
|
||||
this.beforeAnimation();
|
||||
|
||||
if (!this.stylesheet) {
|
||||
this.stylesheet = createKeyframeStylesheet(this._keyframeString);
|
||||
}
|
||||
|
||||
135
core/src/utils/animation/test/multiple/index.html
Normal file
135
core/src/utils/animation/test/multiple/index.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!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 { Animation as IonicAnimation } from '../../../../dist/collection/utils/animation/animation.js';
|
||||
window.IonicAnimation = IonicAnimation;
|
||||
|
||||
|
||||
const squareA = document.querySelectorAll('.square-a');
|
||||
const squareB = document.querySelectorAll('.square-b');
|
||||
const squareC = document.querySelectorAll('.square-c');
|
||||
|
||||
const rootAnimation = new IonicAnimation();
|
||||
const animationA = new IonicAnimation('animation-a');
|
||||
const animationB = new IonicAnimation('animation-b');
|
||||
const animationC = new IonicAnimation('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]);
|
||||
rootAnimation.play();
|
||||
|
||||
console.log(rootAnimation);
|
||||
|
||||
document.querySelector('.play').addEventListener('click', () => {
|
||||
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;
|
||||
}
|
||||
</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>
|
||||
|
||||
<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>
|
||||
|
||||
78
core/src/utils/animation/test/simple/index.html
Normal file
78
core/src/utils/animation/test/simple/index.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<!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 { Animation as IonicAnimation } from '../../../../dist/collection/utils/animation/animation.js';
|
||||
|
||||
window.IonicAnimation = IonicAnimation;
|
||||
|
||||
const square = document.querySelectorAll('.square');
|
||||
const animation = new IonicAnimation('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