add base before and after hooks

This commit is contained in:
Liam DeBeasi
2019-07-12 13:24:38 -04:00
parent 9ff5dd39e0
commit f4f4d9fe38
3 changed files with 314 additions and 1 deletions

View File

@@ -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);
}

View 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>

View 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>