Skip to Content
Technical Articles

Surprise your users with a true x-mas user experience: let it snow

It’s getting colder and colder and Christmas is nearing. While we keep our bodies warm with winter jackets, hats and scarves, we warm our hearts with that special, warm Christmas feeling. That feeling however, is rarely related to anything IT. But what if I tell you, you may surprise your Fiori users with just that; they login, and the Fiori Launchpad is snowing…

 

Snowing Effect Credit

First of I’d like to point out that the snowing effect itself has not been created by me and was just found by me via codepen.io. Please see the original snow effect javascript and author here: snowing effect @ codepen.

Developing a “special” Fiori Launchpad Plugin

You may guessed it; I’m talking about a plugin for the Fiori Launchpad. To develop a plugin in Fiori is very similar to developing an app, with a few differences. Let’s go through all the steps.

Prerequisite

First of, as a prerequisite, in your SAP Web IDE you must enable following extension: SAP Fiori Launchpad Extensibility

Step 1: Create a project for the plugin

Create a new project from template and choose the now available “SAP Fiori Launchpad Plugin” template. More details can be found at SAP Help. This will generate all necessary files for the plugin, the most important one being Component.js, similar to an app.
I called my project Snow with namespace snow.

Step 2: “Convert” codepen original to Fiori

Now we don’t just have access to direct HTML files and especially the plugin, is something that is loaded into the Fiori Launchpad shell – so we can’t just copy-n-paste what we have in codepen. Instead we need to turn the snowing effect (or rather all the snowflakes) into an object we can call. I solved it the following way:
I created a new folder in the project called controller and a file Flake.js – my snowflake controller. Flake.js gets most of the javascript code from codepen, but “fiorified”, like below:

sap.ui.define([
	"sap/ui/base/Object"
], function(Object) {
	"use strict";

	return Object.extend("snow.controller.Flake", {
		
		init: function(x,y) {
			var maxWeight = 5,
			    maxSpeed = 3;
			    
			this.x = x;
			this.y = y;
			
			this.r = this.randomBetween(0, 1);
			this.a = this.randomBetween(0, Math.PI);
			
			this.aStep = 0.01;
			this.weight = this.randomBetween(2, maxWeight);
			this.alpha = (this.weight / maxWeight);
			this.speed = (this.weight / maxWeight) * maxSpeed;
		},
		
		randomBetween: function(min, max, round) {
			var num = Math.random() * (max - min + 1) + min;
			if (round) {
				return Math.floor(num);
			} else {
				return num;
			}
		},
		
		distanceBetween: function(vector1, vector2) {
			var dx = vector2.x - vector1.x,
				dy = vector2.y - vector1.y;
			return Math.sqrt(dx * dx + dy * dy);
		},
		
		update: function() {
			this.x += Math.cos(this.a) * this.r;
			this.a += this.aStep;
			this.y += this.speed;
		}
		
	});
});

Simply put: what we had in codepen as functions are now instead methods of our Flake object and global variables are instead object properties.
Please note: you won’t find the init or the loop function from the original in above code: init creates all the snowflakes and loop displays and animates them: both we cannot do from the controller directly.

Step 3: Create Flake objects to create snowing effect

So far we just have a controller that can simulate snowflakes. Now we need to use this object and display the snowflakes in our Fiori Launchpad. For this we have to modify the existing Component.js file.

3.1 Import Flake controller

sap.ui.define([
	"sap/ui/core/Component",
	"sap/ui/Device",
	"snow/controller/Flake"
], function (Component, Device, Flake) {

	return Component.extend("snow.Component", {

First step: we need access to our Flake controller (line 4 and 5 above) as well as the Device object.

3.2 Create all snowflakes for the snowing effect:

Inside the init() lifecycle method of our Component, we can now do what the original did in its init function (more or less):

init: function () {
	//var rendererPromise = this._getRenderer();
	
	// define snow variables
	this._numFlakes = 200;
	this._windowW = Device.resize.width; //window.innerWidth;
	this._windowH = Device.resize.height; //window.innerHeight;
	this._flakes = [];
	
	// get canvas to paint in: there seems to be only one in 
	// in the Fiori Launchpad so this should always work
	var canvas = $("canvas").get(0); 
	if(!canvas) {
		// stop processing in case we don't have a canvas!
		return; 
	}
	this._ctx = canvas.getContext("2d");
	
	// first loop to create all Flake objects
	var i = this._numFlakes, flake, x, y;
	while (i--) {
		// create new flake
		flake = new Flake();
		// get random location
		x = flake.randomBetween(0, this._windowW, true);
		y = flake.randomBetween(0, this._windowH, true);
		flake.init(x, y);
		// add flake
		this._flakes.push(flake);
	}
	// start looping all flakes to move them
	this.loop();
},

You may noticed that the code now deviates quite a bit from the original. The main reason -as said- is that we don’t just have an HTML file to enhance but instead we work with our plugin’s lifecycle methods. Reading the comments provided in the code above should explain how the code changed and why it needs to be in this order.
One important thing to understand is the jQuery code to get the canvas:

var canvas = $("canvas").get(0);

What the above simply does, is searching the rendered HTML for any canvas tags: <canvas>. Luckily for us, the Fiori Launchpad indeed uses a canvas and that is in fact the only reason, why we can convert this codepen example into a Fiori plugin. The Flake controller we created gives the snowflakes a position and can change it and the loop function (next step) draws them into the canvas. Without the canvas we cannot draw nor animate the snowflakes, to create the snowing effect. That’s why the plugin will not do anything, if no canvas object in the rendered HTML code was found!

3.3 Let it snow: display and move the snowflakes

The very last code bit above calls this.loop() which is an equivalent to the loop function in the original, for us, added as a method to the Component.js:

loop: function() { 
	var i = this._flakes.length,
		flakeA;
	
	// clear canvas
	this._ctx.save();
	this._ctx.setTransform(1, 0, 0, 1, 0, 0);
	this._ctx.clearRect(0, 0, this._windowW, this._windowH);
	this._ctx.restore();
	
	// loop through the flakes and "animate" them
	while (i--) {
		flakeA = this._flakes[i];
		flakeA.update();
		
		this._ctx.beginPath();
		this._ctx.arc(flakeA.x, flakeA.y, flakeA.weight, 0, 2 * Math.PI, false);
		this._ctx.fillStyle = "rgba(255, 255, 255, " + flakeA.alpha + ")";
		this._ctx.fill();
		
		if (flakeA.y >= this._windowH) {
			flakeA.y = -flakeA.weight;
		}  
	}
	// continue animation...
	requestAnimationFrame( this.loop.bind(this) );
},

And here we are: a snowing effect drawn onto the Fiori Launchpad canvas.

Step 4: Test the plugin!

Before we fully leave development, we of course should test the plugin and make sure it’s running and -most importantly- doesn’t cause any errors. Testing a plugin is a bit different from an app: we have to run a sandbox launchpad (which the template wizard already took care of), which will load our plugin: please find a detailed guide here at SAP Help.

Use your Plugin

Once you tested the plugin and everything works fine, you can deploy your plugin and add it to your Fiori Lauchpad. Depending on your system landscape you may need to do different steps, but find below an example for using SAP Cloud Platform.

Deploy the Plugin to SAP Cloud Platform

This blog post also has a guide (at the very end) on how to add a plugin to a SAP Cloud Platform Fiori Launchpad Portal site.

24 Comments
You must be Logged on to comment or reply to a post.
  • Thanks for this Meli –  I’m one of those people who look forward to December 1st in order to convince my family to put up the Christmas Tree so love this plug-in (which was easy to deploy following your instructions). I’ve now lined it up to activate for end users the week before Christmas (even though it’s Summer here and very unlikely to have snow in Tasmania).

    (Almost time to say) Happy Holidays,

    Matt

    ps. Maybe a backport for Web Dynpro or Enterprise Portal is required for Jelena Perfiljeva (might look a little more like the Matrix if it was done for SAPGUI)?

  • Hey Meli,

    This is amazing blog and worked perfectly fine in webide ( local run configuration).

    But when same was deployed on FLP, it is not working, as below line is undefined.

    var canvas = $("canvas").get(0); 

    The reason is probably due to absence of “canvas” tags.

    As mentioned in your blog, it is mandatory to have canvas tags, I explicitly tried to add “canvas tags” using below statement .

    	$("#shell").html('<canvas height="286" width="1280" aria-hidden="true" style="position:absolute;z-index:-1"></canvas>');
    	

    Still it didn’t work, probably Fiori 2.0 does not provide canvas element but Fiori 3.0 does.

     

    Could you suggest something?

    Regards

    Kiran

    • me too have same issue…i am on fiori 2.0

       

      try this..it works..

       

      $(“#shell-cntnt”).prepend(‘<canvas id=”shell-cntnt” height=”762″ width=”1470″ aria-hidden=”true” style=”position:absolute;z-index:-1″></canvas>’);

    • Hi Kiran,

      That’s true, I’m not exactly sure at which version the Fiori Launchpad got the canvas tag. However, I’m pretty sure it’s still Fiori 2.0 but maybe a higher UI5 version you need? Have you looked at the HTML of your Fiori Launchpad with a browser inspector? For example in Google Chrome you can right-click and inspect, once logged into your Fiori Launchpad. Then you can search for “<canvas” or “canvas”. Please see an example below of my Fiori Launchpad (which is not  the webide and not Fiori 3.0):

      If however there is indeed no canvas, check the highlighted divs above, which basically were used alone before the canvas. You may try to change the jQuery to add a canvas tag to those (which is already in another reply you got). However, please don’t do it as in your examples; don’t hard-code height and width of your canvas, since your users may use different devices with different screen sizes etc.).

      You can find in the Component.js code the height and width for the snowflakes, so they don’t appear out of screen (no point in that, right 😉 ). So you may use that instead, meaning the properties we already set in init() method:

      this._windowW // width
      this._windowH // height
      "<canvas height='" + this._windowH + "' width='" + this._windowW + "' ...>"
      /
  • Hey Meli

    As far as I investigated, canvas tag is available when we test locally on webide.

    Once we deploy our application, canvas tags are not available.

    Did you try, deploying your app? The one shown in screenshot attached, st Fiori 3.0. This I am guessing, coz user icon is on right hand side in Fiori 3 and left side in Fiori 2. Well it doesn’t matter what is the version, as of now, canvas tags are available in webide, but not on flp.

     

    Also, I changed the version from Project Settings in webide, to very old version (say 1.50). Snow effect is working perfectly fine there, because of canvas tags.

    Can you check by deploying your app, and search for canvas tags?

     

    And yes, thanks for the idea of dynamic height and width for canvas tags.  It is appreciated :). But as, in my example, adding a canvas tag, as of now is not working. Will try again by adding using jquery, and will update.

    Thanks and Regards

    Kiran

     

    /
    • Hi again Kiran,

      The screenshot shown is the deployed app already, so maybe you are right with Fiori 3.0. I have no control over the version of my environment and since I’m using a demo one, it may be upgraded to Fiori 3.0 already.

      Regarding the jQuery, did you try the suggestion from Prabhuarul Pancras‘s comment/reply above?

  • I just implemented it in my system and it works like a charm. Good Job!

    We have Fiori 2.0 UI Version 1.60.18

    I faced an small issue when using a custom theme the snow was not visible. I added this line: canvas.style[“z-index”] = “0”;

    Thanks, Jordi

  • Awesome!

    I had a bit of a play with this and added some CSS tweaks, to make it look nice in Fiori 3

    Did notice it uses quite a bit of CPU-time. Maybe some of the animations can be pushed to the GPU?

    /
    • Nice one, looks great! Thanks for sharing.

      The animation drawing does take a bit, yes. As I’m not the author of the drawing itself, I’m not sure if it could be pushed to the GPU. However you could change the number of snow flakes being drawn and how much they move in the code. That should lessen CPU usage. You find below at the start of your init method:

      this._numFlakes = 200;
  • Hi Meli,

    It’s just amazing.

    But, when i add footer methods like setFooterControl, setFooter to set footer on my launchpad. The snow effect goes away while all other methods run fine.

    This code gets Auto generated while creating launchpad plugin project –

    var rendererPromise = this._getRenderer();

    rendererPromise.then(function (oRenderer) {
    oRenderer.setFooterControl(“sap.m.Toolbar”, {
    id: “myFooter”,
    content: [new sap.m.Button({
    text: “Important Information”,
    press: function () {
    MessageToast.show(“This SAP Fiori Launchpad has been extended to improve your experience”);
    }
    })]
    });
    });

    After, commenting above code, snow effect works fine.

    Does, anyone tried and succeeded in this.

    Please help me t out to solve this as i need both snow effect+ footer on my launchpad.

    Thanks & Regards,

    Ambuj

     

     

    • Hi,

      I also commented away the “setFooterControl” auto generated code and I only have the code as explained in the blog in my plugin. If you need another plugin in your launchpad, for the footer for example as the auto-generated code shows, I recommend having a separate plugin for that and keep the snowing effect plugin as just that, and don’t mix different plugins within one plugin code.

      /Meli