-
Notifications
You must be signed in to change notification settings - Fork 309
World Cup Goal Animation
submitted by Ryo Nakagawara
Using ggplot2
and gganimate
as the base we can even recreate our favorite goals from the World Cup with R!
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
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
)
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)
Install gganimate using devtools::install_github('thomasp85/gganimate')
The Grammar
Misc
Examples