Skip to main content

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

info

Put SlideEventListener anywhere on the slide to hook up WL Kernel to all events associated with it

tip

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).

note

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

cell 1
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

cell 1
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"->""}
cell 2
.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.