Create interactive content with jSignage
From SpinetiX Support Wiki
This page is related to Interactivity page.
Introduction
The JSignage API provides both high-level interactive layers (multi-page, carousel, pop-up etc. ) and low-level interactive event functions (click, keydown, textInput, etc.) to make it easy to build interactive applications in SVG.
Interactivity is typically achieved by installing handlers (callbacks) for typical input events (such as mouse or key events) or Shared Variable updates (cause by events from serial port devices, USB I/O devices and the network) - these callbacks could then perform one or more of these actions:
- Show or hide a layer.
- This is done simply by calling the
.show()
,.hide()
or.setVisible()
functions common to all layers. In or out animation effects are triggered automatically, and all animations inside the layer are restarted as well.
- This is done simply by calling the
- Switch to a 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. 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.
- Push new data to a slideshow, playlist, crawler, textTicker or textBar layer.
- This is done using the
.pushData( )
function, and it 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.
- This is done using the
- Move to the next or previous slide of a carousel.
- Pause or resume media content.
- Simply call the
.pause()
or.resume()
functions common to all layers.
- Simply call the
- Make an AJAX request to a web server with some user input.
- The response handler will then call methods to modify the content accordingly.
- Send requests to an attached device via the serial port or USB I/O API.
- Modify a Shared Variable for synchronized actions on multiple devices.
- When the interactive application is running on a wall of monitors, it is necessary to add intermediary steps through Shared Variables to synchronize all the displays. This is done with using
SV.set()
function.
- When the interactive application is running on a wall of monitors, it is necessary to add intermediary steps through Shared Variables to synchronize all the displays. This is done with using
Interactive layers
The following types of layers are included into jSignage for building interactive content:
- Multi-page, constructed with
$.multiPage()
- Carousel, constructed with
$.carousel()
- Pop-up, constructed with
$.popup()
- Push button, constructed with
$.pushButton()
- Progress bar, constructed with
$.progressBar()
- Progress wheel, constructed with
$.progressWheel()
More layers' types can be added by extending the jSignage library with new constructors.
Interactive events
jSignageLayer.<eventHandler>( callback );
Returns: jSignage Object
Description
jSignage API offer support for all the SVG Tiny 1.2 events, such as:
-
.click()
,.mousedown()
,.mouseup()
,.mouseover()
,.mousemove()
,.mouseout()
,.mouseenter()
,.mouseleave()
,.mousewheel()
,.hover()
for all layer types -
.keydown()
,.keyup()
, and.textInput()
for focus sensitive layers, or on the top-level$('svg')
-
.DOMFocusIn()
,.DOMFocusOut()
, and.DOMActivate()
for focus sensitive layers
- The keyboard's events from SVG tiny 1.2 are a subset of the full DOM level 3 event.
- While
.keydown()
and.keyup()
return the key identifier (U+0041 for 'A') and some modified flags (shiftKey=true), the.textInput()
returnsdata
that contains the resulting pressed key ('a' or 'A'). However, keys such as 'Enter' or 'Escape' will not trigger any.textInput()
events. See SVG Tiny 1.2 uDOM for more details. - See also the jQuery events documentation (Mouse Events, Keyboard Events) for reference.
Parameters
-
callback
- Type: Callback.
- The callback function is invoked when the event occurs with one parameter, an object holding the event details.
Tutorials
- Simple push button
- Advanced click handler
- Virtual museum navigation
- Image carousels
- Interactive slideshow
- Interactive text ticker
- Add a mouse cursor
Examples
See also interactive projects page.
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;
}
});
});
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.
Control 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;
}
});
});
- 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.
Start / stop a timer on mouse events
This example shows how easy is to start / stop a timer based on mouse events.
$(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);
});
});
See JSignage timer methods page for more details on the timers methods.
Change color on mouse over
Create a rectangle, add it to the svg element and change its color on mouse movements:
$(function(){
$.rect( { width: 200, height: 100, top:'20%', left: '20%', fill:'blue' })
.addTo('svg')
.mouseover( function() { $(this).attr('fill','red'); })
.mouseout( function() { $(this).attr('fill','green'); }) ;
});
Draw 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 );
});
});
- 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
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 "primary" player whenever a click is received (the "primary" must have the "Enable events" option enabled).
- If a "follower" player is restarting, it will automatically get the last value of the control shared variable from the "primary" and thus continue to be in sync with the others.
- If the "primary" restarts, it will send a reset update to all the "followers". 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:
-
PRIMARY
with the IP address of your "primary" 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 "follower" players (e.g. 192.168.1.10:1234); -
screen-1-1
with the multiscreen ID of your "primary" player.
var varName = 'click@PRIMARY_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 primary and is useful in case of a primary 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).
}
});
});
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();
});
});
- 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 thecur
media before its end.
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.
JavaScript code:
$(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();
});