AngularJS, Animate, ja Material slideshow

No niin, tulin kirjailleeksi Angular-appsin korvaamaan powerpoint kalvosulkeisia jollain joka toimii kivasti selaimessa, vaikkapa ipadissa. Tarkasti ottaen modifioin olemassaolevaa täysin hyvää koodia, lisäten siihen muutaman oman mausteen. Originaali artikkeli ja koodi löytyy täältä:

http://onehungrymind.com/build-a-full-page-angularjs-slideshow/

Mitäpä modasin tästä? No, originaali artikkelissa oli $timer palvelulla ajastetut slidet, halusin itse vaihtaa nappia painamalla. Lisäksi halusin ottaa Material Design kirjastot mukaan jatkokehitystä varten, ja käyttää sieltä tulevaa primary button-ideaa. Ja tietysti päivitellä kirjastot tuoreimpiin. Lisäksi kokeilin tehdä tämän alusta alkaen Netbeans HTML5 projektina. Ja täytyy sanoa että sekin puoli alkaa hiljalleen olemaan toimiva.

Joka tapauksessa, jahka projektirunko on luotu (esim. Netbeans HTML5 projekti ja angular seed), on aika päivittää kirjastoriippuvuudet bower.jsoniin:

{
 "name": "angular-seed",
 "description": "Spagettikoodi slideshow presentation",
 "version": "0.0.1",
 "private": true,
 "dependencies": {
 "angular": "1.3.x",
 "angular-route": "1.3.x",
 "angular-loader": "1.3.x",
 "angular-animate": "1.3.x",
 "angular-mocks": "~1.3.x",
 "angular-material": "~0.5.x",
 "bootstrap":"*",
 "angular-bootstrap":"*",
 "gsap": "~1.14.x",
 "PreloadJS": "*",
 "material-design-icons": "*"
 },
 "resolutions": {
 "angular": "~1.3.x"
 }
 }

Seuraava tempaus: Bower lykkää tiedostot juuren alle bower_components kansioon, kun taas Netbeans sisäänrakennettu serveri ei osaa sieltä hakea tiedostoja. Tein siis muutoksen jossa bower lykkääkin hakemansa tiedostot app/lib alle, tekemällä .bowerrc tiedoston jonka sisältö on:

{
 "directory" : "app/lib"
 }

Nyt kun ajan bower install operaation, tarvittavat kirjastot tiukkuvat app/lib kansion alle – jonka muuten lisään .gitignoreen samalla.

Seuraava steppi on ladata javascript kirjastot. Tässä projektissa en käyttänyt requirea joten lataus menee ihan näin:

<script src="lib/jquery/dist/jquery.js"></script>
 <script src="lib/angular/angular.js"></script>
 <script src="lib/angular-route/angular-route.js"></script>
 <script src="lib/angular-animate/angular-animate.js"></script>
 <script src="lib/gsap/src/uncompressed/TweenMax.js"></script>
 <script src="lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
 <script src="lib/PreloadJS/lib/preloadjs-0.4.1.combined.js"></script>
 <script src="lib/angular-aria/angular-aria.js"></script>
 <script src="lib/hammerjs/hammer.js"></script>
 <script src="lib/angular-material/angular-material.js"></script>
 <script src="js/app.js"></script>

Tämän ohella on hyvä latailla material design tyylisivut ja haluamasi teema, esim. näin:

<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.css"/>
 <link rel="stylesheet" href="./lib/angular-material/angular-material.css"/>
 <link rel="stylesheet" href="./lib/angular-material/themes/default-theme.css"/>
 <link rel="stylesheet" href="./css/app.css"/>

Nyt saakin koota sitten index.html sivun body-tagin sisälle slideshow käyttöliittymä. Se menee taas näin:

<div>

<div class="img-container">
 <img ng-repeat="slide in slides"
 class="fullBg {{currentAnimation}}"
 ng-swipe-right="nextSlide()"
 ng-swipe-left="prevSlide()"
 ng-if="isCurrentSlideIndex($index)"
 ng-src="{{slide.image}}"
 >
</div>

<md-button
 aria-label="Next"
 style="position: absolute; right: 3em; top:2em;"
 class="md-fab md-primary"
 ng-click="nextSlide()"
 >
 <md-icon icon="lib/material-design-icons/navigation/svg/production/ic_arrow_forward_24px.svg"
 style="width: 24px; height: 24px;">
 </md-icon>
</md-button>
</div>

No niin, pari huomiota tuosta koodista:

– Päätyön tekee tuo kuva joka hakee ng-repeat direktiivillä controllerin scope-muuttujista slides-muuttujalta kuvien nimet, ja lataa ne. ng-if vuorostaan näyttää valittuna olevan, css luokka määrittää täysikokoisen kuvan sekä valitsee halutun animaation käyttöön siirtymiin. Tästä koodista voi kiittää originaalia 100%.

– Alla on kokeiluluontoinen md-button. Sliden vaihtamisen voi näprätä mihin hyvänsä muuhunkin muotoon, mutta tässä oli tarkoitus testata materialia, joten otetaan pyöreä fab-nappi, primary tehostein. Jos haluaa kokeilla muita teemoja, voi lisätä tähän esim. md-theme=indigo tms – mutta sitten on myös syytä ladata vastaavat css-tyylisivut.

– Button käyttää myös yhtä material-icons kirjaston valmis-ikoneista.

Homma alkaa kasaantumaan. Tämähän vaatii myös tyylisivun. En tehnyt originaaliin kummempia muutoksia joten se löytyy tuolta alussa olevan linkin takaa, mutta tässä keskeinen kohta, eli miltä .fullBg näyttää:

.fullBg {
 position: fixed;
 width: 100%;
 height: 100%;
 top:0px;
 left: 0px;
 }

No niin, sitten varsinaiseen mielenkiintoiseen kohtaan eli koodiin. Tässä on taustacontrolleri:

'use strict';

var app = angular.module('website', ['ngMaterial','ngAnimate', 'ui.bootstrap']);

// Declare app level module which depends on filters, and services

app.controller('MainCtrl', function ($scope) {

  $scope.slides = [];
  for (var i = 1; i <= 29; i++) {
  $scope.slides.push(
    {
      image: 'images/Slide' + i + '.PNG',
      description: 'Image ' + i
    }
  );
}

$scope.currentIndex = 0;
$scope.currentAnimation = 'slide-left-animation';
//$scope.currentAnimation = 'slide-down-animation';
//$scope.currentAnimation = 'fade-in-animation';

$scope.setCurrentSlideIndex = function (index) {
 $scope.currentIndex = index;
};

$scope.isCurrentSlideIndex = function (index) {
 return $scope.currentIndex === index;
};

$scope.nextSlide = function () {
 $scope.currentIndex = ($scope.currentIndex < $scope.slides.length - 1) ? ++$scope.currentIndex : 0;
};

$scope.prevSlide = function () {
 $scope.currentIndex = ($scope.currentIndex > 0) ? --$scope.currentIndex : $scope.slides.length - 1;
};

$scope.setCurrentAnimation = function (animation) {
 $scope.currentAnimation = animation;
};

$scope.isCurrentAnimation = function (animation) {
 return $scope.currentAnimation === animation;
};

$scope.keyPress = function (keyEvent) {
 $scope.nextSlide();
};

});

Jep, eli alussa ladataan slide-kuvia 29 kappaletta. Nollataan indeksi ja valitaan animaatiotyyli. Sitten määritellään muutama funktio – lähinnä set/get funktioita tilanhallintaan – niillä näprätään mikä on valittuna oleva slide ja millä ehdoilla edetään tai peruutetaan, ja mikä on tämänhetkinen animaatio.

Sitten päästään itse mielenkiintoiseen: miten angular-animate toimii. Sillä voi määritellä animaatio-tyyliluokkia, ja kun html elementille on sellainen määritelty, se määrittää mitä tapahtuu esim. elementin poistuessa tai ilmestyessä säiliöön. Esim. jos kuvallemme on annettu, kuten yllä, tällainen tyylimääritys:

<img ng-repeat="slide in slides"
 class="fullBg slide-left-animation"
 ...

..niin kuvan poistuessa näkyvistä tai ilmestyessä ajetaan seuraava animaatio, jonka voit myös määritellä controllerille:

app.animation('.slide-left-animation', function ($window) {
 return {
 enter: function (element, done) {
 TweenMax.fromTo(element, 1, {left: $window.innerWidth}, {left: 0, onComplete: done});
 },
 leave: function (element, done) {
 TweenMax.to(element, 1, {left: -$window.innerWidth, onComplete: done});
 }
 };
});

Se tekee mitä vain haluat. Tässä siis sisääntullessa ajetaan TweenMax.fromTo funktio (GreenSock kirjastosta, joka aiemmin otettiin käyttöön) – ja se animoi siirtymän oikealta vasemmalle (kuvan vasen reuna on aluksi ruudun oikean reunan ulkopuolella, sitten siirtyy sieltä nollaan).

Vastaavasti kun kuva poistuu säiliöstä, se lipuu ikkunan koon verran vasemmalle ruudun ulkopuolelle.

Muita vastaavia animaatiovaihtoehtoja:

app.animation('.slide-down-animation', function ($window) {
 return {
 enter: function (element, done) {
 TweenMax.fromTo(element, 1, {top: -$window.innerHeight}, {top: 0, onComplete: done});
 },
 leave: function (element, done) {
 TweenMax.to(element, 1, {top: $window.innerHeight, onComplete: done});
 }
 };
 });

app.animation('.fade-in-animation', function ($window) {
 return {
 enter: function (element, done) {
 TweenMax.fromTo(element, 1, {opacity: 0}, {opacity: 1, onComplete: done});
 },
 leave: function (element, done) {
 TweenMax.to(element, 1, {opacity: 0, onComplete: done});
 }
 };
});

Mutta tosiaan, ei ole omaa keksintöä, kunhan ihastelen ja sovellan ratkaisua. Angular-animate on hyvin mielenkiintoinen kirjasto ja varovaisen tutustumisen perusteella myös vakaa. Angular-Material tutkinta jatkuu, ei voi sanoa että tässä olisi käytetty sen parhaita piirteitä ja ainakin omalla ruudulla tuo oikean yläkulman fab-nappi on penteleen ruma (sen saa sulautumaan taustaan jos jättää primary luokan pois määrittelystä) – mutta tämä oli itselle enempi tech demo edelleen.

Joka tapauksessa, taas koodia ja linkkivinkkejä itselle muistiin.

 

 

Mainokset

2 thoughts on “AngularJS, Animate, ja Material slideshow

    • Heh, tää tarvisi myös tykkää-napin kommentteihin. Mulla oli tarkoituksena tosiaan vain vähän testailla frameworkejä, eritoten tuo animate vaikuttaisi lupaavalta. Mutta tuo Reveal on tosissaan kyllä näyttävä, ja valmista tavaraa. Koskaan ei ole hihassa liikaa hyviä frameworkejä. 😉

Vastaa

Täytä tietosi alle tai klikkaa kuvaketta kirjautuaksesi sisään:

WordPress.com-logo

Olet kommentoimassa WordPress.com -tilin nimissä. Log Out / Muuta )

Twitter-kuva

Olet kommentoimassa Twitter -tilin nimissä. Log Out / Muuta )

Facebook-kuva

Olet kommentoimassa Facebook -tilin nimissä. Log Out / Muuta )

Google+ photo

Olet kommentoimassa Google+ -tilin nimissä. Log Out / Muuta )

Muodostetaan yhteyttä palveluun %s