Files
anishio/docs/icon.md
T

1.8 KiB

Animated Icon Plan

Animate a Discord emote (GIF) as the app's window/taskbar icon using eframe/egui.

Steps

1. Add the image crate

cargo add image

2. Place your GIF

Save your Discord emote as assets/emote.gif in the project root.

3. Load GIF frames

use eframe::egui::{ViewportCommand, IconData};
use image::codecs::gif::GifDecoder;
use image::AnimationDecoder;
use std::time::Instant;

fn load_gif_frames(gif_bytes: &[u8]) -> Vec<IconData> {
    let decoder = GifDecoder::new(gif_bytes).unwrap();
    decoder.into_frames()
        .collect_frames()
        .unwrap()
        .into_iter()
        .map(|f| {
            let rgba = f.into_buffer();
            let (w, h) = (rgba.width(), rgba.height());
            IconData { rgba: rgba.into_raw(), width: w, height: h }
        })
        .collect()
}

Embed the GIF at compile time:

let gif_bytes = include_bytes!("../assets/emote.gif");
let icon_frames = load_gif_frames(gif_bytes);

4. Add fields to the app struct

icon_frames: Vec<IconData>,
current_frame: usize,
last_icon_swap: Instant,

5. Cycle the icon in update()

if !self.icon_frames.is_empty() {
    let elapsed = self.last_icon_swap.elapsed().as_millis();
    if elapsed > 100 { // ~10 FPS
        self.current_frame = (self.current_frame + 1) % self.icon_frames.len();
        let icon = self.icon_frames[self.current_frame].clone();
        ctx.send_viewport_cmd(ViewportCommand::Icon(Some(std::sync::Arc::new(icon))));
        self.last_icon_swap = Instant::now();
        ctx.request_repaint();
    }
}

Notes

  • The icon animates in both the taskbar and the window title bar on Windows.
  • Adjust the 100ms interval to control animation speed.
  • Discord emotes are typically small (48x48 or 128x128) which works fine for icons.