Skip to content

World Cup Goal Animation

Thomas Lin Pedersen edited this page Sep 4, 2018 · 2 revisions

submitted by Ryo Nakagawara

Using ggplot2 and gganimate as the base we can even recreate our favorite goals from the World Cup with R!

The Packages

library(ggplot2)    # general plotting base
library(gganimate)  # animate goal plots
library(dplyr)      # data manipulation/tidying
library(ggsoccer)   # draw soccer field plot
library(ggimage)    # add soccer ball emoji + flags
library(extrafont)  # incorporate Dusha font into plots
library(tweenr)     # create in-between frames for data
# loadfonts()         run once every new session

The Data

With actual player position data not easily available to the layman I ended up just creating the data points manually and timing it myself.

pass_data <- data.frame(
  x = c(100, 94, 82, 82.5,  84, 76.5, 75.5, 94, 99.2),     
  y = c(0,   35, 31, 22,     8, 13, 19, 60, 47.5),
  time = c(1, 2, 3, 4, 5, 6, 7, 8, 9))

golovin_movement <- data.frame(
  x = c(78, 80, 80, 80, 75.5, 74.5, 73.5, 73, 73),  
  y = c(30, 30, 27, 25,   10,    9, 15, 15, 15),
  label = "Golovin",
  time = c(1, 2, 3,  4,  5,  6,  7,  8,  9)
)

zhirkov_movement <- data.frame(
  x = c(98, 90, 84, 84, 84, 84, 84, 84, 84),
  y = c( 0,  2,  2,  2,  2,  2,  2,  2,  2),
  label = "Zhirkov",
  time = c(1, 2, 3, 4, 5, 6, 7, 8, 9)
)

gazinsky_movement <- data.frame(
  x = c(0, 0, 0, 0, NA, 92,   92,   92,   92),
  y = c(0, 0, 0, 0, NA, 66.8, 66.8, 66.8, 66.8),
  label = "Gazinsky",
  time = c(1, 2, 3, 4, 5, 6, 7, 8, 9)
)

saudi_data <- data.frame(
  x = c(95, 95, 90, 87, 84, 80, 79, 79, 79),
  y = c(35, 35, 35, 32, 28, 25, 24, 25, 26),
  label = "M. Al-Breik",
  time = c(1, 2, 3, 4, 5, 6, 7, 8, 9)
)

ball_data <- tibble::tribble(
  ~x,  ~y, ~time,
  100,   0,   1,
  94,   35,   2,
  82,   31,   3,
  82.5, 25,   4,
  84,    6,   5, 
  77,   13,   6,
  76,   19,   7,
  94,   60,   8,
  99.2, 47.5, 9
) 

Plot

After setting up the soccer field / football pitch, we add in the player labels and the soccer ball! I set the state_length and transition length arguments to a very low value as this passage of play happens quite quickly. I set the wrap argument to FALSE as I do not want the last frame (the header) to transition back to the first frame (the corner kick).

NOTE: To use the font "Dusha" you need to download the .TFF file from here.

ggplot(pass_data) +
  annotate_pitch() +
  theme_pitch() +
  coord_flip(xlim = c(49, 101),
             ylim = c(-1, 101)) +
  geom_label(
    data = saudi_data,
    aes(x = x, y = y,
        label = label),
    color = "darkgreen") +
  geom_label(
    data = zhirkov_movement,
    aes(x = x, y = y,
        label = label),
    color = "red") +
  geom_label(
    data = golovin_movement,
    aes(x = x, y = y,
        label = label),
    color = "red") +
  geom_label(
    data = gazinsky_movement,
    aes(x = x, y = y,
        label = label),
    color = "red") +
  ggimage::geom_emoji(
    data = ball_data,
    aes(x = x, y = y),   
    image = "26bd", size = 0.035) +
  ggtitle(
    label = "Russia (5) vs. (0) Saudi Arabia", 
    subtitle = "First goal, Yuri Gazinsky (12th Minute)") +
  annotate(
    "text", x = 69, y = 65, family = "Dusha V5",
    label = "After a poor corner kick clearance\n from Saudi Arabia, Golovin picks up the loose ball, \n exchanges a give-and-go pass with Zhirkov\n before finding Gazinsky with a beautiful cross!") +
  theme(text = element_text(family = "Dusha V5")) +
  # new gganimate code:
  transition_states(
    time, 
    transition_length = 0.5, 
    state_length = 0.0001,
    wrap = FALSE)

gazinsky_sans_caption

Source

Animating the Goals of the World Cup with R!