Hack 78 An Idle Timer (Timeout Event)
When performance is critical, you
don't want to waste processor time checking for user
interactivity. Add a "no-interaction" timeout event that does not
interfere with performance-sensitive multimedia delivery during
periods of interaction.
I was asked to develop a Flash-based
questionnaire application that ran on the screens on the backs of
aircraft passenger seats. When the questionnaire was left idle, Flash
should start playing some video. It should switch back quickly to the
questionnaire if the user moved or clicked the mouse, so I set up
some idle-detection code. However, the video playback became jerky if
the idle-detection code wasn't highly optimized.
Macromedia Director has an idle event that fires
whenever Director is not doing anything, allowing you to use this
spare time doing something constructive via a background task.
It's normal for Flash designers to increase the
frame rate until Flash doesn't have any idle time
(it is significantly lower than the Director idle time in any case
because Flash performance is slower than
Director's). So Flash doesn't
provide built-in support for an idle event to be triggered when no
other processing is being performed, because it
isn't really appropriate to the way Flash is used.
That set me thinking. I needed an idle event that looks for no user
interaction, which would be a more appropriate time for Flash to do
some different processing. If no activity is detected, the SWF could,
say, display an appropriate animation or an audible prompt. (In
Director, these are called timeout
events, but again, Flash has no native
support for detecting when the user is inactive.)
It is common for users to leave Flash sites open while rich media
elements (such as video or sound) load and then do something else in
another browser window. Rather than have the SWF wait on a
"content loaded" message screen
when the download is complete, it would be nice for the SWF to
provide an audible cue if the user hasn't responded
to the fact that content loading has completed.
In kiosk applications, you might want the SWF to return to an attract
loop (i.e., a screen displaying a video or animation intended to
attract passers-by) when no user activity has been detected within
the timeout period.
The key is creating an idle-detection routine that
doesn't consume processing power when the user
is interacting with the SWF. The most obvious
event for detecting user activity is
onMouseMove (a user who is moving the mouse is
presumed to be breathing). However, when the
user is actively using the application, the
onMouseMove event occurs often enough to affect
performance of the main SWF, so we should avoid relying on this event
when designing a background event handler.
A more processor-friendly way to detect interaction is to use two
setInterval( ) timers as follows:
Use a short interval that checks for interaction every one to two
seconds (not often enough to adversely affect performance). Use a long interval (typically 15 to 120 seconds) that specifies the
timeout period length. This interval is reset whenever the short
interval detects user activity.
So, we have two intervals
that occur relatively infrequently compared to events like
onEnterFame, which occurs once for each Flash
frame. The key is to make the code as minimal as possible to avoid a
performance hit; too much background processing will make Flash
sluggish.
Here's a short code listing that shows the two
intervals in action. The more frequent of our two intervals executes
minimal code (only one if statement). The
commented trace( ) statements can be uncommented
to see what the code is doing during the interval periods (be aware
that trace( ) statements are time consuming, so
you don't want them in your final version).
function idleTime( ) {
// Play the attract animation
//trace("playing the animation")
gotoAndStop("attract");
}
function idleS( ) {
// See if there has been any mouse movement in the last two seconds
if ((_root._xmouse + _root._ymouse) != mousePos) {
// If there has, restart the check for the idle timer
//trace("resetting");
gotoAndStop("noAttract");
clearInterval(idle);
idle = setInterval(idleTime, 28000);
//} else {
//trace("no movement this period");
}
// Store an integer representing the mouse position
mousePos = _root._xmouse + _root._ymouse;
}
function startIdle( ) {
mousePos = -100000; // Initialize it to a dummy value
idleSample = setInterval(idleS, 2000);
}
startIdle( );
stop( );
The first interval, idleSample, checks the mouse
position every two seconds (it is better to check for mouse movement
this way rather than onMouseMove, because
onMouseMove happens sufficiently often to
possibly slow down your Flash application). If the sum of the mouse x
and y positions do not remain the same between two interval
invocations, the user has moved the mouse, so we reset the second,
longer interval, idle. If no interaction occurs
before the idle interval reaches its timeout value
(28 seconds), the idleTime( )
function executes and sends the
playhead to the attract frame (which typically
displays a looping animation or possibly a video).
If desired, you can modify the code so that the idleTime(
) function starts a wait animation and the
idleS( ) function stops it. The wait animation
would typically be a screensaver-style animation or a
"where have you gone?" animation.
This preceding code carries on indefinitely until 30 seconds (2,000 +
28,000 ms) of inactivity causes Flash to invoke the
idleTime( ) function, which jumps to the
attract frame. Presumably, an
onMouseDown( ) event handler on that frame
aborts the attractor animation and starts the presentation anew when
the user clicks the mouse.
Final Thoughts
To maintain Flash performance, you have to minimize background tasks
operating when rich media (video, sound, or complex animation at fast
frame rates) are also being delivered. This means that your
background tasks have to be highly optimized. The
"check for idle user" code
presented doesn't run often, so that when the user
is interacting with the site, it does not slow down the main content.
When the user has not been interacting for some time, the code allows
switching to an attract loop or screensaver-style animation. This
would be useful for kiosk applications in which you want the Flash
movie to reset to the beginning if no user is presently interacting
with it. One caveat: be sure that your timeout period (such as 30
seconds) is longer than, say, any animation sequence you expect the
user to sit through. If the user is watching your kiosk run, you
don't want it to suddenly time out. So you might
have to lengthen your timeout to allow for expected periods of
inactivity even when a user is present and otherwise active. If you
expect a user to sit passively through something for more than, say,
90 seconds, it is probably time to rethink your kiosk design instead
of merely increasing the timeout duration.
Similarly, it is a good idea to have keypress events reset the
timeout period. Otherwise, if the user is entering text in a form but
not moving her mouse, the movie might unceremoniously jump to the
attract loop. Therefore, if your kiosk has a keyboard, use
Key.addListener( ) to invoke an
onKeyDown( ) event handler that resets the idle
interval.
|