() => { const engine = makeEngine(); const trig = trigger('myTrigger', [transition('* => *', animate(1234, style({color: 'red'})))]); registerTrigger(element, engine, trig); setProperty(element, engine, 'myTrigger', 'value'); engine.flush(); expect((engine.players[0].getRealPlayer() as MockAnimationPlayer).duration) .toEqual(1234); engine.destroy(DEFAULT_NAMESPACE_ID, null); registerTrigger(element, engine, trig); setProperty(element, engine, 'myTrigger', 'value'); engine.flush(); expect((engine.players[0].getRealPlayer() as MockAnimationPlayer).duration) .toEqual(1234); });
() => { const engine = makeEngine(); const trig = trigger( 'myTrigger', [transition( '* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))])]); registerTrigger(element, engine, trig); let count = 0; listen(element, engine, 'myTrigger', 'start', () => count++); setProperty(element, engine, 'myTrigger', '123'); engine.flush(); expect(count).toEqual(1); setProperty(element, engine, 'myTrigger', '456'); engine.flush(); expect(count).toEqual(2); });
it('should allow a listener to be deregistered, but only after a flush occurs', () => { const engine = makeEngine(); const trig = trigger( 'myTrigger', [transition( '* => 123', [style({height: '0px'}), animate(1000, style({height: '100px'}))])]); registerTrigger(element, engine, trig); let count = 0; const deregisterFn = listen(element, engine, 'myTrigger', 'start', () => count++); setProperty(element, engine, 'myTrigger', '123'); engine.flush(); expect(count).toEqual(1); deregisterFn(); engine.flush(); setProperty(element, engine, 'myTrigger', '456'); engine.flush(); expect(count).toEqual(1); });
it('should not destroy timeline-based animations after they have finished', () => { const engine = makeEngine(); const log: string[] = []; function capture(value: string) { return () => { log.push(value); }; } const steps = [style({height: 0}), animate(1000, style({height: 500}))]; const player = invokeAnimation(engine, element, steps); player.onDone(capture('done')); player.onDestroy(capture('destroy')); expect(log).toEqual([]); player.finish(); expect(log).toEqual(['done']); player.destroy(); expect(log).toEqual(['done', 'destroy']); });
it('should trigger a listener callback with an AnimationEvent argument', () => { const engine = makeEngine(); registerTrigger( element, engine, trigger('myTrigger', [ transition( '* => *', [style({height: '0px'}), animate(1234, style({height: '100px'}))]) ])); // we do this so that the next transition has a starting value that isnt null setProperty(element, engine, 'myTrigger', '123'); engine.flush(); let capture: AnimationEvent = null !; listen(element, engine, 'myTrigger', 'start', e => capture = e); listen(element, engine, 'myTrigger', 'done', e => capture = e); setProperty(element, engine, 'myTrigger', '456'); engine.flush(); expect(capture).toEqual({ element, triggerName: 'myTrigger', phaseName: 'start', fromState: '123', toState: '456', totalTime: 1234 }); capture = null !; const player = engine.players.pop() !; player.finish(); expect(capture).toEqual({ element, triggerName: 'myTrigger', phaseName: 'done', fromState: '123', toState: '456', totalTime: 1234 }); });
it('should throw an error when normalization fails within a transition animation', () => { const engine = makeEngine(new ExactCssValueNormalizer({left: '100px'})); const trig = trigger('something', [ state('a', style({left: '0px', width: '200px'})), state('b', style({left: '100px', width: '100px'})), transition('a => b', animate(9876)) ]); registerTrigger(element, engine, trig); setProperty(element, engine, trig.name, 'a'); setProperty(element, engine, trig.name, 'b'); let errorMessage = ''; try { engine.flush(); } catch (e) { errorMessage = e.toString(); } expect(errorMessage).toMatch(/Unable to animate due to the following errors:/); expect(errorMessage).toMatch(/- The CSS property `left` is not allowed to be `0px`/); expect(errorMessage).toMatch(/- The CSS property `width` is not allowed/); });
export function queryTranslate(direction: string, axis: string, from: number, to: number, zIndex: number = 1000) { return query(':' + direction, [ style({ transform: 'translate' + axis + '(' + from + '%)', zIndex: zIndex, boxShadow: '0 3px 2px -2px gray' }), animate(speed + ' ease-in-out', style({ transform: 'translate' + axis + '(' + to + '%)' })), ], { optional: true }); }
import { trigger, state, style, transition, animate, keyframes, animation } from "@angular/animations"; export let fadeInAnimation = animation([ style({ opacity : 0}), animate('{{ duration }} {{ delay }} {{ easing }}') ], { params: { duration: '2000ms', delay : '', easing : 'ease-out' } }); export let fadeOutAnimation = animation([ style({ opacity : 0}), animate('{{ duration }} {{ delay }} {{ easing }}') ], { params: { duration: '2000ms', delay : '', easing : 'ease-in' } }); export let myFade = trigger('validationMessages',[ state('void', style({ opacity: 0})), transition(':enter, :leave',[ animate(1000)
// TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out. export const transformMenu: AnimationTriggerMetadata = trigger('transformMenu', [ state('void', style({ opacity: 0, // This starts off from 0.01, instead of 0, because there's an issue in the Angular animations // as of 4.2, which causes the animation to be skipped if it starts from 0. transform: 'scale(0.01, 0.01)' })), state('enter-start', style({ opacity: 1, transform: 'scale(1, 0.5)' })), state('enter', style({ transform: 'scale(1, 1)' })), transition('void => enter-start', animate('100ms linear')), transition('enter-start => enter', animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)')), transition('* => void', animate('150ms 50ms linear', style({opacity: 0}))) ]); /** * This animation fades in the background color and content of the menu panel * after its containing element is scaled in. */ export const fadeInItems: AnimationTriggerMetadata = trigger('fadeInItems', [ state('showing', style({opacity: 1})), transition('void => *', [ style({opacity: 0}), animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)') ])
* * Parameter Options: * * duration: Duration the animation will run in milliseconds. Defaults to 500 ms. * * delay: Delay before the animation will run in milliseconds. Defaults to 0 ms. * * ease: Animation accelerate and decelerate style. Defaults to ease-out. * * Returns an [AnimationTriggerMetadata] object with boolean states for a pulse animation. * * usage: [@tdPulse]="{ value: true | false, params: { duration: 200 }}" */ export const tdPulseAnimation: AnimationTriggerMetadata = trigger('tdPulse', [ state('0', style({ transform: 'scale3d(1, 1, 1)', })), state('1', style({ transform: 'scale3d(1, 1, 1)', })), transition('0 <=> 1', [ group([ query('@*', animateChild(), { optional: true }), animate('{{ duration }}ms {{ delay }}ms {{ ease }}', keyframes([ style({ transform: 'scale3d(1, 1, 1)', offset: 0 }), style({ transform: 'scale3d(1.05, 1.05, 1.05)', offset: 0.5 }), style({ transform: 'scale3d(1, 1, 1)', offset: 1.0 }), ]), ), ]), ], { params: { duration: 500, delay: '0', ease: 'ease-out' }}), ]);