使用 gganimate 显示补间数据的计算

Use gganimate to display calculation of tweened data

我想使用 gganimate 来:

如果移动是单个(x,y)坐标线性移动就简单了(只要在每个阶段提前计算好然后动画,它会从每个阶段线性移动到下一个阶段) ,但如果不是,我不知道该怎么做。如果我在 aes() 中调用一个函数,这似乎是自然的解决方案,它似乎在开始时计算一次,然后在行移动时不更新它。

这是一个例子。

library(tidyverse)
library(gganimate)

# A function to find the x and y coordinate of the minimum y value of either set
min_of_both <- function(x1, y1, x2, y2) {
  cm <- bind_rows(tibble(x = x1, y = y1),
                  tibble(x = x2, y = y2))

  return(list(x = cm[which(cm$y == min(cm$y)),]$x,
              y = min(cm$y)))
}

# Create two parabola paths, curve A which moves downwards from t = 1 to t = 2
curveA <- tibble(xA = -50:50/10, yA = 5+(-50:50/10)^2, t = 1) %>%
  bind_rows(tibble(xA = -50:50/10, yA = -10 + (-50:50/10)^2, t = 2))
# And curve B which is static in both time 1 and 2
curveB <- tibble(xB = -50:50/10, yB = 1 + (-30:70/10)^2)

data <- curveB %>%
  bind_rows(curveB) %>%
  bind_cols(curveA)

# Plot Curve A
p <- ggplot(data, aes(x = xA, y = yA)) + 
  geom_path(color = 'red') +
  # And Curve B
  geom_path(aes(x=xB,y=yB), color = 'blue')+
  # Then plot a single point that uses both curves as input
  # Note I also get problems if trying to run the function through data= instead of mapping=
  # or if I define two separate functions, one for x and one for y, so as to avoid $
  geom_point(aes(
    x = min_of_both(xA,yA,xB,yB)$x, 
    y = min_of_both(xA,yA,xB,yB)$y), 
    size = 3,
    color = 'black') +
  theme_minimal()+
  transition_states(t)+
  ease_aes('sine-in-out')
animate(p)

这导致(不确定动画是否会在 Whosebug 上播放,但抛物线确实移动):

黑点是用来标记每条抛物线在每个时刻的最低 y 坐标的,但它却标记了动画中任意点(在结尾处)任何抛物线上的最低 y 坐标。

感谢任何提示。

经过一番摸索之后,我想我已经理解您的观点并找到了一个解决方案。最好的方法可能是手动 tween 路径并使用您的函数计算最小值,同时在绘图之前按 .frame 分组:

# Same curve setup, but labelling points for grouping later
curveA <- tibble(xA = -50:50/10, 
                 yA = 5+(-50:50/10)^2, 
                 point = 1:101,
                 t = 1) %>%
  bind_rows(tibble(xA = -50:50/10, 
                   yA = -10 + (-50:50/10)^2,
                   point = 1:101,
                   t = 2))

curveB <- tibble(xB = -50:50/10, 
                 yB = 1 + (-30:70/10)^2,
                 point = 1:101,
                 t = 1)


A_frames <-  curveA %>%
  tween_along(ease = 'sine-in-out', 100, along = t, id = point) %>% 
  filter(.phase == "transition") %>% 
  select(xA, yA, point, .frame) %>% 
  arrange(.frame, point)  # arrange by point needed to keep in order

B_frames <-  curveB %>%
  bind_rows(curveB %>% mutate(t = 2)) %>% 
  tween_along(ease = 'sine-in-out', 100, along = t, id = point) %>% 
  filter(.phase == "transition") %>% 
  select(xB, yB, point, .frame) %>%
  arrange(.frame, point)


data <- A_frames %>%
  left_join(B_frames, by = c(".frame", "point")) %>% 
  group_by(.frame) %>% 
  mutate(xmin = min_of_both(xA,yA,xB,yB)$x,
         ymin = min_of_both(xA,yA,xB,yB)$y)

# Plot Curve A
p <- ggplot(data, aes(x = xA, y = yA)) + 
  geom_path(color = 'red') +
  # And Curve B
  geom_path(aes(x=xB,y=yB), color = 'blue')+
  # Then plot a single point that uses both curves as input
  # Note I also get problems if trying to run the function through data= instead of mapping=
  # or if I define two separate functions, one for x and one for y, so as to avoid $
  geom_point(aes(xmin, ymin), 
             size = 3,
             color = 'black') +
  theme_minimal()+
  transition_states(.frame)+
  ease_aes('sine-in-out')

animate(p, fps = 24)