首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用R语言点亮专属烟花

用R语言点亮专属烟花

作者头像
医小北同学
发布2026-02-28 18:34:25
发布2026-02-28 18:34:25
710
举报
文章被收录于专栏:技术学习技术学习

2026年2月15日 · 长春

用R语言点亮专属烟花(参考)

今年过年没回家,享受一下在长春过春节的幸福时光吧。提前祝大家新春快乐!

1、加载包

代码语言:javascript
复制
library(dplyr)
library(ggplot2)
library(gganimate)
library(gifski)
library(transformr)
set.seed(2026)  # 马年专属种子

2、定义烟花配色

代码语言:javascript
复制
# 定义三种菊花形烟花的配色
firework_shapes <- list(
  chrysanthemum_blue = list(name = "蓝色菊花型", colors = c("#00CCFF", "#33DDFF", "#99EEFF", "#CCF5FF")),
  chrysanthemum_yellow = list(name = "黄色菊花型", colors = c("#FFD700", "#FFE033", "#FFEB66", "#FFF5CC")),
  chrysanthemum_red = list(name = "红色菊花型", colors = c("#FF3333", "#FF6666", "#FF9999", "#FFCCCC")),
  line = list(name = "线条型", colors = c("#FFFFFF", "#CCCCCC", "#999999"))
)

3、定义烟花位置和类型

代码语言:javascript
复制
# 预定义每个烟花的位置和类型 - 打乱顺序
firework_positions <- list(
  # 菊花型烟花 - 不同颜色分布在画布不同位置
  chrysanthemum_yellow1 = list(x = -12, y_base = 6, shape = "chrysanthemum_yellow"),
  line1 = list(x = -15, y_base = 4, shape = "line"),
  chrysanthemum_blue1 = list(x = -5, y_base = 9, shape = "chrysanthemum_blue"),
  chrysanthemum_red1 = list(x = 3, y_base = 7, shape = "chrysanthemum_red"),
  line2 = list(x = -8, y_base = 5, shape = "line"),
  chrysanthemum_yellow2 = list(x = 8, y_base = 8, shape = "chrysanthemum_yellow"),
  chrysanthemum_blue2 = list(x = 15, y_base = 6, shape = "chrysanthemum_blue"),
  chrysanthemum_red2 = list(x = 0, y_base = 10, shape = "chrysanthemum_red"),
  line3 = list(x = 8, y_base = 5, shape = "line"),
  line4 = list(x = 15, y_base = 4, shape = "line")
)

4、生成烟花数据

代码语言:javascript
复制
# 主数据框
all_particles <- data.frame()
trail_particles <- data.frame()
# 烟花ID计数器
fw_id <- 1

# 为每个烟花生成数据
for(pos_name in names(firework_positions)) {
  pos <- firework_positions[[pos_name]]
  shape_type <- pos$shape

  shape_info <- firework_shapes[[shape_type]]

# 差异化参数
  launch_x <- pos$x
  launch_y_start <- -8
  explode_y <- pos$y_base + runif(1, -1, 1)

# 打乱爆炸顺序 - 更大的延迟范围
  launch_delay <- sample(0:120, 1)
  rise_dur <- sample(25:35, 1)

# 根据烟花类型确定粒子数和参数
if(shape_type == "chrysanthemum_blue") {
    n_particles <- 400  # 蓝色菊花型粒子数最多
    speed_range <- c(0.02, 0.30)  # 蓝色速度最大,半径最大
    resistance <- 0.0006
    gravity <- 0.0
    particle_size <- 1.6
    explosion_duration <- 90  # 蓝色持续时间最长
    
  } elseif(shape_type == "chrysanthemum_yellow") {
    n_particles <- 350  # 黄色菊花型粒子数稍少
    speed_range <- c(0.015, 0.22)  # 黄色速度和半径比蓝色小
    resistance <- 0.0008
    gravity <- 0.0
    particle_size <- 1.5
    explosion_duration <- 75  # 黄色持续时间比蓝色短
    
  } elseif(shape_type == "chrysanthemum_red") {
    n_particles <- 300  # 红色菊花型粒子数最少
    speed_range <- c(0.01, 0.18)  # 红色速度和半径最小
    resistance <- 0.0010
    gravity <- 0.0
    particle_size <- 1.4
    explosion_duration <- 60  # 红色持续时间最短
    
  } else {  # 线条型
    n_particles <- 150
    speed_range <- c(0.01, 0.02)
    resistance <- 0.0025
    gravity <- 0.007
    particle_size <- 2.2
    explosion_duration <- 60
  }

# 生成基础粒子数据
  particle_data <- data.frame(
    firework_id = fw_id,
    shape_type = shape_type,
    particle_id = 1:n_particles,
    launch_x = launch_x,
    explode_y = explode_y,
    launch_delay = launch_delay,
    rise_dur = rise_dur,
    speed_range_min = speed_range[1],
    speed_range_max = speed_range[2],
    resistance = resistance,
    gravity = gravity,
    particle_size = particle_size,
    explosion_duration = explosion_duration
  )

# 根据形状计算角度和速度分布
  particle_data <- particle_data %>%
    mutate(
      angle = case_when(
        shape_type %in% c("chrysanthemum_blue", "chrysanthemum_yellow", "chrysanthemum_red") ~ {
          # 菊花型分为5层
          layer <- sample(1:5, n(), replace = TRUE, prob = c(0.1, 0.2, 0.3, 0.25, 0.15))
          base_angle <- runif(n(), 0, 2*pi)
          base_angle + layer * 0.35
        },
        
        shape_type == "line" ~ rep(3*pi/2, n()) + rnorm(n(), 0, 0.05)
      ),
      
      # 修复:使用向量化方法而不是if语句
      speed = ifelse(
        shape_type %in% c("chrysanthemum_blue", "chrysanthemum_yellow", "chrysanthemum_red"),
        {
          # 先分配层
          layer <- sample(1:5, n(), replace = TRUE, prob = c(0.1, 0.2, 0.3, 0.25, 0.15))
          
          # 根据烟花类型和层分配基础速度
          base_speeds <- numeric(n())
          
          # 蓝色菊花型
          blue_mask <- shape_type == "chrysanthemum_blue"
          if(any(blue_mask)) {
            blue_layers <- layer[blue_mask]
            blue_speeds <- c(0.02, 0.08, 0.18, 0.25, 0.32)
            base_speeds[blue_mask] <- blue_speeds[blue_layers] * runif(sum(blue_mask), 0.8, 1.2)
          }
          
          # 黄色菊花型
          yellow_mask <- shape_type == "chrysanthemum_yellow"
          if(any(yellow_mask)) {
            yellow_layers <- layer[yellow_mask]
            yellow_speeds <- c(0.015, 0.06, 0.14, 0.20, 0.25)
            base_speeds[yellow_mask] <- yellow_speeds[yellow_layers] * runif(sum(yellow_mask), 0.8, 1.2)
          }
          
          # 红色菊花型
          red_mask <- shape_type == "chrysanthemum_red"
          if(any(red_mask)) {
            red_layers <- layer[red_mask]
            red_speeds <- c(0.01, 0.05, 0.12, 0.18, 0.22)
            base_speeds[red_mask] <- red_speeds[red_layers] * runif(sum(red_mask), 0.8, 1.2)
          }
          
          base_speeds
        },
        # 线条型的速度
        runif(n(), speed_range_min, speed_range_max)
      ),
      
      color = sample(shape_info$colors, n(), replace = TRUE)
    )

# 为菊花型记录层级信息
if(shape_type %in% c("chrysanthemum_blue", "chrysanthemum_yellow", "chrysanthemum_red")) {
    particle_data <- particle_data %>%
      mutate(
        chrysanthemum_layer = sample(1:5, n(), replace = TRUE, prob = c(0.1, 0.2, 0.3, 0.25, 0.15))
      )
  } else {
    particle_data <- particle_data %>%
      mutate(chrysanthemum_layer = 0)
  }

# 扩展到所有帧并计算轨迹
  particle_data <- particle_data %>%
    slice(rep(1:n(), each = total_frames)) %>%
    mutate(
      frame = rep(1:total_frames, times = n_particles),
      effective_frame = frame - launch_delay,
      is_rising = effective_frame <= rise_dur & effective_frame > 0,
      is_exploding = effective_frame > rise_dur,
      explode_time = effective_frame - rise_dur,
      
      # 位置计算
      x = case_when(
        effective_frame <= 0 ~ launch_x,
        is_rising ~ launch_x + rnorm(n(), 0, 0.015),
        shape_type %in% c("chrysanthemum_blue", "chrysanthemum_yellow", "chrysanthemum_red") ~ {
          # 菊花形:运动半径根据烟花类型调整
          launch_x + cos(angle) * speed * explode_time * (1 - resistance * explode_time)
        },
        TRUE ~ launch_x + cos(angle) * speed * explode_time * (1 - resistance * explode_time)
      ),
      
      y = case_when(
        effective_frame <= 0 ~ launch_y_start,
        is_rising ~ launch_y_start + (explode_y - launch_y_start) * (effective_frame / rise_dur) + rnorm(n(), 0, 0.005),
        shape_type == "line" ~ explode_y + sin(angle) * speed * explode_time - gravity * explode_time^2,
        # 菊花形:爆炸过程中没有下落
        TRUE ~ explode_y + sin(angle) * speed * explode_time * (1 - resistance * explode_time)
      ),
      
      # 大小变化
      size = case_when(
        effective_frame <= 0 ~ 0,
        is_rising ~ particle_size * 0.3,
        explode_time <= explosion_duration ~ particle_size,
        explode_time <= (explosion_duration + 40) ~ particle_size - (explode_time - explosion_duration) * 0.025,
        TRUE ~ particle_size * 0.3
      ),
      
      # 透明度变化
      alpha = case_when(
        effective_frame <= 0 ~ 0,
        is_rising ~ 0.7,
        explode_time <= explosion_duration ~ 1.0,
        explode_time <= (explosion_duration + 40) ~ 1 - (explode_time - explosion_duration)/40,
        TRUE ~ 0
      )
    ) %>%
    mutate(
      size = pmax(size, 0.1),
      alpha = pmax(pmin(alpha, 1), 0),
      x = pmin(pmax(x, -35), 35),
      y = pmin(pmax(y, -12), 18)
    ) %>%
    filter(effective_frame <= 180, effective_frame >= -5)

# 生成拖尾粒子数据
if(nrow(particle_data) > 0) {
    explosion_particles <- particle_data %>%
      filter(is_exploding, explode_time > 0, explode_time <= 150)
    
    if(nrow(explosion_particles) > 0) {
      # 根据烟花类型确定拖尾层数
      if(shape_type %in% c("chrysanthemum_blue", "chrysanthemum_yellow", "chrysanthemum_red")) {
        # 菊花形:更长的拖尾
        tail_layers <- 10
        tail_decay_rate <- 0.6
      } else {
        # 线条型:较短拖尾
        tail_layers <- 5
        tail_decay_rate <- 0.8
      }
      
      # 创建拖尾数据
      tail_data_list <- list()
      
      for(i in 1:tail_layers) {
        tail_data <- explosion_particles %>%
          group_by(firework_id, particle_id) %>%
          arrange(frame) %>%
          mutate(
            tail_x = lag(x, i),
            tail_y = lag(y, i),
            # 拖尾大小和透明度随层数衰减
            tail_size = particle_size * (tail_decay_rate^i),
            tail_alpha = alpha * (tail_decay_rate^i) * 0.7
          ) %>%
          ungroup() %>%
          filter(!is.na(tail_x)) %>%
          select(frame, firework_id, particle_id, 
                 x = tail_x, y = tail_y, 
                 size = tail_size, alpha = tail_alpha, color, shape_type) %>%
          mutate(tail_layer = i)
        
        tail_data_list[[i]] <- tail_data
      }
      
      # 合并所有拖尾数据
      tail_points <- bind_rows(tail_data_list)
      trail_particles <- bind_rows(trail_particles, tail_points)
    }
  }

# 添加主粒子数据
  all_particles <- bind_rows(all_particles, particle_data)
  fw_id <- fw_id + 1
}

5、绘制动画

代码语言:javascript
复制
# 绘制动画
p <- ggplot() +
# 1. 先绘制拖尾粒子(放在底层)
  geom_point(
    data = trail_particles,
    aes(x = x, y = y, color = color, size = size, alpha = alpha),
    shape = 16
  ) +
# 2. 再绘制主粒子(放在上层)
  geom_point(
    data = all_particles,
    aes(x = x, y = y, color = color, size = size, alpha = alpha),
    shape = 16
  ) +
  scale_color_identity() +
  scale_size_identity() +
  scale_alpha_identity() +
  coord_cartesian(xlim = canvas_xlim, ylim = canvas_ylim) +
  theme_void() +
  theme(
    legend.position = "none",
    plot.background = element_rect(fill = "#000033", color = NA),
    panel.background = element_rect(fill = "#000033", color = NA),
    plot.title = element_text(color = "gold", size = 24, hjust = 0.5, face = "bold", margin = margin(t = 10)),
    plot.subtitle = element_text(color = "lightblue", size = 16, hjust = 0.5, margin = margin(t = 5, b = 10))
  ) +
  transition_time(frame) +
  ease_aes('cubic-out')

6、渲染动画并保存

代码语言:javascript
复制
animate(
  p,
  nframes = total_frames,
  fps = fps,
  width = 1200,
  height = 900,
  renderer = gifski_renderer(loop = TRUE),
  detail = 2
)
anim_save("专属烟花.gif", fps =20, width =1000, height =600, res =150)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-02-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 医小北 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用R语言点亮专属烟花(参考)
    • 1、加载包
    • 2、定义烟花配色
    • 3、定义烟花位置和类型
    • 4、生成烟花数据
    • 5、绘制动画
    • 6、渲染动画并保存
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档