-
Notifications
You must be signed in to change notification settings - Fork 28
SCION for Interaction Design and Web Front end Development
Let's start with the simple example of drag-and-drop behaviour in the browser. You can run this demo live here.
An entity that can be dragged has two states: idle and dragging. If the entity is in an idle state, and it receives a mousedown event, then it starts dragging. While dragging, if it receives a mousemove event, then it changes its position. Also while dragging, when it receives a mouseup event, it returns to the idle state.
This natural-language description of behaviour can be described using the following simple state machine:
This state machine could be written in SCXML as follows:
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
profile="ecmascript"
initial="idle">
<state id="idle">
<transition event="mousedown" target="dragging"/>
</state>
<state id="dragging">
<transition event="mouseup" target="idle"/>
<transition event="mousemove" target="dragging"/>
</state>
</scxml>
One can add action code in order to script an HTML DOM element, so as to change its position on mousemove events:
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
profile="ecmascript">
<datamodel>
<data id="firstEvent"/>
<data id="eventStamp"/>
<data id="rectNode"/>
<data id="rectX"/>
<data id="rectY"/>
</datamodel>
<state id="initial-default">
<transition event="init" target="idle">
<assign location="rectNode" expr="_event.data"/>
<assign location="rectX" expr="0"/>
<assign location="rectY" expr="0"/>
</transition>
</state>
<state id="idle">
<onentry>
<script>
rectNode.textContent = 'idle';
</script>
</onentry>
<transition event="mousedown" target="dragging">
<assign location="firstEvent" expr="_event.data"/>
<assign location="eventStamp" expr="_event.data"/>
</transition>
</state>
<state id="dragging">
<onentry>
<script>
rectNode.textContent = 'dragging';
</script>
</onentry>
<transition event="mouseup" target="idle"/>
<transition event="mousemove" target="dragging">
<script>
var dx = eventStamp.clientX - _event.data.clientX;
var dy = eventStamp.clientY - _event.data.clientY;
//note that rectNode, rectX and rectY are all exposed
//from the datamodel as local variables
rectNode.style.left = rectX -= dx;
rectNode.style.top = rectY -= dy;
</script>
<assign location="eventStamp" expr="_event.data"/>
</transition>
</state>
</scxml>
There are then 4 steps that must be performed to go from an SCXML document to a working state machine instance that is consuming DOM events and scripting web content:
- Get the SCXML document, and convert it to a SCXML "model" object for easier interpretation.
- Use the SCXML model object to instantiate the SCXML interpreter.
- Connect relevant event listeners to the SCXML interpreter.
- Call the
start
method on the SCXML interpreter to start execution of the statechart.
<html>
<head>
<style type="text/css">
html, body {
height:100%;
margin: 0;
padding: 0;
}
div#rect{
width:100px;
height:100px;
background-color:red;
border:2px solid black;
position:absolute;
left:0px;
top:0px;
}
</style>
<script type="text/javascript" src="https://cdn.rawgit.com/jbeard4/SCION/2.0.14/dist/scxml.js"></script>
</head>
<body>
<div id="rect"/>
<script>
var rect = document.getElementById("rect");
scxml.urlToModel("drag-and-drop.xml",function(err,model){
if(err) throw err;
//instantiate the interpreter
var interpreter = new scxml.scion.Statechart(model);
//start the interpreter
interpreter.start();
//send the init event
interpreter.gen({name:"init",data:rect});
function handleEvent(e){
e.preventDefault();
interpreter.gen({name : e.type,data: e});
}
//connect all relevant event listeners
rect.addEventListener('mousedown',handleEvent);
document.documentElement.addEventListener("mouseup",handleEvent);
document.documentElement.addEventListener("mousemove",handleEvent);
});
</script>
</body>
</html>