Adobe SVG Viewer and animation modification (Part I)
As stated in a previous blog entry, WaterSums 1.1 introduced many new features that were only available with the Silverlight plugin. WaterSums 1.1.1 aims to backport some of this functionality to the Adobe SVG Viewer (ASV) plugin.
One of these features is adjusting the speed of animation playback using the toolbar buttons in WaterSums and this uncovered some knotty problems with controlling Adobe SVG Viewer. Update: more knotty problems have been discovered and this blog entry has been updated to include the extra information.
WaterSums uses JQuery (http://jquery.com/) and the SVG plugin from Keith Wood (http://keith-wood.name/svg.html) to make some Javascript tasks easier. ASV allows the clock time for animations to be set and the duration attribute can be set to change the speed of the animation. The complication? - modifying animations while the animations are running can cause the Adobe SVG Viewer to crash, taking WaterSums with it. Pausing the animations before making the changes reduces the number of crashes, but does not eliminate them. Even pausing the animations asynchronously as suggested in my blog entries Adobe SVG Viewer and mousewheel zoom Part I and Part II will not solve the problem completely. In particular, if the modifications occur at the end of the animation cycle when animations are about to restart, the crash will still occur. It appears that something in the ASV is very sensitive at this stage of the animation cycle and great care must be taken in controlling the viewer. Unfortunately the Adobe SVG Viewer is no longer supported by Adobe, so there is no chance that these bugs will be fixed.
It is also vital to note that if several pause/operate/unpause cycles are repeated quickly, many of crashes will occur while attempting to pause the animations. From examination, these seem to happen when important changes happen to the animation. In WaterSums, animation is "frame-based", in that each time step can be considered a separate frame. The simulation clock updates the digital time display by animating the href attribute in a text reference. These changes are significant to the Adobe SVG Viewer and attempting to pause animations during such a transition can cause a crash.
After experimentation it appears that, for a series of operations like this:
- pausing of animations must not be done in a critical period of the animation when the animation is being restarted
- pausing of animations must not be done when the animation involves 'significant changes' eg. updating text content
- the modification of animation must be done asynchronously
- setting of clock time must be done asynchronously
- all such operations must be complete before the animations are restarted ('unpaused')
- a delay after restarting animations improves reliability
To sum up, then, using the Adobe SVG Viewer effectively seems to require the JavaScript controlling functions to make sure that certain operations are performed one by one, and that the next operation is not started until the previous operation has finished.
In this blog entry, we look at implementing a JavaScript function that will allow such a chain of operations to be performed asynchronously with animation being safely paused before the first operation begins and 'unpaused' once the last operation is completed. Each of the operations must be completed before next one can begin, so there must be a way of identifying that each operation is complete. Note that the function must return immediately since JavaScript does not have sleep or yield functions, but the operations will proceed asynchronously using the setTimout() function to queue up the necessary operations.
/* Adobe SVG Viewer pause control constants */ var ASVAnimationExecuting = 0; /* (default startup value) */ var ASVAnimationPaused = 1; var ASVAnimationWaitingToPause = -1; var ASVAnimationPausePending = -2; var ASVAnimationUnPausePending = -3; /* * These are return values used as return values when * ASVPauseAnimation or ASVUnPauseAnimation are called when * the pause/unpause mechanism is already busy. * They are not used as values in ASVPauseState. * Code assumes they are 10 less than their corresponding 'pending' * value. */ var ASVAnimationAlreadyWaitingToPause = -11; var ASVAnimationPauseAlreadyPending = -12; var ASVAnimationUnPauseAlreadyPending = -13; /* Adobe SVG Viewer pause control variables */ var ASVPauseState = ASVAnimationExecuting;
With these basic constants and a variable to store the ASV pause status, the interface functions required are:
-
function ASVPauseAnimation(fnpause)
The function only attempts to safely pause animations if the status is currentlyASVAnimationExecuting. The function returns immediately, and the pause may or may not be complete before it returns.ASVPauseAnimationreturns the current pause state of animation, which will be one of the animation status values above.
fnpauseis a user-defined callback function taking a singlebooleanargument. The callback function is called when the pause occurs or when it times out after 5 seconds. The argument indicates whether the pause was successful or not. Any return value from the callback is ignored.
The function also triggers a customASVPausedevent if the pause is successful or a customASVPauseFailedevent if the pause fails. These events are triggered after the user callback has returned. -
function ASVUnPauseAnimation(fnunpause)
The function only attempts to unpause animations if the status is currentlyASVAnimationPaused. The function returns immediately, and the unpause may or may not be complete before it returns.ASVUnPauseAnimationreturns the current pause state of animation, which will be one of the animation status values above.
fnunpauseis a user-defined callback function taking a singlebooleanargument. The callback function is called when the unpause occurs or when it times out after 5 seconds. The argument indicates whether the unpause was successful or not. Any return value from the callback is ignored.
The function also triggers a customASVUnPausedevent if the unpause is successful or a customASVUnPauseFailedevent if the unpause fails. These events are triggered after the user callback has returned. -
function ASVPauseAnimationToExecute(fnop, fncomplete)
pauses animation, calls a user operation callback function repeatedly until the return value indicates the operation has finished, and then unpauses the animation. Once the animation is executing again, the user's completion callback is called. This function usesASVPauseAnimation()andASVUnPauseAnimation()functions, so theASVPause/ASVUnPauseevents will also be triggered during the process and the same timeout periods apply meaning thatASVPauseFailed/ASVUnPauseFailedevents can also be generated. A similar timeout period is enforced for the user operations performed by the user callback function.fnopis a user-defined callback function taking a single argument - a sequence number - which is called repeatedly once animations have been paused. Interaction through this callback is as follows:- the callback function is initially called with a sequence number of 0
- when the callback function returns,
ASVPauseAnimationToExecutesaves the value in a global variableASVOpSeqNumand arranges to call the function again in 5 milliseconds with the value of the global at that time - to indicate that the operation is completed, the user callback function must return 0
function fnop(seqnum) if seqnum = 0 // starting process set animation durations seqnum = 1 if seqnum = 1 if animations durations have been updated seqnum = 2 if seqnum = 2 set current time seqnum = 3 if seqnum = 3 if time has been set seqnum = 0 // process has finished return seqnumIn this way, if operations occur immediately, the process will be completed synchronously. If delays occur, there can be one or more subsequent calls to the callback.
In the next two parts in this blog series, the details of these functions will be given (DV).
-
Part 2 describes
ASVPauseAnimation()andASVUnPauseAnimation() - Part 3 is planned to describe
ASVPauseAnimationToExecute()
