使用gganimate与geom_sf时的颜色填充异常问题
Let's break down why your animation only shows correct colors at the end, and fix it step by step:
Problem Root Cause
Your static plot works because you're directly referencing precincts$margin for fill, but when you add transition_states(precincts$margingroup), gganimate splits your data by margingroup for each animation frame. Cross-referencing an external data frame (precincts) inside aes() breaks the per-frame data mapping—only when all frames are rendered (at the end of the animation) do all color values load correctly. Additionally, using precincts$margingroup for the group aesthetic in geom_sf (which uses centroids as its data source) creates a mismatch between the data being plotted and the grouping variable.
Step-by-Step Solution
1. Align Your Data Frames
First, merge the margin, margingroup, and dotsize columns from precincts into your centroids data frame. This ensures all variables used in geom_sf's aesthetics are from the same data source:
# Replace "common_id_column" with the shared identifier between centroids and precincts (e.g., precinct ID) centroids <- centroids %>% left_join(precincts %>% select(common_id_column, margin, margingroup, dotsize), by = "common_id_column")
2. Update Static Plot Aesthetics
Modify your geom_sf call to use columns directly from centroids instead of referencing precincts externally. This keeps the data mapping consistent for animation:
precincts$margingroup <- cut(precincts$margin, breaks = breaks, labels = c(1:37)) # Merge data first (as step 1) # ... pointfig <- ggmap(myMap) + geom_sf(data=centroids, aes(fill=margin, group=margingroup), size=dotsize, pch=21, alpha=1, inherit.aes = FALSE) + scale_fill_gradient2(midpoint = 0, low='darkmagenta', mid='white', high='orange', limits = c(-50,50), oob = scales::squish) + geom_shadowtext(mapping = aes(x = longitude, y = latitude, label = name, vjust=vjust), data = places, size = 5, fontface = "bold") + labs(fill='Fidesz Margin, %',caption = "Data from valasztas.hu") + ggtitle("Borsod-Abaúj-Zemplén 6th District By-Election by Precinct") + theme_void() + theme(legend.box.just = "center") + theme(plot.title = element_text(size = 12, face = "bold", vjust=4)) + theme(plot.margin = unit(c(1,1,1,1), "cm")) + coord_sf()
(Note: I fixed a typo in scale_fill_gradient2—you had min='white' which should be mid='white' to match the gradient2 syntax)
3. Adjust Animation Code to Use Centroid Data
Update transition_states to reference the margingroup column in centroids (now part of the data frame used by geom_sf), and tweak shadow_wake to avoid overriding fill colors:
anim <- pointfig + transition_states(margingroup, transition_length = 1, state_length = 0.45, wrap = FALSE) + shadow_wake(wake_length = 0.1, alpha = TRUE, fill = FALSE) # Add fill=FALSE to keep shadow from affecting fill color
The fill=FALSE argument ensures the wake effect only applies to the point's border, not its fill, preventing unintended color distortion during transitions.
4. Verify Ordered States (Optional)
Ensure margingroup is an ordered factor to guarantee the animation plays in the correct margin order:
centroids$margingroup <- factor(centroids$margingroup, levels = c(1:37), ordered = TRUE)
Testing
Run the updated code—your animation should now display the correct fill color for each group as it transitions, rather than only at the end.
内容的提问来源于stack exchange,提问作者Owen Winter

