Ionic Tour


Ionic Tour

This module allows devs to simulate a tour of their mobile app with the use of a tourtip that translates itself to whichever element is the next step on the tour.

The tour is instantiated inside a parent controller and steps are defined using an angular directive. You simply need to add tour-step="1" as an attribute to an element. You may also specify callbacks that are invoked at different points of the step including before, during, and after the animation.

The module was inspired by a similar tour called nz-tour created by Tanner Linsley and the codebase is modeled after the $ionicModal module.

For condensing purposes, the code below is not the code used in the codepen demo. For that, you may either open up the demo in codepen or visit the github repo.

bower install

First thing is to install ionic-tour via bower.

$ bower install --save ionic-tour

and include the files.

<link href="lib/ionic-tour/ionic.tour.css" rel="stylesheet">  
<script src="lib/collide/collide.js"></script>  
<script src="lib/ionic-tour/ionic.tour.js"></script>  


app.js

Next, include ionic.tour as a dependency in your module and parent controller. $ionicTour has a method called fromTemplateUrl that loads a template and returns a promise that is used to attach a new tour object to the parent scope.

angular.module('starter', ['ionic', 'ionic.tour'])  
.controller('MainCtrl', function($scope, $ionicTour) {

  $ionicTour.fromTemplateUrl('tour-template.html', {
    scope: $scope
  }).then(function(tour){
    $scope.tour = tour;
  });

});


tour-template.html

We've specified a template in the controller but we still need to create a separate html file called tour-template.html. The elements tourtip and tourtip-arrow must be defined as below but they can be styled within ionic.tour.css. Everything else is up you. In this template, I've defined a series of 5 buttons that operate as tour controls.

<tourtip>  
  <tourtip-arrow></tourtip-arrow>
    <div ng-click="next()">Next</div>
    <div ng-click="previous()">Previous</div>
    <div ng-click="finish()">Finish</div>
    <div ng-click="step(1)">goToStep 1</div>
    <div ng-click="reset()">Reset</div>
  </tourtip>


index.html

Next, we need to define the tour steps and this is done using the tour-step directive attribute. The step numbers must be incremental. I've also specified a tour.start({autoplay: true}) click event which renders the tourtip onto the DOM. You may also notice some of the elements have additional attributes such as tour-on-start which operate as callbacks that are invoked at different parts of the step animation. These are defined in app.js.

<div ng-click="tour.start({autoplay: true})">Start</div>

<div tour-step="1" tour-on-start="onStart"></div>  
<div tour-step="2"></div>  
<div tour-step="3"></div>  
<div tour-step="4" tour-on-transition="onTransition"></div>  
<div tour-step="5" tour-on-leave="onLeave></div>  
<div tour-step="6" tour-on-start="onStart" tour-on-end="onEnd"></div>  


app.js

There are several functions defined on the scope of our parent controller and you may notice that the functions that operate as tour controls such as next() or previous() in turn invoke functions on the tour object that was previously attached to the parent scope.

The attribute callbacks, however, inject different parameters that allow you to manipulate the element as you please. In this example, whenever, the tourtip goes to an element, it adds a button-assertive class (turns red) to the element and removes it whenever it leaves.

angular.module('starter', ['ionic', 'ionic.tour'])  
.controller('MainCtrl', function($scope, $ionicTour) {

  $ionicTour.fromTemplateUrl('tour-template.html', {
    scope: $scope
  }).then(function(tour){
    $scope.tour = tour;
  });

  $scope.next = function() {
    $scope.tour.next();
  }

  $scope.previous = function() {
    $scope.tour.previous();
  }

  $scope.finish = function() {
    $scope.tour.finish({
      destroy: false
    });
  }

  $scope.step = function(index) {
    $scope.tour.step(index);
  }

  $scope.reset = function() {
    $scope.tour.reset();
  }

  $scope.onStart = function(stepEl, tourtipEl) {
    angular.element(stepEl).addClass('button-outline button-assertive')
  }

  $scope.onLeave = function(stepEl, tourtipEl) {
    angular.element(stepEl).removeClass('button-outline button-assertive')
  }

  $scope.onEnd = function(stepEL, tourtipEl) {
    // do something
  }

  $scope.onTransition = function(ratio) {
    // do something
  }

})

Considerations

  1. Ionic Tour makes the assumption that the tourtip width will be a percentage of the window width and positions itself in the center. It's recommended to keep the tourtip's width between 95% to 100% so that the arrow has a full range to transition to.

  2. The tourtip may only be positioned either above or below the step element. It cannot position itself to the left or the right.

  3. Scrolling is disabled while the tour is in progress.

  4. Triggering other events outside of the tour may be done using the callback directive attributes. In the demo above, the side menu is opened when a specific step is reached.

  5. Window resize events are not currently implemented but is currently the next item up for development. (Fixed: module now accommodates windoe resize event)


Documentation

  1. $ionicTour Methods
  2. $scope.tour Methods
  3. Listeners
  4. Directive Attributes

$ionicTour Methods


fromTemplateUrl(url, options) - The module makes the assumption that the contents of the tourtip are retrived from an external template. In order to launch the module, the template must be loaded and a new tour object attached to the current scope via promise.

  • @param {string} url The url where the tooltip template is located.
  • @param {object} options Options to be passed
  • @returns {promise} A promise that will be resolved with an instance of
  • an {@link ionic.controller:ionicModal} controller.
  $ionicTour.fromTemplateUrl('tour-template.html', {
    scope: $scope
  }).then(function(tour){
    $scope.tour = tour;
  });

$scope.tour Methods


start() - Appends the tourtip to the DOM and calculates positioning for all the step elements. Automatically animates to the first step unless specified otherwise.

  • @param {object} options
  • @return {undefined}
$scope.start = function() {
  $scope.tour.start({
    autoplay: true
  });
}


finish() - Removes the tourtip from the DOM and destroys the scope unless specified.

  • @param {Object} options
  • @return {undefined}
$scope.finish = function() {
  $scope.tour.finish({
    destroy: false
  });
}


step(index) - Move and animates the tourtip to the specified step.

  • @param {number} index The index of the step
  • @return {undefined}
$scope.step = function(index) {
  $scope.tour.step(index);
}


reset() - Resets the index to 1 and animates the tourtip to the first step.

  • @return {undefined}
$scope.reset = function() {
  $scope.tour.reset();
}


next() - Changes the index to the next step and and triggers the tourtip animation to the next step element. If there are no longer any steps, tourAtEnd is broadcasted...see listeners.

  • @return {undefined}
$scope.next = function() {
  $scope.tour.next();
}


previous() - Changes the index to the previous step and and triggers the tourtip animation to the previous step element. If the index reaches 1 (beginning of steps), tourAtStart is broadcasted...see listeners.

  • @return {undefined}
$scope.previous = function() {
  $scope.tour.previous();
}

removeStep() - Removes a specified step from the tour. If the step being removed is the step the tourtip is currently positioned on, it will by default go to the previous element unless it is the first step, then the tourtip will go to the next element.

  • @param {number} index The step number to be removed
  • @return {undefined}
$scope.removeStep = function(index) {
  $scope.tour.removeStep(index);
}

sort() - Sorts the steps into sequential order. This is useful if you add a new step and it is not the the next in sequential order. Additionally, adding a step is as simple as compiling it and rendering it to the body.

  • @return {undefined}
  $scope.addStep = function() {
    var template = '<div class="button-bar padding"><a tour-step="11" tour-on-start="onStart" tour-on-leave="onLeave" class="button">11</a></div>';
    var el = $compile(template)($scope.tour.scope);

    var body = document.querySelector('.scroll');
    angular.element(body).append(el);

    $scope.tour.sort();
  }

Listeners

Ionic Tour broadcasts two listeners at the successful completion of two events, when the tour has succesfully been started and finshed.

tourStarted - Broadcasted when there are no longer any steps at the beginning of the steps array. There is also a property available on the tour object that returns a boolean if the step is the first step $scope.tour._isFirst.

$scope.$on('tourAtStart', function(){
  // something
})


tourAtEnd - Broadcasted when there are no longer any steps at the end of the steps array. There is also a property available on the tour object that returns a boolean if the step is the last step $scope.tour._isLast.

$scope.$on('tourAtEnd', function(){
    // something
})

Directive Attributes


tour-step {=} - Accepts an integer. Identifies the element as the element to reference in a that particular step. Steps should start at 1 and increment by 1 for each additional step.

<div tour-step="1"></div>  
<div tour-step="2"></div>  
<div tour-step="3"></div>  


Callbacks

A set of 5 callbacks are available through directive attributes and are invoked at different times as the tourtip moves from one step to another. Each example offers a snippet on how to include attribute in the markup and also how to declare the callback (as well as parameters) in the controller. They are listed in order of invocation.

tour-on-enter {=} - Invoked before the tooltip animates to the next step element.

  • @param {object} stepEl The element of the current step
  • @param {object} tourtipEl The tooltip element
  • @return {undefined}
<div tour-step="1" tour-on-enter="onEnter"></div>  
$scope.onEnter = function(stepEl, tourtipEl) {
  // do something
}


tour-on-start(stepEl, tourtipEl) {=} - Invoked directly before the tooltip animates to the step element.

  • @param {object} stepEl The element of the current step
  • @param {object} tourtipEl The tooltip element
  • @return {undefined}
<div tour-step="1" tour-on-start="onStart"></div>  
$scope.onStart = function(stepEl, tourtipEl) {
  // do something
}


tour-on-transition {=} - Invoked repeatedly as the tooltip animates toward the next step element. The first parameter, ratio, indicates the percentage of progression of the animation.

  • @param {number} ratio Percentage of animation completion (0 to 1)
  • @param {object} stepEl The element of the current step
  • @param {object} tourtipEl The tooltip element
  • @return {undefined}
<div tour-step="1" tour-on-transition="onStart"></div>  
$scope.onTransition = function(ratio, stepEl, tourtipEl) {
  // do something
}


tour-on-end {=} - Invoked directly after the tooltip fully animates to the next step element.

  • @param {object} stepEl The element of the current step
  • @param {object} tourtipEl The tooltip element
  • @return {undefined}
<div tour-step="1" tour-on-end="onEnd"></div>  
$scope.onEnd = function(stepEl, tourtipEl) {
  // do something
}


tour-on-leave {=} - Invoked on the element after animation but before the tourtip moves to the next step element.

  • @param {object} stepEl The element of the current step
  • @param {object} tourtipEl The tooltip element
  • @return {undefined}
<div tour-step="1" tour-on-leave="onLeave"></div>  
$scope.onLeave = function(stepEl, tourtipEl) {
  // do something
}


Demo GitHub