Graphics animation & interaction
Interactive plots
By the default, everything you plot using Plot or Graphics or Graphics3D can be dragged or panned or rotated. This behavior is controlled by the options and can be switched off if necessary. For example
Figure = Plot[{x, Sin[x], Sin[x]^2}, {x,0,2Pi}];
.slide
# Simple plot
<Figure/>
Try to drag it using you mouse
The result will look like following
Animation
In general all visuals can be done in the same way as in regular cells, since it uses the same components.
When a slide becomes visible or a fragment got revealed (see Transitions and fragments) it fires an event, where all information is encoded. To enable this - use SlideEventListener
Put SlideEventListener anywhere on the slide to hook up WL Kernel to all events associated with it
Keep the dynamic variables scoped using LeakyModule
and use explicit event routing like in routing. Later it will allow you to reuse your components for other slides much easier.
Example 1
Let us see the simples example
.slide
# Animation example
<Figure Id={"routed-event"}/>
The figure will be changed, when the fragment below is revealed
<span style="color:red">Magic</span> <!-- .element: class="fragment" data-fragment-index="1" -->
<SlideEventListener Id={"routed-event"}/>
SlideEventListener
is attached only to the slide, where it has been placed.
Now let us make a simple figure
Figure[OptionsPattern[]] := With[{event = EventClone[OptionValue["Id"]]},
EventHandler[ResultCell[], {"Destroy" -> Function[Null, Delete[event]]}];
...
]
Options[Figure] = {"Id"->""}
Here we clone a slide event (this is a safe way, if more than 1 handlers will be involved). Afterwards we need to make sure that if one reevaluate the cell, the handler will be removed automatically. That's how you can clean-up handlers after the evaluation.
Now the content
Figure[OptionsPattern[]] := With[{event = EventClone[OptionValue["Id"]]},
EventHandler[ResultCell[], {"Destroy" -> Function[Null, Delete[event]]}];
LeakyModule[{points},
(* initial state *)
points = RandomReal[{-1,1}, {40,2}];
EventHandler[event, {"fragment-1" -> Function[Null,
(* act when the event happend *)
points = {Sin[#], Cos[#]} &/@ Range[40]
]}];
Graphics[
{Red, Line[points // Offload]},
PlotRange->{{-1,1}, {-1,1}},
TransitionType->"CubicInOut",
TransitionDuration->2000
]
]
]
Options[Figure] = {"Id"->""}
This little script will plot randomly distributed points as lines for its initial state. When the event
is fired, it changes the distribution of points
to a circle. The animation is done by Graphics (i.e. it is a native feature of it and has nothing to do with slides).
Consider options "TransitionType"
and "TransitionDuration"
of Graphics to control the transition animation.
The expected result will be
Sure the state is not reservable in this case. You need to manage it by your own considering more events generated by SlideEventListener.
However, in practice reports are usually linear and do not require to repeat all animations again.
Example 2
Animating transitions complex figures
SpinStructure[OptionsPattern[]] := LeakyModule[{t2=0, t0=0, cloned = EventClone[OptionValue["Slide"]]},
(* clone and remove for the case if one assigns other handlers to the same patterns *)
EventHandler[ResultCell[], {"Destroy" -> Function[Null,
Print["Removed!"];
EventRemove[cloned];
]}];
EventHandler[cloned, {
OptionValue["Stage1"] -> Function[Null,
t0=1;
],
OptionValue["Stage2"]->Function[Null,
t2=1;
],
(* you need to go back to the previous slide to reset *)
"Slide"->Function[Null,
t0=0; t2=0;
]
}];
Graphics[With[{
cSpins = ((*GB[*){{{1,-1}(*|*),(*|*){1,-1}(*|*),(*|*){1,1}(*|*),(*|*){1,-1}}(*||*),(*||*){{1,1}(*|*),(*|*){1,-1}(*|*),(*|*){1,1}(*|*),(*|*){1,-1}}(*||*),(*||*){{1,-1}(*|*),(*|*){1,1}(*|*),(*|*){1,-1}(*|*),(*|*){1,1}}(*||*),(*||*){{1,1}(*|*),(*|*){1,1}(*|*),(*|*){1,-1}(*|*),(*|*){1,1}}}(*]GB*))
},
{
Table[{
With[{spinPos = (0.23 cSpins[[i,j]] - 0.23 {1,0}), i=i, j=j},
Arrow[{{i,j}, ( {i+0.23, j} + t0 spinPos) // Offload}]
],
Circle[{i,j}, 0.18]
}, {i,1,4}, {j,1,4}],
Orange,
Table[{
Circle[{i+0.5,j+0.5}, 0.2],
Disk[{i+0.5,j+0.5}, 0.15],
Black,
Arrow[{{i+0.5,j+0.5}, {-0.3, -0.08 Offload[t2]} + {i+0.5,j+0.5}}]
}, {i,1,3,2}, {j,1,3,2}],
Disk[{1+1.5,1+1.5}, 0.2],
Black,
Arrow[{{1+1.5,1+1.5}, {-0.3, -0.08 Offload[t2]} + {1+1.5,1+1.5}}]
}
],
Axes->True,
AxesLabel->{"a", "c-axis"},
AxesStyle->Directive[FontSize->18], Ticks->{{-10}, {-10}},
PlotRange->{{0,5}, {0,5}},
ImageSize->550,
TransitionType->"CubicInOut",
TransitionDuration->500
]
]
Options[SpinStructure] = {"Stage1"->"", "Stage2"->"", "Slide"->""}
The whole animation consists two stages, on which the direction of arrows changes. We can assign each stage to a fragment on our slide
.slide
# Animation Test
---
# Spin structure
<SpinStructure Slide={"sslide"} Stage1={"fragment-1"} Stage2={"fragment-2"} />
Reported spin configuration $T < 35K$ <!-- .element: class="fragment fade-in" data-fragment-index="1" -->
Expected low-temperature spin configuration <!-- .element: class="fragment fade-in" data-fragment-index="2" -->
<SlideEventListener Id={"sslide"}/>
Example 3
Zoom in to the graph
Plt[OptionsPattern[]] := With[{ev = OptionValue["Zoom"], win = CurrentWindow[]},
EventHandler[ev, {
"Slide" -> Function[Null,
FrontSubmit[ZoomAt[1], MetaMarker["marked"], "Window"->win]
],
"fragment-1" -> Function[Null,
FrontSubmit[ZoomAt[2], MetaMarker["marked"], "Window"->win]
]
}];
Plot[Sinc[x], {x,-10,10}, Epilog->{MetaMarker["marked"]}]
]
Options[Plt] = {"Zoom"->""}
.slide
# Zoom in
---
<Plt Zoom={"vslide"}/>
Zoom <!-- .element: class="fragment fade-in" data-fragment-index="1" -->
<SlideEventListener Id={"vslide"}/>
Append graphics to a slide
MetaMarker can work well in a case if one wants to append some data on the existing graphics canvas
Buttons, sliders etc
See built-in examples Examples/Slides
.