Explanation
This phenomenon arises because transition_reveal
tweens values to get the transition position (where the arrowhead is located) in each frame. Whenever the calculated transition position coincides with an actual point on the dataset, there would be two sets of coordinates for the same location. This results in the reversed arrow.
(In your example, the arrow is reversed all the way because the default number of frames is the same as the number of rows in your data, so each calculated transition position is a duplicate of an existing data point. If the frame number is some other number, e.g. 137, the arrow would reverse in some frames & point straight in others.)
We can demonstrate this phenomenon with a smaller dataset:
p <- ggplot(data.frame(x = 1:4, y = 1:4),
aes(x, y)) +
geom_line(size = 3, arrow = arrow(), lineend = "round", linejoin = "round") +
theme_minimal() +
transition_reveal(x)
animate(p + ggtitle("4 frames"), nframes = 4, fps = 1) # arrow remains reversed till the end
animate(p + ggtitle("10 frames"), nframes = 10, fps = 1) # arrow flips back & forth throughout


Workaround
The key function here is expand_data
from the ggproto object TransitionReveal
. I wrote a modified version that adds a check for duplicated positions before returning the expanded dataset:
TransitionReveal2 <- ggproto(
"TransitionReveal2", TransitionReveal,
expand_panel = function (self, data, type, id, match, ease, enter, exit, params,
layer_index) {
row_vars <- self$get_row_vars(data)
if (is.null(row_vars))
return(data)
data$group <- paste0(row_vars$before, row_vars$after)
time <- as.numeric(row_vars$along)
all_frames <- switch(type,
point = tweenr:::tween_along(data, ease, params$nframes,
!!time, group, c(1, params$nframes),
FALSE, params$keep_last),
path = tweenr:::tween_along(data, ease, params$nframes,
!!time, group, c(1, params$nframes),
TRUE, params$keep_last),
polygon = tweenr:::tween_along(data, ease, params$nframes,
!!time, group, c(1, params$nframes),
TRUE, params$keep_last),
stop(type, " layers not currently supported by transition_reveal",
call. = FALSE))
all_frames$group <- paste0(all_frames$group, "<", all_frames$.frame, ">")
all_frames$.frame <- NULL
# added step to filter out transition rows with duplicated positions
all_frames <- all_frames %>%
filter(!(.phase == "transition" &
abs(x - lag(x)) <= sqrt(.Machine$double.eps) &
abs(y - lag(y)) <= sqrt(.Machine$double.eps)))
all_frames
}
)
We can define an alternate version of transition_reveal
that uses the above instead:
transition_reveal2 <- function (along, range = NULL, keep_last = TRUE) {
along_quo <- enquo(along)
gganimate:::require_quo(along_quo, "along")
ggproto(NULL, TransitionReveal2, # instead of TransitionReveal
params = list(along_quo = along_quo, range = range, keep_last = keep_last))
}
Demonstrate with the original data:
temp_plot + transition_reveal2(x)
