You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让gganimate竞赛条形图过渡时显示中间数值?

问题描述

我用gganimate制作竞赛条形图,展示赛季各月总进球数的演化过程,目前输出效果接近预期,但最后一个需求卡壳了:想让geom_text在过渡过程中显示中间数值,比如不要直接从7跳到12,而是在条形调整时依次显示7、8、9、10、11。以下是我的数据样本和绘图代码:

df2<-structure(list(Season = c("2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024", 
"2023/2024", "2023/2024", "2023/2024", "2023/2024", "2023/2024"
), DATEMM = structure(c(2023.5, 2023.5, 2023.5, 2023.5, 2023.5, 
2023.5, 2023.5, 2023.5, 2023.58333333333, 2023.58333333333, 2023.58333333333, 
2023.58333333333, 2023.58333333333, 2023.58333333333, 2023.58333333333, 
2023.58333333333, 2023.66666666667, 2023.66666666667, 2023.66666666667, 
2023.66666666667, 2023.66666666667, 2023.66666666667, 2023.66666666667, 
2023.66666666667, 2023.75, 2023.75, 2023.75, 2023.75, 2023.75, 
2023.75, 2023.75, 2023.75, 2023.83333333333, 2023.83333333333, 
2023.83333333333, 2023.83333333333, 2023.83333333333, 2023.83333333333, 
2023.83333333333, 2023.83333333333, 2023.91666666667, 2023.91666666667, 
2023.91666666667, 2023.91666666667, 2023.91666666667, 2023.91666666667, 
2023.91666666667, 2023.91666666667), class = "yearmon"), League = c("Super League", 
"Jupiler Pro League", "Bundesliga", "Liga", "LigaNOS", "Ligue 1", 
"Premier League", "Serie A", "Super League", "Premier League", 
"LigaNOS", "Ligue 1", "Liga", "Bundesliga", "Serie A", "Jupiler Pro League", 
"Liga", "Premier League", "LigaNOS", "Bundesliga", "Super League", 
"Serie A", "Ligue 1", "Jupiler Pro League", "Liga", "Premier League", 
"Bundesliga", "LigaNOS", "Serie A", "Super League", "Ligue 1", 
"Jupiler Pro League", "Premier League", "Liga", "Bundesliga", 
"Serie A", "LigaNOS", "Super League", "Ligue 1", "Jupiler Pro League", 
"Premier League", "Liga", "Bundesliga", "Serie A", "LigaNOS", 
"Ligue 1", "Super League", "Jupiler Pro League"), goals = c(36, 
21, 0, 0, 0, 0, 0, 0, 94, 86, 85, 85, 80, 65, 52, 38, 210, 206, 
185, 158, 155, 148, 137, 84, 312, 300, 271, 246, 237, 217, 212, 
139, 401, 392, 353, 314, 292, 290, 267, 207, 607, 479, 454, 429, 
392, 358, 339, 316), rank = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 
1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 
1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 
1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L), image_file = c("~/lglogos/Super League.png", 
"~/lglogos/Jupiler Pro League.png", "~/lglogos/Bundesliga.png", 
"~/lglogos/Liga.png", "~/lglogos/LigaNOS.png", "~/lglogos/Ligue 1.png", 
"~/lglogos/Premier League.png", "~/lglogos/Serie A.png", "~/lglogos/Super League.png", 
"~/lglogos/Premier League.png", "~/lglogos/LigaNOS.png", "~/lglogos/Ligue 1.png", 
"~/lglogos/Liga.png", "~/lglogos/Bundesliga.png", "~/lglogos/Serie A.png", 
"~/lglogos/Jupiler Pro League.png", "~/lglogos/Liga.png", "~/lglogos/Premier League.png", 
"~/lglogos/LigaNOS.png", "~/lglogos/Bundesliga.png", "~/lglogos/Super League.png", 
"~/lglogos/Serie A.png", "~/lglogos/Ligue 1.png", "~/lglogos/Jupiler Pro League.png", 
"~/lglogos/Liga.png", "~/lglogos/Premier League.png", "~/lglogos/Bundesliga.png", 
"~/lglogos/LigaNOS.png", "~/lglogos/Serie A.png", "~/lglogos/Super League.png", 
"~/lglogos/Ligue 1.png", "~/lglogos/Jupiler Pro League.png", 
"~/lglogos/Premier League.png", "~/lglogos/Liga.png", "~/lglogos/Bundesliga.png", 
"~/lglogos/Serie A.png", "~/lglogos/LigaNOS.png", "~/lglogos/Super League.png", 
"~/lglogos/Ligue 1.png", "~/lglogos/Jupiler Pro League.png", 
"~/lglogos/Premier League.png", "~/lglogos/Liga.png", "~/lglogos/Bundesliga.png", 
"~/lglogos/Serie A.png", "~/lglogos/LigaNOS.png", "~/lglogos/Ligue 1.png", 
"~/lglogos/Super League.png", "~/lglogos/Jupiler Pro League.png"
)), row.names = c(NA, -48L), class = c("tbl_df", "tbl", "data.frame"
))

绘图代码:

colpal<-c('Ligue 1'="#E5174F",'Premier League' ="#99CBE7", 'Liga' = "#122623", 'Bundesliga' = "#00bbf9ff", 'Serie A' ="#3469A6",
          "LigaNOS" = "#5EB1BF","Jupiler Pro League" ="#F6AE2D","Super League" = "#B3001B")
colpal.order<-c('Ligue 1','Premier League', 'Liga', 'Bundesliga', 'Serie A',"LigaNOS","Jupiler Pro League",
                "Super League")

gfb<-ggplot(df2, aes(rank,group=League,fill=factor(League,levels = colpal.order),
                     color = as.factor(League)))+
  geom_tile(aes(y=goals/2,x=rank,height=goals,width = 0.5),alpha = 0.8, color = NA) +
    ggtext::geom_richtext(
    aes(
      y = 0,
      x = rank,
      label = sprintf("<img src='%s' width='70'/>", image_file)),
    hjust = 1,
    inherit.aes = FALSE,
    fill = NA,
    color = NA,
    label.padding = grid::unit(30, "pt"))+
  scale_fill_manual(values = colpal)+
  scale_color_manual(values = colpal) +
  coord_flip(clip="off",expand=FALSE) +
  scale_y_continuous(labels = scales::comma) +
  scale_x_reverse()+
  geom_text(aes(y=goals, label= ifelse(goals > 0, sprintf("%1.0f", goals), "")),
            color="white",fontface = "bold", size = 20,family="Archivo Black",vjust = 0.5, hjust = 1)+
  guides(color = guide_none())+
  theme_minimal()+
  theme( panel.grid = element_blank(),
         panel.grid.major.x = element_line( linewidth = .5, color="#122623"),
         panel.background = element_rect(colour = NA,fill="transparent"),
         legend.position = "none", 
         axis.ticks.y = element_blank(),
         axis.title.y = element_blank(),
         axis.text.y = element_blank(),
         axis.ticks.x = element_blank(), 
         axis.title.x = element_blank(), 
         axis.text.x = element_text(size=20,face = 'bold'),
         plot.margin = margin(8, 6, 8, 6, "cm"),
         plot.background = element_rect(fill = "#ffffff", color = NA), 
         plot.title = element_text(size = 28, hjust = 0.5, face = 'bold', color = "#21FA90", vjust = 5,family="Archivo Black"), 
         plot.subtitle = element_text(size = 17.5, hjust = 0.5, face = "italic", color = "grey", vjust = 5,family="Archivo Black"),
         plot.caption = element_text(size = 12.5, color = "grey", vjust = 0,family="Archivo Black"),
  ) 
  
  anim <- gfb +
    transition_states(DATEMM, state_length = 0) + 
    view_follow(
      fixed_y = c(0, NA),
      fixed_x = TRUE,
    )+
    enter_grow() +
    exit_shrink() +
        ease_aes('linear') +
    labs( title = "Goal scored this season", 
          subtitle = 'Month: {closest_state}', 
          caption = 'Source: Hedgeflare calculation') 
anim
  animate(anim, fps = 10, duration =07, width = 500, height = 1000, renderer = gifski_renderer("gganim1.gif"))

解决方案

要实现文本数值的平滑过渡显示,核心是让geom_text的标签跟随动画的插值帧动态计算当前数值,而不是直接使用原始数据中的离散值。具体调整如下:

1. 修改geom_text的标签逻辑

gganimate在transition_states中会对数值型变量自动进行线性插值(因为你用了ease_aes('linear')),直接对插值后的goals值取整,就能在过渡帧中显示中间整数。

将原代码中的geom_text部分替换为:

geom_text(aes(y=goals, label= ifelse(goals > 0, sprintf("%1.0f", round(goals)), "")),
          color="white",fontface = "bold", size = 20,family="Archivo Black",vjust = 0.5, hjust = 1)

2. 完整调整后的代码

替换后的完整绘图代码如下:

colpal<-c('Ligue 1'="#E5174F",'Premier League' ="#99CBE7", 'Liga' = "#122623", 'Bundesliga' = "#00bbf9ff", 'Serie A' ="#3469A6",
          "LigaNOS" = "#5EB1BF","Jupiler Pro League" ="#F6AE2D","Super League" = "#B3001B")
colpal.order<-c('Ligue 1','Premier League', 'Liga', 'Bundesliga', 'Serie A',"LigaNOS","Jupiler Pro League",
                "Super League")

gfb<-ggplot(df2, aes(rank,group=League,fill=factor(League,levels = colpal.order),
                     color = as.factor(League)))+
  geom_tile(aes(y=goals/2,x=rank,height=goals,width = 0.5),alpha = 0.8, color = NA) +
    ggtext::geom_richtext(
    aes(
      y = 0,
      x = rank,
      label = sprintf("<img src='%s' width='7
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:17:47