css-dissolve-animation-angular v0.1.4
Live example: https://dissolve.herokuapp.com/
DissolveAnimation
This module implements ES6 Promises, classes, and subclasses under the hood to control various CSS animations. It's currently only setup to work with Angular, but everything is writen in vanilla JavaScript (TypeScript) so it could easily be adapted to work with other frameworks.
Background
I decided to create this after I was unable to find other libraries that are fully compatible with all browsers. I also couldn't find a simple implementation for cross dissolves and sequence dissolves.
Getting started
cd into your project directory and run
npm install --save css-dissolve-animation-angularAnimation Types
There are currently only two dissolve types:
- Cross Dissolve: One element fades out while another element fades in simultaneously.
 
import { CrossDissolve } from 'css-dissolve-animation-angular';- Sequence Dissolve: One element fades out then the second element fades in.
 
import { SequenceDissolve } from 'css-dissolve-animation-angular';Dissolve class Parameters
- Both 
CrossDissolveandSequenceDissolveshare the following signature: 
CrossDissolve(dataArray: any[],
              {
                staticKlasses?: string,
                interval?: number,
                transitionDuration?: number,
                fadeInOverride?: string,
                fadeOutOverride?: string,
                eventIdentifier?: string,
              });dataArray: an array ofobjects containing the data you want to use to populate the html template.- Optional parameters: an object containing any of the following:
staticKlasses: astringcontaining the CSS class(es) you want applied to all elements (leave blank if you don't want any CSS classes applied).interval:numberin miliseconds indicating the interval between transitions (8000if none is provided).transitionDuration:numberin miliseconds indicating length of the transition (3000if none is provided).fadeInOverride: Optional CSS class containing style information needed to execute the fade in. Note: If you change the transition duration, this parameter is required.fadeOutOverride: Same asfadeInOverrideexcept the class you want to use to fade out.eventIdentifier: an optional unique string used to when building theEvents. See here for more details.
 
Using With Angular Components
- In each component using DissolveAnimation, prepend the path to this module's stylesheet under 
styleUrls: 
Referencing CSS example:
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['../../../node_modules/css-dissolve-animation-angular/css/styles.css', './app.component.css']
})Using DissolveAnimation With Angular Templates
- DissolveAnimation provides the template with two instances of the 
TransitionItemclass:itemAanditemB.itemAshould have a higherz-indexthanitemB. Consequently, html elements containingitemAshould be placed below those containingitemBor thez-indexshould be adjusted accordingly. See here and here for more details on howTransitionItems can be used in templates. 
TransitionItem signature:
TransitionItem(track: number, state: string, klass: string, data: string);track: indicates which item has a higherz-index. DissolveAnimation assignsitemAtrack1anditemBtrack2.state: indicates whether the item is in a'hide'or'show'state (intended for internal use).klass: is astringcontaining the CSS class(es) assigned to the item.klassgets updated depending on thestateand whether or not a transition is in progress.data: is an item fromdataArraywhich is specified when instantiatingCrossDissolve/SequenceDissolve(see above).
Setting Up an Angular Project for DissolveAnimation
- Create a new Angular project (see here if you don't have Angular installed):
 
ng new my-animationcdinto the root of your project and run:
npm install --save css-dissolve-animation-angularCross Dissolve Example
- Follow the instuctions above on how to setup a project.
 - Create a new service called 
photo: 
ng g service photo --module=app- In 
src/app/photo.service.tsreplace the boilerplate code with the following code to implement the ability to fetch photos from picsum.com (we'll use these photos in our example project): 
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class PhotoService {
  constructor(private http: HttpClient) { }
  public fetchPhotos():Observable<any> {
    return this.http.get('https://picsum.photos/list');
  }
}- In 
src/app/app.module.tsadd the following at the top: 
import { HttpClientModule } from '@angular/common/http';Include HttpClientModule in the imports array:
  imports: [
    BrowserModule,
    HttpClientModule
  ],Implementing CrossDissolve in a component:
- Create a new component named 
slideshow: 
ng g component slideshow- In 
src/app/app.component.htmldelete all the boilerplate html and add 
<app-slideshow></app-slideshow>- Also replace the boilerplate code in 
src/app/slideshow/slideshow.component.tswith the following: 
import { Component, OnInit } from '@angular/core';
import { CrossDissolve } from 'css-dissolve-animation-angular';
import { PhotoService } from '../photo.service';
@Component({
  selector: 'app-slideshow',
  templateUrl: './slideshow.component.html',
  styleUrls: ['../../../node_modules/css-dissolve-animation-angular/css/styles.css', './slideshow.component.css']
})
export class SlideshowComponent implements OnInit {
  public crossDissolve: CrossDissolve;
  constructor( public photoService: PhotoService ) { }
  ngOnInit() {
    this.photoService.fetchPhotos().subscribe((response: any) => {
      const start = this.getRandomInt(0,response.length-11);
      const photos = response.slice(start, start+10);
      this.addData(photos);
      this.crossDissolve = new CrossDissolve(photos, { staticKlasses: 'bg' });
      this.crossDissolve.animate();
    });
  }
  private addData(photos: any[]): void {
    for (let photo of photos) {
      photo['style'] = this.buildBGStyle(photo.id);
    }
  }
  private getRandomInt(min: number, max:number): number {
      return Math.floor(Math.random() * (max - min + 1)) + min;
  }
  private buildImgUrl(id: number): string {
    const width = Math.floor(window.innerWidth*window.devicePixelRatio);
    const height = Math.floor(window.innerHeight*window.devicePixelRatio);
    return `https://picsum.photos/${width}/${height}/?image=${id}`
  }
  private buildBGStyle(id: number): any {
    const url = this.buildImgUrl(id);
    return {'background-image':  `url(${url})`};
  }
}Implementing CrossDissolve in the template:
- Replace the boilerplate code in 
src/app/slideshow/slideshow.component.htmlwith the following: 
<div *ngIf="crossDissolve &&
      crossDissolve.itemA.data &&
      crossDissolve.itemB.data">
  <div [ngClass]="crossDissolve.itemB.klass"
       [ngStyle]="crossDissolve.itemB.data.style">
  </div>
  <div [ngClass]="crossDissolve.itemA.klass"
       [ngStyle]="crossDissolve.itemA.data.style">
  </div>
</div>Implementing CrossDissolve in the CSS:
- Add the following in 
src/app/slideshow/slideshow.component.css: 
.bg {
  left: 0;
  top: 0;
  position: fixed;
  height: 100%;
  width: 100%;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}- Run 
ng serve --openand a fabulous full screen slideshow should appear complete with absurdly long cross dissolves. To learn how to speed them up, skip to AdjustingCrossDissolveTransition Duration. 
Sequence Dissolve Example
- If you haven't already done so, follow the instuctions above on how to setup a project.
 
Implementing SequenceDissolve in a component:
- Create a new component named 
headlines: 
ng g component headlines- In 
src/app/app.component.htmladd the following BELOW<app-slideshow></app-slideshow>: 
<app-headlines></app-headlines>Note: If you put it above <app-slideshow>, it will be covered by the slideshow. However, in production, you would likely want the slideshow to load last so it would be best to put the slideshow towards the bottom and adjust the z-index on the other elements to ensure they appear above.
- In 
src/app/headlines/create a new file namedheadlines.tsand add the following: 
export const HEADLINES =
[
{ headline: "Lorem ipsum dolor sit amet", url: "http://www.example.com/" },
{ headline: "consectetur adipiscing elit", url: "http://www.example.com/" },
{ headline: "sed do eiusmod tempor incididunt", url: "http://www.example.com/" },
{ headline: "Ut enim ad minim veniam", url: "http://www.example.com/" }
];- In 
src/app/headlines/headlines.component.tsreplace the boilerplate code with the following: 
import { Component, OnInit } from '@angular/core';
import { SequenceDissolve } from 'css-dissolve-animation-angular';
import { HEADLINES } from './headlines';
@Component({
  selector: 'app-headlines',
  templateUrl: './headlines.component.html',
  styleUrls: ['../../../node_modules/css-dissolve-animation-angular/css/styles.css', './headlines.component.css']
})
export class HeadlinesComponent implements OnInit {
  public sequenceDissolve: SequenceDissolve;
  constructor() { }
  ngOnInit() {
    this.sequenceDissolve = new SequenceDissolve(HEADLINES, { staticKlasses: 'headline' });
    this.sequenceDissolve.animate();
  }
}Implementing SequenceDissolve in the CSS:
- In 
src/app/headlines/headlines.component.cssadd the following: 
.headline {
  color: #ffffff;
  padding-top: 100px;
  text-align: center;
}
.headline a {
  color: #ffffff;
  text-decoration: none;
}
.headline a:hover {
  text-decoration: underline;
}Implementing SequenceDissolve in the template:
- Finally, in 
src/app/headlines/headlines.component.htmlreplace the boilerplate code with the following: 
<div *ngIf="sequenceDissolve &&
            sequenceDissolve.itemA &&
            sequenceDissolve.itemB">
    <h1 [ngClass]="sequenceDissolve.itemB.klass">
      <a href="{{sequenceDissolve.itemB.data.url}}"
         target="_blank">
        {{sequenceDissolve.itemB.data.headline}}
      </a>
    </h1>
    <h1 [ngClass]="sequenceDissolve.itemA.klass">
      <a href="{{sequenceDissolve.itemA.data.url}}"
         target="_blank">
        {{sequenceDissolve.itemA.data.headline}}
      </a>
    </h1>
</div>- Run 
ng serve --openand text should appear above the slideshow, complete with ridiculously long sequence dissolves. To learn how to speed them up, skip to AdjustingSequenceDissolveTransition Duration. 
Adjust The Interval Between Transitions
- In 
src/app/slideshow/slideshow.component.tsaddinterval: 3000(interval in milliseconds) to the optional parameters object like so: 
this.crossDissolve = new CrossDissolve(photos, { staticKlasses: 'bg', interval: 3000});- In 
src/app/headlines/headlines.component.tsaddinterval: 3000to the optional parameters object: 
this.sequenceDissolve = new SequenceDissolve(HEADLINES, { staticKlasses: 'headline', interval: 3000});- Save the changes. Now the interval between transitions should be three seconds instead of eight.
 - Note: since the next image is always loading in the background, there should be a lower chance that the the image will not be fully loaded once the transition occurs. However, if you know your website will be viewed over a slow connection, it is a good idea to keep the intervals long in order to ensure enough load time before the next image fades in.
 
Adjusting CrossDissolve Transition Duration
- Add the following to 
src/app/slideshow/slideshow.component.css: 
.my-cross-dissolve-fade-in {
  -webkit-animation: daFadeIn 1s linear;
  -moz-animation: daFadeIn 1s linear;
  -o-animation: daFadeIn 1s linear;
  animation: daFadeIn 1s linear;
}
.my-cross-dissolve-fade-out {
  -webkit-animation: daFadeOut 1s linear;
  -moz-animation: daFadeOut 1s linear;
  -o-animation: daFadeOut 1s linear;
  animation: daFadeOut 1s linear;
}- In 
src/app/slideshow/slideshow.component.tsadd three more parameters toCrossDissolve's optional parameters object like so: 
this.crossDissolve = new CrossDissolve(photos, {
                                         staticKlasses: 'bg',
                                         interval: 3000,
                                         transitionDuration: 1000,
                                         fadeInOverride: 'my-cross-dissolve-fade-in',
                                         fadeOutOverride: 'my-cross-dissolve-fade-out'
                                       });The first parameter added is the new transition duration in miliseconds. The last two reflect the two classes we added to
slideshow.component.css.After saving, the page should reload and the transitions should only be one second long.
Adjusting SequenceDissolve Transition Duration
- Add the following to 
src/app/headlines/headlines.component.css: 
.my-sequence-dissolve-fade-in {
  -webkit-animation: daFadeIn calc(0.5s/.96) linear;
  -moz-animation: daFadeIn calc(0.5s/.96) linear;
  -o-animation: daFadeIn calc(0.5s/.96) linear;
  animation: daFadeIn calc(0.5s/.96) linear;
}
.my-sequence-dissolve-fade-out {
  -webkit-animation: daFadeOut calc(0.5s/.96) linear;
  -moz-animation: daFadeOut calc(0.5s/.96) linear;
  -o-animation: daFadeOut calc(0.5s/.96) linear;
  animation: daFadeOut calc(0.5s/.96) linear;
}- In 
src/app/headlines/headlines.component.tsadd three more parameters toSequenceDissolvelike so: 
this.sequenceDissolve = new SequenceDissolve(HEADLINES, {
                                               staticKlasses: 'headline',
                                               interval: 3000,
                                               transitionDuration: (500/.96),
                                               fadeInOverride: 'my-sequence-dissolve-fade-in',
                                               fadeOutOverride: 'my-sequence-dissolve-fade-out'
                                             });- The main difference between 
CrossDissolveandSequenceDissolveis the transitions do not occur simutaneously so if we set the duration to 1/2 second (500 miliseconds), the entire sequence dissove would take around one second to complete. Notice I say "around one second". I say this because the duration of each transition is decreased by 4% to avoid a flash when next elements appears. If we want the true duration to be 500 miliseconds, we must divide it by .96. The same principle applies to the CSS: if we want the true duration to be .5 we must divide it by .96. 
Listening to Events
Both animation types emit an event each time a state change is registered. The event names are built using the following format:
[eventIdentifier]--[item name]--[css class label]--[status]eventIdentifier: One of the optional parameters specified in the optional parameters object. If it isn't specified this parameter will be an empty string.item namethe item to which this event belongs (EitheritemAoritemB).css class label: the css class label whichstatusis describing. See below for possible css classes.status: one of 2 possibilities:WILL-ADDorWILL-REMOVE
Possible css classes
da-visible*da-invisible*da-shown^da-hidden^
* used with CrossDissolve
^ used with SequenceDissolve
Event subscription example
If you specified 'my-cross-dissolve' as the eventIdentifier, and you want to know when the da-visible class is about to be added to itemA, you would listen to the following event:
my-cross-dissolve--itemA--da-visible--WILL-ADDIf you specified 'my-sequence-dissolve' as the eventIdentifier, and you want to know when the da-shown class is about to be removed from itemB, you would listen to the following event:
my-sequence-dissolve--itemB--da-shown--WILL-REMOVEIf you do not specify eventIdentifier, and you want to know when the da-hidden class is about to be added to itemA, you would listen to the following event:
itemB--da-hidden--WILL-ADD