PlotlyJS.jl animation

Are there any examples of animating plots in PlotlyJS.jl, in particular using multiple frames such as this Python example?

I noticed a mention of using react!, but that has downsides (e.g. it resets the viewer orientation: I want to be able to animate a mesh3d plot and look at it from different angles).

I use react!, and you are right, it is somewhat limited. However, it should be possible to change the camera
after each react! to achieve programmatic control of things like the up vector, the target of the camera, and so on.

1 Like

This is an example of animation with button and slider. Each frame updates some attributes of the basic trace, as well as the layout (this example is more general than that in the link you posted). Even more general would be an example with two basic traces, each one updated by the animation frames.

using PlotlyJS

N=300
X = LinRange(0, 10, N)
Y = -3 .+ 7*rand(N)

trace = scatter(x = [X[1]],  
                y = [Y[1]],
                mode="lines",
                line_width=1.5,
                line_color="RoyalBlue")

n_frames = length(X)
frames  = Vector{PlotlyFrame}(undef, n_frames)
for k in 1:n_frames
    frames[k] = frame(data=[attr(x=X[1:k], #update x and y
                                 y=Y[1:k],
                                 )],
                      layout=attr(title_text="Test frame $k"), #update title
                      name="fr$k", #frame name; it is passed to slider 
                      traces=[0] # this means that the above data update the first trace (here the unique one) 
                        ) 
end    


updatemenus = [attr(type="buttons", 
                    active=0,
                    y=1,  #(x,y) button position 
                    x=1.1,
                    buttons=[attr(label="Play",
                                  method="animate",
                                  args=[nothing,
                                        attr(frame=attr(duration=5, 
                                                        redraw=true),
                                             transition=attr(duration=0),
                                             fromcurrent=true,
                                             mode="immediate"
                                                        )])])];


sliders = [attr(active=0, 
                minorticklen=0,
                
                steps=[attr(label="f$k",
                            method="animate",
                            args=[["fr$k"], # match the frame[:name]
                                  attr(mode="immediate",
                                       transition=attr(duration=0),
                                       frame=attr(duration=5, 
                                                  redraw=true))
                                 ]) for k in 1:n_frames ]
             )];    

ym, yM = extrema(Y)
layout = Layout(title_text="Test", title_x=0.5,
    width=700, height=450,
              xaxis_range=[-0.1, 10.1], 
              yaxis_range=[ym-1, yM+1],
              updatemenus=updatemenus,
              sliders=sliders
    )
pl = Plot(trace, layout, frames)
5 Likes

This is the animation for head scanning:

using FileIO, Images, HTTP
using PlotlyJS

url = "https://s3.amazonaws.com/assets.datacamp.com/blog_assets/attention-mri.tif"

download(url, "attention-mri.tif")
img = load("attention-mri.tif");
GRAY = permutedims(channelview(img), (2,1,3));

r, c = size(GRAY[1, :, :])
n_slices = size(GRAY, 1)
height = (n_slices-1) / 10
grid = LinRange(0, height, n_slices)
slice_step = grid[2] - grid[1]


pl_bone=[[0.0, "rgb(0, 0, 0)"],
         [0.1, "rgb(21, 21, 30)"],
         [0.2, "rgb(44, 44, 62)"],
         [0.3, "rgb(66, 66, 92)"],
         [0.4, "rgb(89, 92, 121)"],
         [0.5, "rgb(112, 123, 143)"],
         [0.6, "rgb(133, 153, 165)"],
         [0.7, "rgb(156, 184, 188)"],
         [0.8, "rgb(185, 210, 210)"],
         [0.9, "rgb(220, 233, 233)"],
         [1.0, "rgb(255, 255, 255)"]];

initial_slice = surface(
                     z=height*ones(r,c),
                     surfacecolor=GRAY[end, end:-1:1, :],
                     colorscale=pl_bone,
                     reversescale=true, #commenting out this line we get  darker slices
                     showscale=false)


frames  = Vector{PlotlyFrame}(undef, n_slices)
for k in 1:n_slices
    frames[k] = frame(data=[attr(
                                 z=(height-(k-1)*slice_step)*ones(r,c),
                                 surfacecolor=GRAY[end-(k-1), end:-1:1, :])],
                                 name="fr$k",
                                 traces=[0])
end    

sliders = [attr(steps = [attr(method= "animate",
                              args= [["fr$k"],                           
                              attr(mode= "immediate",
                                   frame= attr(duration=40, redraw= true),
                                   transition=attr(duration= 0))
                                 ],
                              label="$k"
                             ) for k in 1:n_slices], 
                active=17,
                transition= attr(duration= 0 ),
                x=0, # slider starting position  
                y=0, 
                currentvalue=attr(font=attr(size=12), 
                                  prefix="slice: ", 
                                  visible=true, 
                                  xanchor= "center"
                                 ),  
               len=1.0) #slider length
           ];
layout = Layout(title_text="Head Scanning", title_x=0.5,
                width=600,
                height=600,
                scene_zaxis_range= [-0.1, 6.8],
                sliders=sliders,
            )
pl= Plot(initial_slice, layout, frames)

Moving the slider with the mouse you can visualize each slice.

4 Likes

Thanks, I really appreciate it. It would be a great example to add to the docs.

Fantastic! One note: I installed to a fresh environment, and I needed to add AstroIO in the first line.

I 'm running Julia 1.6.1 and PlotlyJS, version 0.18.7, and the last one is 0.18.8.

This code is not working in my machine. I am getting an empty plot. Can anybody check why this is happening? For more details, please check the stackoverflow question: https://stackoverflow.com/questions/70950598/animation-of-scatter-plot-using-plotlyjs-jl-julia

@lokitkhemka

For animations use Plot, not plot.
Hence, replace the last line with:

fig =Plot(trace, layout, frames)
display(fig)

Thank you for the answer. But, it is only displaying first plot for all the frames.


Please refer to the attached screenshot

I’ve just copied the code posted here and it works. I’m running Julia 1.7.0 and PlotlyJS 0.18.8.

1 Like

I get the same result as you when I run the code in vscode. But you can disable Use Plot Pane in the julia extension setting so you can see the animation in the browser

1 Like

4 posts were split to a new topic: PlotlyJS.jl question

Hi,

I have a panel data set, and I need to do some plotting animation similar to the well-known “gapminder” example using PlotlyJS.jl, as we can find here Intro to animations in Python

I checked the various threads already available in the Discourse related to this task. The closest to my needs is this solution provided by @empet here PlotlyJS.jl animation (third entry in this issue), a solution I can indeed replicate in VScode and Pluto.

Regarding my problem, replicating the “gapminder” plots for each of the twelve individual years (1952, 1957, 1962, 1967, …, 2007) is not difficult. However, I am failing to implement the animations with the slider and the button due to some stupid mistake of mine when using @empet’s solution.

The code to produce the individual frames is, e.g., like this for the year 1952:

using PlotlyJS, CSV, DataFrames
df = dataset(DataFrame, "gapminder")
df_1952 = df[df.year .== 1952, :]

fig1 = plot(
    df_1952,
    x=:gdpPercap, y=:lifeExp, color=:continent , mode="markers",
    marker=attr(size=:pop, sizeref=maximum(df07.pop) / (60^2), sizemode="area"),
    Layout(
        title_text ="Life Expectancy vs Per Capita GDP (1952)", title_x = 0.5,
        xaxis=attr(
            type="log",
            title_text="GDP per capita (2000 dollars)",
			#gridcolor="white"
        ),
        yaxis=attr(title_text="Life Expectancy (years)"),

	) )

produces the following plot:

Help will be very much appreciated. Thanks.

This is the PlotlyJS version of gapminder data animation, for
years= 1952:5:2007:
https://gist.github.com/empet/c241a3f680a88510a2336540d2bc9b95

1 Like

@empet, thank you so much. I think it would be a good idea if someone could add this to the docs of PlotlyJS.jl. I looked everywhere and could not find anything close to your solution. This kind of plotting functionality is extremely handy.

Thanks again for the solution and the detailed comments.

1 Like