Dynamic Backgrounds

Animating a Dynamic Background with an SVG in Ionic



·

Recently I’ve been writing some content and filming some videos on SVGs (Scalable Vector Graphics), mainly because they are freakin’ cool.

The content I have been creating is mostly pretty introductory and in the case of animations I have just been using simple CSS transitions and keyframes, so I wanted to tackle a more real-world problem and create something that actually looks really cool.

What I wanted to create was a background for an application that would have a graphical representation of the time of day or the weather. It could be a midday and sunny, cloudy, nighttime, or even a sunset (kind of like the default weather application on iOS) – an SVG is a perfect candidate for this. Since we can manipulate parts of the SVG with Javascript, we can work from a single SVG and modify it on the fly. If the original SVG is a sunny day with clouds in the sky, we could remove the sun and change the colour of the sky to make it night time, or we could move the sun to the bottom of the screen and make the background orange to create a sunset effect.

Here’s what I ended up creating:

Time of Day Animation in Ionic

It’s quite a basic implementation, but it’s pretty cool, right? In the future, I’d like to extend this so that it automatically adjusts for time and weather, but right now it allows you to switch between various pre-defined settings (Day, Night, Sunset, and Cloudy).

In this tutorial, I am going to walk through how I created this dynamic background using an SVG in Ionic. I won’t walk through the actual SVG creation, just modifying it, so if you are unfamiliar with how an SVG is created I’d recommend watching this first: SVG Animations in Ionic.

Before We Get Started

Last updated for Ionic 3.9.2

Before you go through this tutorial, you should have at least a basic understanding of Ionic concepts. You must also already have Ionic set up on your machine.

If you’re not familiar with Ionic already, I’d recommend reading my Ionic Beginners Guide or watching my beginners series first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic, then take a look at Building Mobile Apps with Ionic.

1. Generate a New Ionic Application

We’re going to start by generating a new blank Ionic application, to do that just run the following command:

ionic start ionic-sunset blank

Once that has finished generating, you should make it your working directory:

cd ionic-sunset

We will just be adding the SVG animation to the Home Page which is automatically generated, so there is no more set up required.

2. Set up the SVG

Before we do anything to it, this is what the SVG looks like that I created in Illustrator:

SVG Screenshot going to embed that directly into the application.

Modify src/pages/home/home.html to reflect the following:

<ion-content>

    <svg class="background" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 750 1334">
      <defs>
        <style>
          .cls-1 {
            fill: url(#linear-gradient);
          }

          .cls-2 {
            fill: #fefbdf;
            opacity: 0.07;
          }

          .cls-3 {
            fill: #ffda71;
          }

          .cls-4, .cls-5 {
            fill: #fff;
          }

          .cls-4 {
            opacity: 0.95;
          }
        </style>
        <linearGradient id="linear-gradient" x1="375" y1="1334" x2="375" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#fff"/>
          <stop offset="1" stop-color="#50a7dd"/>
        </linearGradient>
      </defs>
      <title>sunsetsvg</title>
      <rect id="Sky-2" data-name="Sky" class="cls-1" width="750" height="1334"/>
      <g id="Sun" data-name="Sun">
        <circle class="cls-2" cx="385" cy="335.7" r="315.74"/>
        <circle class="cls-3" cx="385" cy="335.7" r="180.56"/>
      </g>
      <g id="Clouds">
        <path id="Cloud" class="cls-4" d="M303.24,920.47c9.53-6.84,15.35-16,15.35-26,0-18.94-20.8-34.66-48.16-37.83,0-.2,0-0.39,0-0.59,0-28.13-29.43-50.93-65.74-50.93-31.76,0-58.27,17.45-64.4,40.65A156.9,156.9,0,0,0,116.74,844c-58.3,0-105.56,31.51-105.56,70.37s47.26,70.37,105.56,70.37c22.56,0,43.45-4.72,60.61-12.75,10.15,19.11,43.41,33.12,82.91,33.12,47.56,0,86.11-20.31,86.11-45.37C346.37,943,329,928.31,303.24,920.47Z"/>
        <path id="Cloud-2" data-name="Cloud" class="cls-5" d="M714.81,520.89c0-19.94-38.95-36.37-89.12-38.62,3.74-4.4,5.79-9.17,5.79-14.16,0-22-39.8-39.81-88.89-39.81-45.55,0-83.1,15.35-88.27,35.13-13.56-5.18-30.92-8.51-50-9.1-10.25-10.15-27-16.77-45.95-16.77-31.19,0-56.48,17.93-56.48,40,0,0.42,0,.84,0,1.26-19.2,9.08-31.53,23.07-31.53,38.79,0,27.36,37.31,49.54,83.33,49.54,20.06,0,38.45-4.21,52.83-11.23a33.31,33.31,0,0,0,35.73,9.44c11.07,22.73,38.54,38.83,70.7,38.83,20,0,38.26-6.26,51.83-16.47,8.79,9.95,23.5,16.47,40.17,16.47,26.92,0,48.74-17,48.74-38a30.14,30.14,0,0,0-1.5-9.38C688.94,551.06,714.81,537.14,714.81,520.89Z"/>
        <path id="Cloud-3" data-name="Cloud" class="cls-4" d="M303.24,351c9.53-6.84,15.35-16,15.35-26,0-18.94-20.8-34.66-48.16-37.83,0-.2,0-0.39,0-0.59,0-28.13-29.43-50.93-65.74-50.93-31.76,0-58.27,17.45-64.4,40.65a156.9,156.9,0,0,0-23.56-1.76c-58.3,0-105.56,31.51-105.56,70.37s47.26,70.37,105.56,70.37c22.56,0,43.45-4.72,60.61-12.75,10.15,19.11,43.41,33.12,82.91,33.12,47.56,0,86.11-20.31,86.11-45.37C346.37,373.53,329,358.87,303.24,351Z"/>
        <path id="Cloud-4" data-name="Cloud" class="cls-5" d="M657.61,1201.29c9.53-6.84,15.35-16,15.35-26,0-18.94-20.8-34.66-48.16-37.83,0-.2,0-0.39,0-0.59,0-28.13-29.43-50.93-65.74-50.93-31.76,0-58.27,17.45-64.4,40.65a156.9,156.9,0,0,0-23.56-1.76c-44.93,0-83.29,18.71-98.53,45.08-30.67,13.75-50.29,34.78-50.29,58.36,0,41.42,60.52,75,135.19,75,41.79,0,79.14-10.52,103.94-27,14.64,6.08,33.12,9.71,53.21,9.71,47.56,0,86.11-20.31,86.11-45.37C700.74,1223.79,683.38,1209.13,657.61,1201.29Z"/>
      </g>
    </svg>

</ion-content>

Let’s break the important components down in the SVG above. We have a linearGradient defined that we are using for the sky – this gradient transitions from blue to white which allows us to just change one colour (i.e. changing the blue top to orange will give a sunset effect). This gradient is then applied to the Sky-2 rectangle which covers the entire background.

Next, we have a group of circles that makes up the sun – there is the sun itself and then a larger circle around the sun that gives it a bit of a glow/lens flare effect.

Then we have a group of three clouds that are defined with custom paths. We will be getting references to all of these objects and modifying them in some way (either their style or position) to achieve the result we want.

3. Add the Time

Before we get into manipulating the SVG itself we are going to overlay the time onto the SVG and set up some styling. Although there isn’t much time functionality built into the application yet, we are going to use the date-fns package to help format time for us. I haven’t had a lot of time to play around with date-fns yet, but it seems to be a much more lightweight alternative to moment for managing time.

To install date-fns you will need to run the following command.

npm install date-fns --save

Then we will set up everything we need in the home.ts file.

Modify src/pages/home/home.ts to reflect the following:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import * as dateFns from 'date-fns';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    timeOfDay: Date = new Date();
    timeString: string = '12:00';

    constructor(public navCtrl: NavController) {

    }

    ionViewDidLoad(){

        this.timeString = dateFns.format(this.timeOfDay, 'h:mm A');

    }

}

We have imported the functionality from the date-fns package, and then we are using that to format our Date object into a string that will look like this: 7:35 AM.

Now let’s add that to our template.

Modify src/pages/home/home.html to reflect the following:

<ion-content>

    <div class="controls">
        <button color="light" ion-button (click)="setDay()">Day</button>
        <button color="light" ion-button (click)="setCloudy()">Cloudy</button>
        <button color="light" ion-button (click)="setSunset()">Sunset</button>
        <button color="light" ion-button (click)="setNight()">Night</button>
    </div>

    <svg class="background" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 750 1334">
      <defs>
        <style>
          .cls-1 {
            fill: url(#linear-gradient);
          }

          .cls-2 {
            fill: #fefbdf;
            opacity: 0.07;
          }

          .cls-3 {
            fill: #ffda71;
          }

          .cls-4, .cls-5 {
            fill: #fff;
          }

          .cls-4 {
            opacity: 0.95;
          }
        </style>
        <linearGradient id="linear-gradient" x1="375" y1="1334" x2="375" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#fff"/>
          <stop offset="1" stop-color="#50a7dd"/>
        </linearGradient>
      </defs>
      <title>sunsetsvg</title>
      <rect id="Sky-2" data-name="Sky" class="cls-1" width="750" height="1334"/>
      <g id="Sun" data-name="Sun">
        <circle class="cls-2" cx="385" cy="335.7" r="315.74"/>
        <circle class="cls-3" cx="385" cy="335.7" r="180.56"/>
      </g>
      <g id="Clouds">
        <path id="Cloud" class="cls-4" d="M303.24,920.47c9.53-6.84,15.35-16,15.35-26,0-18.94-20.8-34.66-48.16-37.83,0-.2,0-0.39,0-0.59,0-28.13-29.43-50.93-65.74-50.93-31.76,0-58.27,17.45-64.4,40.65A156.9,156.9,0,0,0,116.74,844c-58.3,0-105.56,31.51-105.56,70.37s47.26,70.37,105.56,70.37c22.56,0,43.45-4.72,60.61-12.75,10.15,19.11,43.41,33.12,82.91,33.12,47.56,0,86.11-20.31,86.11-45.37C346.37,943,329,928.31,303.24,920.47Z"/>
        <path id="Cloud-2" data-name="Cloud" class="cls-5" d="M714.81,520.89c0-19.94-38.95-36.37-89.12-38.62,3.74-4.4,5.79-9.17,5.79-14.16,0-22-39.8-39.81-88.89-39.81-45.55,0-83.1,15.35-88.27,35.13-13.56-5.18-30.92-8.51-50-9.1-10.25-10.15-27-16.77-45.95-16.77-31.19,0-56.48,17.93-56.48,40,0,0.42,0,.84,0,1.26-19.2,9.08-31.53,23.07-31.53,38.79,0,27.36,37.31,49.54,83.33,49.54,20.06,0,38.45-4.21,52.83-11.23a33.31,33.31,0,0,0,35.73,9.44c11.07,22.73,38.54,38.83,70.7,38.83,20,0,38.26-6.26,51.83-16.47,8.79,9.95,23.5,16.47,40.17,16.47,26.92,0,48.74-17,48.74-38a30.14,30.14,0,0,0-1.5-9.38C688.94,551.06,714.81,537.14,714.81,520.89Z"/>
        <path id="Cloud-3" data-name="Cloud" class="cls-4" d="M303.24,351c9.53-6.84,15.35-16,15.35-26,0-18.94-20.8-34.66-48.16-37.83,0-.2,0-0.39,0-0.59,0-28.13-29.43-50.93-65.74-50.93-31.76,0-58.27,17.45-64.4,40.65a156.9,156.9,0,0,0-23.56-1.76c-58.3,0-105.56,31.51-105.56,70.37s47.26,70.37,105.56,70.37c22.56,0,43.45-4.72,60.61-12.75,10.15,19.11,43.41,33.12,82.91,33.12,47.56,0,86.11-20.31,86.11-45.37C346.37,373.53,329,358.87,303.24,351Z"/>
        <path id="Cloud-4" data-name="Cloud" class="cls-5" d="M657.61,1201.29c9.53-6.84,15.35-16,15.35-26,0-18.94-20.8-34.66-48.16-37.83,0-.2,0-0.39,0-0.59,0-28.13-29.43-50.93-65.74-50.93-31.76,0-58.27,17.45-64.4,40.65a156.9,156.9,0,0,0-23.56-1.76c-44.93,0-83.29,18.71-98.53,45.08-30.67,13.75-50.29,34.78-50.29,58.36,0,41.42,60.52,75,135.19,75,41.79,0,79.14-10.52,103.94-27,14.64,6.08,33.12,9.71,53.21,9.71,47.56,0,86.11-20.31,86.11-45.37C700.74,1223.79,683.38,1209.13,657.61,1201.29Z"/>
      </g>
    </svg>

    <div class="time">{{timeString}}</div>
</ion-content>

We’ve added the time string at the bottom, and we’ve also added some buttons that we will use to trigger our SVG functions that we will create in the next step.

Now we just need to add a little styling so everything displays as it should.

Modify src/pages/home/home.scss to reflect the following:

page-home {

    .scroll-content {
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .time {
        z-index: 1;
        font-size: 3em;
        font-weight: bold;
        color: #fff;
        text-shadow: 1px 1px 2px #8e8e8e;
    }

    .background {
        position: absolute;
    }

    .controls {
        position: absolute;
        top: 10px;
        z-index: 1;
        opacity: 0.2;
    }

}

4. Manipulate the SVG

Now for the fun part. We are going to create some functions that will modify what the SVG looks like. To do that, we are going to need to grab references to the different elements of our SVG, and then use Angular’s Renderer to modify the styles and attributes on those elements.

We will add everything now and then walk through it.

Modify src/pages/home/home.ts to reflect the following:

import { Component, Renderer } from '@angular/core';
import { NavController } from 'ionic-angular';
import * as dateFns from 'date-fns';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

    timeOfDay: Date = new Date();
    timeString: string = '12:00';

    sky: any;
    entireSun: any;
    allClouds: any;
    clouds: any;

    constructor(public navCtrl: NavController, public renderer: Renderer) {

    }

    ionViewDidLoad(){

        this.timeString = dateFns.format(this.timeOfDay, 'h:mm A');

        this.sky = document.querySelector('linearGradient [offset="1"]');
        this.entireSun = document.querySelector('#Sun');
        this.clouds = document.querySelectorAll('#Clouds path');

        this.setTransitions();

    }

    setTransitions(){

        this.renderer.setElementStyle(this.sky, 'transition', '1s linear');
        this.renderer.setElementStyle(this.entireSun, 'transition', '1s linear');

        this.clouds.forEach((cloud) => {
            this.renderer.setElementStyle(cloud, 'transition', '1s linear');
        });

    }

    setNight(){

        this.renderer.setElementAttribute(this.sky, 'stop-color', '#141944');
        this.renderer.setElementAttribute(this.entireSun, 'transform', 'translate(1, 2000)');
        this.renderer.setElementAttribute(this.entireSun, 'opacity', '1');

        this.clouds.forEach((cloud) => {
            this.renderer.setElementStyle(cloud, 'fill', '#fff');
            this.renderer.setElementStyle(cloud, 'opacity', '0.2');
        });

    }

    setDay(){

        this.renderer.setElementAttribute(this.sky, 'stop-color', '#50a7dd');
        this.renderer.setElementAttribute(this.entireSun, 'opacity', '1');
        this.renderer.setElementAttribute(this.entireSun, 'transform', 'translate(1, 1)');

        this.clouds.forEach((cloud) => {
            this.renderer.setElementStyle(cloud, 'fill', '#fff');
            this.renderer.setElementStyle(cloud, 'opacity', '1');
        });

    }

    setSunset(){

        this.renderer.setElementAttribute(this.entireSun, 'transform', 'translate(1, 1000)');
        this.renderer.setElementAttribute(this.entireSun, 'opacity', '1');
        this.renderer.setElementAttribute(this.sky, 'stop-color', '#e2905a');

        this.clouds.forEach((cloud) => {
            this.renderer.setElementStyle(cloud, 'fill', '#e2c1d8');
            this.renderer.setElementStyle(cloud, 'opacity', '0.4');
        });

    }

    setCloudy(){
        this.renderer.setElementAttribute(this.sky, 'stop-color', '#cecece');
        this.renderer.setElementAttribute(this.entireSun, 'transform', 'translate(1, 1)');
        this.renderer.setElementAttribute(this.entireSun, 'opacity', '0.2');

        this.clouds.forEach((cloud) => {
            this.renderer.setElementStyle(cloud, 'fill', '#fff');
            this.renderer.setElementStyle(cloud, 'opacity', '1');
        });
    }

}

In the ionViewDidLoad function we set up our references to the elements we need using querySelector – since we have multiple clouds we use querySelectorAll for those instead which will return an array of elements. We also call the setTransitions function which gives all of our elements a transition property that is set to 1s linear. This is so that rather than the SVG changing instantly, it will slowly animate to its new style/position.

Then we just have our various functions to control what the SVG looks like. They are all mostly the same so let’s just take a look at one example in particular. The setSunset function first sets the transform property on the sun such that the sun moves towards the bottom of the screen. Since one of the other functions reduces the opacity of the sun, we also make sure that the opacity is set back to 1. We modify the stop-color on the linear gradient so that it is orange, and then we loop through all of our clouds to change their colour to a more pinkish colour and also reduce their opacity.

If you were to open the application now, it should look something like this:

Time of Day Animation in Ionic

Summary

Given the impressive effect this creates, the actual implementation is really quite simple and I’m sure there are plenty more styles and combinations you could come up with to make even more interesting time/weather effects. This example is a hard coded demonstration, but it wouldn’t be too much work to modify this so that it animates automatically based on the time of day or even the weather.

What to watch next...