Animating points efficiently in JSXGraph
Explanation
The aim here is to find the most efficient way to animate objects in JSXGraph. Our example below has a bunch of dots that we want to expand outwards then rotate as smoothly as possible.
The most common refresh rate for monitors is 60 frames per sescond (fps), so for smooth animations, we use requestAnimationFrame
which for most monitors, fires every 1000/60 = 16.7 ms. Depending on how much work the browser needs to do when calculating javascript values and then moving items around, the result is not always smooth.
We illustrate several ways of going about this.
1. Create new: In this scenario, we create the initial points, then destroy them (using JSXGraph's board.removeObject(plotMatrix[i][j]);
) then create new ones in the next desired position using plotMatrix[i][j] = board.create("point", [x, y]...
. This is intensive since DOM elements are detroyed then recreated, and JSXGraph needs to update all the internal object parameters. It is the least efficient way of going about things.
2. Visibility: In this method, we create all the required points first, then change their visibility as required. It works OK for a small number of points, but the creation time for the points grows quickly as the number of points gets higher. For the 144 case, typical times for creation of the board and all required points was > 5 seconds on a desktop, and > 30 s on a phone.
Creating points also creates a corresponding DIV (for the point label) even if it is empty. This slows things down a lot and is not good usability.
3. setPosition: In this case, the points are repositioned using plotMatrix[i][j].setPosition(JXG.COORDS_BY_USER,[x,y]);
. This is less DOM intensive than creating points anew, but JSXGraph still needs to update all the internal object parameters and run other processes, which slow things down. Internally in JSXGraph, setPosition moves a point via a transformation, then the point's cx- and cy-values are updated. This is slow because of the board.suspendUpdate()
required.
4. transform: In this case, the points are repositioned using
board.suspendUpdate(); tr = board.create('transform', [diffXusr, diffYusr], {type: 'translate'}); tr.applyOnce(plotMatrix[i][j]); board.unsuspendUpdate();
Internally, after the transform, this changes the cx- and cy-values of the point. In terms of animation speed, there is virtually no difference between this and setPosition because of the board.suspendUpdate()
.
5. moveTo: In this case, the points are repositioned using
plotMatrix[i][j].moveTo([x, y], 1);
Internally, after the move, this also changes the cx- and cy-values of the point. This is a lot faster because there is no board.suspendUpdate()
, but it still doesn't achieve 60 fps.
6. modCxCy: (my non-JSXGraph solution) When the actual SVG <ellipse> elements are directly moved by changing their "cx" and "cy" (dot centers) attributes. This is least CPU intensive since DOM elements are just being transformed, and JSXGraph is not called on to do anything. It allows us to freely move around the dots, resulting in a frame rate of 60 fps (ideal) when animating 36 points on a desktop, and around 40~50 fps (good) on a phone.
The downside to this technique is JSXGraph doesn't know where your points have ended up, so at the end of each rotation event (on clicking "Stop"), we update JSXGraph internal values (screen position in px and "user coordinates", the Cartesian x- and y-values).
Definitions
"Creation" refers to how long it takes to create the initial points required for each scenario.
"Duration" refers to how long it takes to "explode" the dots from their original position.
"Frame rate" ("fps", or Frames per Second) is the animation speed achieved. For smoothest animations, the aim is to achieve 60 fps
Note: After the "explode" translation, we aim to rotate the points smoothly. This is only really possible with the "modCxCy" scenario because it's possible to do so at 60 fps. The other cases range from way too sluggish to just slow, except moveTo
, which is not too bad.
Number points:
Creation: 0.0 s. Duration: 0.0 s. Frame rate: 0.0 fps.