Skip to content

Commit dfb07ed

Browse files
authored
Merge pull request #29 from DarrenRuane/feat/add-heart-beat-head-shake
Add mising 'heartBeat' and 'headShake' animations
2 parents 5b14971 + ca09998 commit dfb07ed

File tree

6 files changed

+184
-1
lines changed

6 files changed

+184
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ All animations have `duration` and `delay` params.
175175
| | tada | `[@tada]` | `[@tadaOnEnter]` | |
176176
| | wobble | `[@wobble]` | `[@wobbleOnEnter]` | |
177177
| | jello | `[@jello]` | `[@jelloOnEnter]` | |
178+
| | heartBeat | `[@heartBeat]` | `[@heartBeatOnEnter]` | `scale` (default: 1.3) |
179+
| | headShake | `[@headShake]` | `[@headShakeOnEnter]` | |
178180
| Bouncing entrances | | | | |
179181
| | bounceIn | `[@bounceIn]` | `[@bounceInOnEnter]` | |
180182
| | bounceInDown | `[@bounceInDown]` | `[@bounceInDownOnEnter]` | `translate` (default: '3000px') |

demo-app/src/app/demo-main/demo-main.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ <h1 [@enter1]>
1414
<app-angular-img *ngSwitchCase="'tada'" [@tada]="animationState"></app-angular-img>
1515
<app-angular-img *ngSwitchCase="'wobble'" [@wobble]="animationState"></app-angular-img>
1616
<app-angular-img *ngSwitchCase="'jello'" [@jello]="animationState"></app-angular-img>
17+
<app-angular-img *ngSwitchCase="'heartBeat'" [@heartBeat]="animationState"></app-angular-img>
18+
<app-angular-img *ngSwitchCase="'headShake'" [@headShake]="animationState"></app-angular-img>
1719
<!-- Bouncing entrances -->
1820
<app-angular-img *ngSwitchCase="'bounceIn'" [@bounceIn]="animationState"></app-angular-img>
1921
<app-angular-img *ngSwitchCase="'bounceInDown'" [@bounceInDown]="animationState"></app-angular-img>

demo-app/src/app/demo-main/demo-main.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
tadaAnimation,
1111
wobbleAnimation,
1212
jelloAnimation,
13+
heartBeatAnimation,
14+
headShakeAnimation,
1315
bounceInAnimation,
1416
bounceInDownAnimation,
1517
bounceInLeftAnimation,
@@ -101,6 +103,8 @@ import {
101103
tadaAnimation(),
102104
wobbleAnimation(),
103105
jelloAnimation(),
106+
heartBeatAnimation(),
107+
headShakeAnimation(),
104108
bounceInAnimation(),
105109
bounceInDownAnimation(),
106110
bounceInLeftAnimation(),
@@ -180,7 +184,7 @@ export class DemoMainComponent {
180184
options = [
181185
{
182186
label: 'Attention Seekers',
183-
animations: ['bounce', 'flash', 'pulse', 'rubberBand', 'shake', 'swing', 'tada', 'wobble', 'jello']
187+
animations: ['bounce', 'flash', 'pulse', 'rubberBand', 'shake', 'swing', 'tada', 'wobble', 'jello', 'heartBeat', 'headShake']
184188
},
185189
{
186190
label: 'Bouncing Entrances',
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
animate,
3+
animateChild,
4+
animation,
5+
AnimationTriggerMetadata,
6+
AUTO_STYLE,
7+
group,
8+
keyframes,
9+
query,
10+
style,
11+
transition,
12+
trigger,
13+
useAnimation
14+
} from '@angular/animations';
15+
16+
import { IAttentionSeekerAnimationOptions } from '../common/interfaces';
17+
18+
const headShake = animation([
19+
animate(
20+
'{{duration}}ms {{delay}}ms',
21+
keyframes([
22+
style({ visibility: AUTO_STYLE, transform: 'translateX(0)', easing: 'ease-in-out', offset: 0 }),
23+
style({ transform: 'translateX(-6px) rotateY(-9deg)', easing: 'ease-in-out', offset: 0.065 }),
24+
style({ transform: 'translateX(5px) rotateY(7deg)', easing: 'ease-in-out', offset: 0.185 }),
25+
style({ transform: 'translateX(-3px) rotateY(-5deg)', easing: 'ease-in-out', offset: 0.315 }),
26+
style({ transform: 'translateX(2px) rotateY(3deg)', easing: 'ease-in-out', offset: 0.435 }),
27+
style({ transform: 'translateX(0)', easing: 'ease-in-out', offset: 0.5 })
28+
])
29+
)
30+
]);
31+
32+
const DEFAULT_DURATION = 1000;
33+
34+
export function headShakeAnimation(options?: IAttentionSeekerAnimationOptions): AnimationTriggerMetadata {
35+
return trigger((options && options.anchor) || 'headShake', [
36+
transition(
37+
`0 ${(options && options.direction) || '<=>'} 1`,
38+
[
39+
...(options && options.animateChildren === 'before' ? [query('@*', animateChild(), { optional: true })] : []),
40+
group([
41+
useAnimation(headShake),
42+
...(!options || !options.animateChildren || options.animateChildren === 'together'
43+
? [query('@*', animateChild(), { optional: true })]
44+
: [])
45+
]),
46+
...(options && options.animateChildren === 'after' ? [query('@*', animateChild(), { optional: true })] : [])
47+
],
48+
{
49+
params: {
50+
delay: (options && options.delay) || 0,
51+
duration: (options && options.duration) || DEFAULT_DURATION
52+
}
53+
}
54+
)
55+
]);
56+
}
57+
58+
export function headShakeOnEnterAnimation(options?: IAttentionSeekerAnimationOptions): AnimationTriggerMetadata {
59+
return trigger((options && options.anchor) || 'headShakeOnEnter', [
60+
transition(
61+
':enter',
62+
[
63+
...(options && options.animateChildren === 'before' ? [query('@*', animateChild(), { optional: true })] : []),
64+
style({ visibility: 'hidden' }),
65+
group([
66+
useAnimation(headShake),
67+
...(!options || !options.animateChildren || options.animateChildren === 'together'
68+
? [query('@*', animateChild(), { optional: true })]
69+
: [])
70+
]),
71+
...(options && options.animateChildren === 'after' ? [query('@*', animateChild(), { optional: true })] : [])
72+
],
73+
{
74+
params: {
75+
delay: (options && options.delay) || 0,
76+
duration: (options && options.duration) || DEFAULT_DURATION
77+
}
78+
}
79+
)
80+
]);
81+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {
2+
animate,
3+
animateChild,
4+
animation,
5+
AnimationTriggerMetadata,
6+
AUTO_STYLE,
7+
group,
8+
keyframes,
9+
query,
10+
style,
11+
transition,
12+
trigger,
13+
useAnimation
14+
} from '@angular/animations';
15+
16+
import { IAttentionSeekerAnimationOptions } from '../common/interfaces';
17+
18+
export interface IHeartBeatAnimationOptions extends IAttentionSeekerAnimationOptions {
19+
/**
20+
* Scale factor
21+
*
22+
* Default: 1.3
23+
*/
24+
scale?: number;
25+
}
26+
27+
const heartBeat = animation([
28+
animate(
29+
'{{duration}}ms {{delay}}ms',
30+
keyframes([
31+
style({ visibility: AUTO_STYLE, transform: 'scale(1)', easing: 'ease-in-out', offset: 0 }),
32+
style({ transform: 'scale({{scale}})', easing: 'ease-in-out', offset: 0.14 }),
33+
style({ transform: 'scale(1)', easing: 'ease-in-out', offset: 0.28 }),
34+
style({ transform: 'scale({{scale}})', easing: 'ease-in-out', offset: 0.42 }),
35+
style({ transform: 'scale(1)', easing: 'ease-in-out', offset: 0.7 })
36+
])
37+
)
38+
]);
39+
40+
const DEFAULT_DURATION = 1300;
41+
const DEFAULT_SCALE = 1.3;
42+
43+
export function heartBeatAnimation(options?: IHeartBeatAnimationOptions): AnimationTriggerMetadata {
44+
return trigger((options && options.anchor) || 'heartBeat', [
45+
transition(
46+
`0 ${(options && options.direction) || '<=>'} 1`,
47+
[
48+
...(options && options.animateChildren === 'before' ? [query('@*', animateChild(), { optional: true })] : []),
49+
group([
50+
useAnimation(heartBeat),
51+
...(!options || !options.animateChildren || options.animateChildren === 'together'
52+
? [query('@*', animateChild(), { optional: true })]
53+
: [])
54+
]),
55+
...(options && options.animateChildren === 'after' ? [query('@*', animateChild(), { optional: true })] : [])
56+
],
57+
{
58+
params: {
59+
delay: (options && options.delay) || 0,
60+
duration: (options && options.duration) || DEFAULT_DURATION,
61+
scale: (options && options.scale) || DEFAULT_SCALE
62+
}
63+
}
64+
)
65+
]);
66+
}
67+
68+
export function heartBeatOnEnterAnimation(options?: IHeartBeatAnimationOptions): AnimationTriggerMetadata {
69+
return trigger((options && options.anchor) || 'heartBeatOnEnter', [
70+
transition(
71+
':enter',
72+
[
73+
...(options && options.animateChildren === 'before' ? [query('@*', animateChild(), { optional: true })] : []),
74+
style({ visibility: 'hidden' }),
75+
group([
76+
useAnimation(heartBeat),
77+
...(!options || !options.animateChildren || options.animateChildren === 'together'
78+
? [query('@*', animateChild(), { optional: true })]
79+
: [])
80+
]),
81+
...(options && options.animateChildren === 'after' ? [query('@*', animateChild(), { optional: true })] : [])
82+
],
83+
{
84+
params: {
85+
delay: (options && options.delay) || 0,
86+
duration: (options && options.duration) || DEFAULT_DURATION,
87+
scale: (options && options.scale) || DEFAULT_SCALE
88+
}
89+
}
90+
)
91+
]);
92+
}

lib/attention-seekers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ export * from './swing.animation';
77
export * from './tada.animation';
88
export * from './wobble.animation';
99
export * from './jello.animation';
10+
export * from './heart-beat.animation';
11+
export * from './head-shake.animation';

0 commit comments

Comments
 (0)