Search code examples
rggplot2gganimate

Rounding frame_time and smooth transitions for gganimate


I have the following data frame:

# Seed RNG
set.seed(33550336)

# Create data frame
df <- data.frame(x = runif(100), 
                 y = runif(100), 
                 t = runif(100, min = 0, max = 10))

I'd like to plot points (i.e., at x and y coordinates) appearing and disappearing as a function of t. gganimate is awesome, so I used that.

# Load libraries
library(gganimate)
library(ggplot2)

# Create animation
g <- ggplot(df, aes(x = x, y = y))
g <- g + geom_point(colour = "#FF3300", shape = 19, size = 5, alpha = 0.25)
g <- g + labs(title = 'Time: {frame_time}')
g <- g + transition_time(t)
g <- g + enter_fade() + exit_fade()
animate(g, fps = 1)

This code produced the following:

enter image description here

There are a couple of things that I don't like about this.

  1. The transitions are very abrupt. My hope using enter_fade and exit_fade was that the points would fade into view, then back out. Clearly this isn't the case, but how would I achieve this result?
  2. I would like to round {frame_time}, so that while the points fade in and out at fractions of t, the actual time t that would be shown would be an integer. If frame_time was a regular variable, this would be simple enough using something like bquote and round, but this doesn't seem to be the case. How can I round frame_time in my title?

Solution

  • Here's a relatively manual approach that relies on doing more of the prep beforehand and feeding that into gganimate. I'd like to see if there's a simpler way to do this inside gganimate more automatically.

    First I make a copy of the data frame for each frame I want to show. Then I calculate the difference between the time I'm presently viewing (time) and the t when I want to show each data point. I use cos to handle the easing in and out, so that each dot's appearance at given time is described with display. In the ggplot call, I then map alpha and size to display, and use transition_time(time) to move through the frames.

    # Create prep table
    fade_time = 1
    frame_count = 100
    frames_per_time = 10
    df2 <- map_df(seq_len(frame_count), ~df, .id = "time") %>%
      mutate(time = as.numeric(time)/frames_per_time,
             delta_norm = (t - time) / fade_time,
             display = if_else(abs(delta_norm) > 1, 0, cos(pi / 2 * delta_norm)))
    
    
    # Create animation
    g <- ggplot(df2, aes(x = x, y = y, alpha = display, size = display))
    g <- g + geom_point(colour = "#FF3300", shape = 19)
    g <- g + scale_alpha(range = c(0, 1)) + scale_size_area(max_size = 5)
    g <- g + labs(title = "{round(frame_time, 1)}") 
    g <- g + transition_time(time)
    animate(g)
    

    enter image description here