AnimationFrameListener
signals once the beginning of a browser's frame (depends on OS settings, hardware). It can be reloaded again after any changes of dependent dynamic symbols similar to requestAnimationFrame
.
The main idea is
- to be in sync with a beginning of native browser's frame drawing
- to animate something as fast as possible
- don't fire next frame, before calculations are done
Therefore this provides a balanced load to the system.
AnimationFrameListener[trigger_Offload, "Event"->event_String, opts___]
where trigger
can be any dynamic symbol wrapped in Offload, that will be changed each frame, it serves the purpose of a signal, that all calculations are done. event
is a string representation of EventObject
to which a handler function, that recalculates trigger
is assigned.
Options
"Event"
(mandatory)
Provides an event object in the form of a string
Example
Here is a basic example with many points and lines animated:
With[{pts = Unique[], event = CreateUUID[], n = Unique[]},
n = 1;
EventHandler[event, Function[Null,
pts = Table[{x, Sum[Cos[2 x i]/(2i+1), {i, 1, n}]/n}, {x,0,10Pi,0.1}];
n = If[n>20, 1, n + 0.1];
]];
EventFire[event, True];
Graphics[{
Blue, Line[pts//Offload], Red, Point[pts//Offload],
AnimationFrameListener[pts//Offload, "Event"->event]
}, Axes->True, PlotRange->{{0,10Pi}, {-0.15,0.3}}, TransitionType->None]
]
Animation goes as fast as possible. It does not make sense to still use transition interpolation of 2D vector graphics, therefore we disabled it with TransitionType->None
.
Here we used the same symbol pts
used for drawing our points for triggering AnimationFrameListener. It does not cost anything, since it listens only for change of a symbol and does not read or decode an actual value.
Here is another example with 3D graphics:
balls = RandomReal[{-1,1}, {100,3}];
vels = RandomReal[{-1,1}, {100,3}];
EventHandler["frame", Function[Null,
vels = Table[
If[Norm[balls[[i]]] < 0.01, -1, 1] vels[[i]] - 0.08 balls[[i]]
, {i, Length[balls]}];
balls = Table[balls[[i]] + 0.08 vels[[i]], {i, Length[balls]}];
]];
Graphics3D[{
Table[With[{i = i},
{
RGBColor[RandomReal[{0,1}, 3]],
Sphere[balls[[i]] // Offload, 0.03]
}
], {i, Length[balls]}],
AnimationFrameListener[balls // Offload, "Event"->"frame"]
}]
There is also a famous example of 1000 particles "dancing" with each other
n = 1000;
r := RandomInteger[{1, n}];
f := (#/(.01 + Sqrt[#.#])) & /@ (x[[#]] - x) &;
s := With[{r1 = r}, p[[r1]] = r; q[[r1]] = r];
x = RandomReal[{-1, 1}, {n, 2}];
{p, q} = RandomInteger[{1, n}, {2, n}];
EventHandler["frame", Function[Null,
(* all calculations *)
x = 0.995 x + 0.02 f[p] - 0.01 f[q];
If[r < 100, s];
]];
Graphics[{
PointSize[0.007], Point[x // Offload],
AnimationFrameListener[x // Offload, "Event"->"frame"]
}, PlotRange -> {{-2,2}, {-2,2}}, "TransitionType"->"Linear", "TransitionDuration"->1]
to cancel animation - remove handler
EventRemove["frame"]
and to restart - evaluate cell 2 and retrigger it using
EventFire["frame", True];