JSignage:Interactivity

From SpinetiX Support Wiki

Jump to: navigation, search

Introduction

The jSignage library provides both high-level interactive layer functions and low-level layer functions that can be used for building interactive projects for HMP devices.

Interactivity is typically achieved by installing handlers (callbacks) for input events (such as mouse events, key events, shared variable update events) and performing one or more of these actions from the handler:

  • Hiding or showing a layer.
    This is done simply by calling the .show(), .hide() or .setVisible() functions common to all layers.
    Applicable in or out animation effects are triggered automatically, and all animations inside the layer are restarted as well.
  • Switching page inside a multi-page layer.
    This causes the current content to disappear and be replaced by a new page of content, applying a transition effect as required.
    Note that the trigger for the change can be any type of event, including a click on a button inside the page that is about to be switched out.
  • Pushing new data to a slideshow, playlist, crawler, textTicker or textBar layer using the .pushData( ) function.
    This will turn on the layer, as if doing a .show() if it is not currently displaying anything.
    If it is already displaying something, the new items will be put at the end of the current data set.
  • Triggering a navigation action on a carousel layer.
    For instance, moving to the next or previous slide.
  • Pausing or resuming media content.
    Call the .pause() or .resume() functions common to all layers.
  • Doing an AJAX request to an HTTP server with the user input.
    The response handler will then call methods to modify the content accordingly.
  • Sending requests to an attached device via the RS232 port or USB I/O.
  • Modifying shared variables for synchronized actions on multiple devices.
    When the interactive application is running on a wall of monitors, it is necessary to add intermadiary steps through shared variables to synchronise all the displays.
    When the application uses only one screen, this is typically not required.

Interactive layers

The following types of layers are included into jSignage for building interactive content:

More types can be added by extending the jSignage library with new constructors.

Installing handlers

It is possible to install interactivity handler in a lot of different ways:

  • .focusin(), .focusout() for focus sensitive layers.
  • .textInput() for focus sensitive layers, or on the top-level $('svg'). See SVG Tiny 1.2 uDOM for more details on the .textInput()
    In contrary to .keydown() and .keyup() that return the keyIdentifier (U+0041 for 'A') and some modified flags (shiftKey=true), the .textInput() returns a data that contains the resulting pressed key ('a' or 'A'). However keys such as 'Enter' or 'Escape' will not trigger any .textInput() events.
  • Shared variables changes, cause by events from serial port devices, USB I/O devices and the network. See Shared variables for more details.

Examples

See also Interactive projects and Tutorial:Adding a mouse cursor pages.

Add/remove a video on click

This example shows how you can easily add & remove a video with a mouse click. The first click adds the video (which starts to play immediately full-screen) to the svg element and the next one removes it.

$(function(){
  var myVideo = null;
  $('svg').click( function(){
    if ( myVideo == null ){
      myVideo = $.video( { href: 'video.avi', repeatCount: 'indefinite', begin: 'indefinite' } );
      myVideo.addTo('svg').begin();
    } else {
      myVideo.removeAfter().end();
      myVideo = null;
    }					
  });
});

Add/remove a video in a multi-screen project

The above example works very well on a normal project, but not in a synchronized content type of project (for instance, a video wall). For this case, the shared variables mechanism must be used to ensure that the video is added / removed in the same time on all the players to keep the synchronization.

In this example, one of the players is the shared variables server (must have the Network API server enabled) and all the other players will get updates from the "master" player whenever a click is received (the "master" must have the "Enable events" option enabled).

  • If a "slave" player is restarting, it will automatically get the last value of the control shared variable from the "master" and thus continue to be in sync with the others.
  • If the "master" restarts, it will send a reset update to all the "slaves". Make sure to remove the begin attribute from the index file, otherwise this update will be sent in the past and therefore ignored by the other players.
  • The video is started with a small delay (1.5s) to compensate the rendering latency of the players.

When using this script, make sure to change:

  • MASTER_IP with the IP address of your "master" player (e.g. 192.168.1.10), optionally followed by a TCP port if the port number on the server is different from the port number on any of the "slaves" players (e.g. 192.168.1.10:1234);
  • screen-1-1 with the multiscreen ID of your "master" player.
var varName = 'click@MASTER_IP';
var showVideo = 0;

$(function() {
  var myGroup = $.createElement('g', {id: 'myGroup'}).addTo('svg');

  var v = createSharedVariable( varName );
  
  if (MULTI_SCREEN_ID == 'screen-1-1'){
    // This code gets executed only by the master and is useful in case of a master restart.
    // Make sure to remove the begin attribute from the index file, otherwise this update
    // will be sent in the past and therefore ignored by the other players.
    v.set('0');
  }
  
  $('svg').mousedown( function(){
    showVideo = ( showVideo ? 0 : 1 );
    v.set(showVideo);
  } );

  v.addUpdateListener( function(v) {
    showVideo = parseInt(v.value);
    if ( showVideo ){
      $.video( { id:'myVideo', href: 'media/video.mp4', repeatCount: 'indefinite', begin: 'indefinite' } )
       .addTo(myGroup) // Add the video to the group.
       .begin( 1.5 ); // Start the video with a small delay to compensate the rendering latency.
    } else {
      myGroup.text(""); // Remove the video(s).
    }                                         
  });
});

Play/pause a media

Play and pause a video by clicking on it. The first click pauses the video and the next one restarts it.

$(function(){
    var paused = false;
    $.media( { href: 'media/video.mp4', repeatCount: 'indefinite' } )
            .click( function() {
                    if ( paused )
                        $(this).resume();
                    else
                        $(this).pause();
                    paused = !paused;
                } )
            .addTo('svg');
});

Note:

  • This example is using the $.media() to display a fullscreen video.

Controlling media using keystrokes

The following examples demonstrates how to play, pause, hide and show a video using keystrokes. The first example is using the keydown handler and the second is using the textInput handler.

$(function(){
    var media = $.media( { href: 'video.mp4', repeatCount: 'indefinite' } ).addTo('svg');
    
    // The list of keyIdentifier  can be found at http://www.w3.org/TR/SVGTiny12/svgudom.html#KeyIdentifiersSet
    $('svg').keydown(function(event) {
        switch ( event.keyIdentifier ){
            case "U+0048": //'H'
                media.hide(); break;
            case "U+0053": //'S'
                media.show(); break;
            case "U+0050": //'P'
                media.pause(); break;
            case "U+0052": //'R'
                media.resume(); break;
        }   
    });
});

Same example as above, but using the .textInput() handler.

$(function(){
    var media = $.media( { href: 'video.mp4', repeatCount: 'indefinite' } ).addTo('svg');
    $('svg').textInput( function(event) {
        switch ( event.data ){
            case "h": media.hide(); break;
            case "s": media.show(); break;
            case "p": media.pause(); break;
            case "r": media.resume(); break;
        }   
    });
});

Notes:

  • SVG Tiny 1.2 uses a reduced interface for keyboard event (see SVG 1.2 uDom for more).
  • It is recommended to have the above code directly into the index.svg file. Otherwise, the layer including the svg file having this code must have a "focusable" attribute set to "true" (i.e. "focusable": "true"). In Elementi, this attribute can also be set from Advanced tab of Layer Properties dialog.

Media menu

Show 2 media when push buttons are pressed. When a media is started, a menu button is shown on top allowing the user to go back to the main menu.

$(function(){
      // Create 2 button inside a <g>, and make it visisble using the show() function
      var menu = $.g( { begin: 'indefinite' }).add([
        p1 = $.pushButton( { left: 100, top: 100, width: 200, height: 60 }, 'P1' ),
        p2 = $.pushButton( { left: 100, top: 200, width: 200, height: 60 }, 'P2' ),
      ]).addTo('svg').show();
      // Create a placeholder for the media to be displayed
      var media = $.g( { id: 'media'} ).addTo('svg');
      // Create a button to go back to the menu. The button is not shown by default ( begin: 'indefinite')
      back= $.pushButton( { left: 100, bottom: 100, width: 200, height: 60, begin: 'indefinite' }, 'Menu' ).addTo('svg');
      
      // Function to show a media. Hide the main menu, show the back button, and create a new media
      function showMedia( href ) {
        menu.hide();
        back.show();
        cur = $.media( { href: href, begin: 'indefinite' })
                     .endEvent( function() { 
                        menu.show(); 
                        back.hide(); 
                        cur[0].parentNode.removeChild( cur[0] );
                      } )
                     .addTo(media)
                     .show();
      }
      
      // Configure the action when the button are click
      p1.click( function() { showMedia( "media/Fusion.jpg" ); });
      p2.click( function() { showMedia( "media/hmp200_sd.mp4" ); });
      back.click( function() { 
       cur.end(); 
    });
 
});

Note:

  • In the function showMedia() the media are created with the following functions:
    • cur[0].parentNode.removeChild( cur[0] ): make sure that the media is removed from the SVG file after being played to avoid memory leak.
    • endEvent(): make sure that the main menu is visible again, and that the back button is hiden
  • The back.click() ends the cur media before its end.

Start / stop a timer on mouse events

This example shows how easy is to start / stop a timer based on mouse events. See JSignage:Timers page for more details on the timers methods.

$(function(){
  var timeout = 5000; // 5s
  var timer = null;
  
  function callback(){ 
    // some code 
  }
  
  $('svg')
    .mousedown(function(){
      if (timer) { $.clearTimeout(timer); }
    })
    .mouseup(function(){
      timer = $.setTimeout( calback, timeout);
    });
});

Drawing using the mouse

This example shows how to draw on the screen using the mouse. Press the mouse left button to star a new drawing and move it around to create your master piece. Release the mouse to finish the path. Press any other button to clear the screen.

$(function(){
  var svg = $('svg');
  var refMatrix = svg[0].getScreenCTM().inverse();
  var pt = document.documentElement.createSVGPoint();
  var path = d = null;
  
  function realPos( evt ){
    pt.x = evt.clientX;
    pt.y = evt.clientY;
    return pt.matrixTransform( refMatrix );
  }

  svg.mousedown( function( evt ) {
      if ( evt.button ) {
        $('path').remove();
        return;
      } 
      pt = realPos( evt );
      d = new $.pathData().moveTo( pt.x, pt.y );
      path = $.path({  fill: 'none', stroke: 'red', strokeWidth: 3, d: d }).addTo( svg );
  }).mouseup( function() { 
      path = false;
  }).mousemove( function( evt ) {
      if ( !path ) return;
      pt = realPos( evt );
      d.lineTo( pt.x, pt.y );
      path.attr( 'd', d );
  });
});

Notes:

  • A path is used to draw the line for each mousemove event. The path data is kept in the variable d which is modified every time the user moves the mouse.
  • It is necessary to convert the mouse position for the user space (depending on the screen configuration) and the document coordinates using realPos() function.
  • The event button property (evt.button) returns 0 for the left button, 1 for the middle button and 2 for right button.

Advanced examples

Tic-tac-toe

See how to build the basic game of Tic-tac-toe.

Paint using SVG

Based on the previous example, a full 'paint' application can be designed using jSignage, which allows the user to :

  • Select the color of the line to draw.
  • Change its size.
  • Clear the content of the drawing area.

Click here to try it on any HMTL 5 compatible browser.

$(function(){
  /* variables */
  var g;
  var path = null;
  var d = null;
  var color = 'red';
  var width = 4; 
  var refMatrix;
  
  /* functions */
  function clear() {
      if (g) { g.end(); }
      g = $.g( { left: 60, frame: { backColor: 'grey' } } )
              .mousedown( function( evt ) { 
                  pt = realPos( evt, refMatrix );
                  d = new $.pathData();
                  d.moveTo( pt.x, pt.y );
                  path = $.path({  fill: 'none', stroke: color, strokeWidth: width, d: d }).addTo( g );
               })
              .mouseup( function() { 
                  path = false;
               })
              .mousemove( function( evt ) {
                  if ( !path ) return;
                  pt = realPos( evt );
                  d.lineTo( pt.x, pt.y );
                  path.attr( 'd', d );
               })
              .removeAfter()
              .addTo('svg');
              
      refMatrix = g[0].getScreenCTM().inverse();
  }
  
  function realPos( evt ){
      var pt=document.documentElement.createSVGPoint();
      pt.x=evt.clientX;
      pt.y=evt.clientY;
      return pt.matrixTransform( refMatrix );
  }
  
  /* main code */
  // Draw the menu on the left
  $.table( {  data : ['red', 'green', 'blue', 'cyan', 'pink', 'yellow', 'purple', 'white', 'black' ],
              rows: 9,
              width: 60, height: 9*50,
              cellPadding: 5,
              frame: { backColor: 'white', frameColor: 'black', padding: 5 },
              renderToSVG: function() { 
                  return $.rect( { fill: this } )
                              .click( function() { color = $(this).attr('fill'); $('#sample').attr('stroke', color); } );
              }
          }).addTo( 'svg' );
  $.table( {  data : [ $.textArea({ fontSize: 'max' }).text("-")
                          .click( function() { 
                            if (width > 2) { width/=2; }
                            $('#sample').attr('stroke-width', width);
                          } ),
                       $.textArea({ fontSize: 'max' }).text("+")
                          .click( function() { 
                            if (width < 32) { width*=2; }
                            $('#sample').attr('stroke-width', width); 
                          } ) ],
              rows: 2,
              top: 9*50+10, width: 60, height: 2*50,
              cellPadding: 5,
              frame: { backColor: 'white', frameColor: 'black', padding: 5 },
          }).attr("pointer-events","boundingBox").addTo( 'svg' );
  $.textArea( { top: 11*50+20, width: 60, height: 100, frame: { backColor: 'blaks'}, fill: 'grey' } )
          .text("CLR")
          .click( function() { clear(); } )
          .addTo('svg');
  
  // add the sample line
  $.g( { top: 11*50+120, width: 60, height: 50 } )
     .add( $.line( { x1:5, x2: 50, y1: 25, y2: 25, 
                     id: 'sample', 
                     stroke: color, strokeWidth: width
                  } ) )
     .addTo('svg');
  
  // clear the screen to start;
  clear(); 
});
This page was last modified on 7 March 2016, at 18:42.