Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save bq-wrongway/5d98c493ca9787083b648fd939593bb3 to your computer and use it in GitHub Desktop.

Select an option

Save bq-wrongway/5d98c493ca9787083b648fd939593bb3 to your computer and use it in GitHub Desktop.
loading_wave_widget working
// use rand::Rng;
// use iced::time::{Duration, Instant};
// use iced::widget::canvas::{Frame, Geometry, Path, Program};
// use iced::widget::{button, canvas, center, column, text};
// use iced::{Element, Point, Rectangle, Renderer, Size, Theme};
// use iced::{Fill, Subscription, Task};
// const NUM_BARS: usize = 10;
// const MAX_BAR_HEIGHT: f32 = 0.7;
// const BAR_SPACING: f32 = 2.0;
// fn main() -> iced::Result {
// iced::application(
// NowPlayingApp::new,
// NowPlayingApp::update,
// NowPlayingApp::view,
// )
// .subscription(NowPlayingApp::subscription)
// .title("iced • Now Playing")
// .window_size([400.0, 200.0])
// .theme(|_| iced::Theme::GruvboxDark)
// .centered()
// .run()
// }
// #[derive(Debug, Clone)]
// pub enum Message {
// TogglePlay,
// Tick,
// Pulse,
// }
// #[derive(Debug)]
// struct NowPlaying {
// is_playing: bool,
// bars: [f32; NUM_BARS],
// target_bars: [f32; NUM_BARS],
// peaks: [f32; NUM_BARS],
// peak_hold_timers: [u8; NUM_BARS],
// }
// impl Default for NowPlaying {
// fn default() -> Self {
// // Start with all bars and targets at zero (empty)
// Self {
// is_playing: false,
// bars: [0.0; NUM_BARS],
// target_bars: [0.0; NUM_BARS],
// peaks: [0.0; NUM_BARS],
// peak_hold_timers: [0; NUM_BARS],
// }
// }
// }
// impl NowPlaying {
// fn create_wave(&mut self, center_index: usize, intensity: f32) {
// for i in 0..NUM_BARS {
// let distance = (i as isize - center_index as isize).abs() as f32;
// let wave_intensity = intensity * (-distance * 0.25).exp();
// if wave_intensity > 0.1 {
// let random_factor = 0.85 + rand::rng().random_range(0.0..0.35);
// self.target_bars[i] = self.target_bars[i].max(wave_intensity * random_factor);
// }
// }
// }
// }
// struct NowPlayingApp {
// state: NowPlaying,
// last_pulse_time: Instant,
// next_pulse_interval: Duration,
// }
// impl NowPlayingApp {
// fn new() -> (Self, Task<Message>) {
// // Create an initial wave using create_wave
// let mut state = NowPlaying::default();
// state.create_wave(NUM_BARS / 2, 0.8);
// (
// Self {
// state,
// last_pulse_time: Instant::now(),
// next_pulse_interval: Duration::from_millis(800),
// },
// Task::none(),
// )
// }
// fn subscription(&self) -> Subscription<Message> {
// iced::time::every(Duration::from_millis(16)).map(|_| Message::Tick)
// }
// fn update(&mut self, message: Message) -> Task<Message> {
// match message {
// Message::TogglePlay => {
// self.state.is_playing = !self.state.is_playing;
// }
// Message::Tick => {
// // --- Pulse scheduling logic (matches JS) ---
// if self.state.is_playing {
// let now = Instant::now();
// if now.duration_since(self.last_pulse_time) > self.next_pulse_interval {
// self.last_pulse_time = now;
// // Randomize next interval between 150-350ms
// let next_ms = 150 + (rand::rng().random_range(0.0..200.0)) as u64;
// self.next_pulse_interval = Duration::from_millis(next_ms);
// // Create 1-3 wave centers
// let num_waves = 1 + (rand::rng().random_range(0.0..3.0)) as usize;
// for _ in 0..num_waves {
// let center = rand::rng().random_range(0..NUM_BARS);
// let intensity = 0.6 + rand::rng().random_range(0.0..0.4);
// self.state.create_wave(center, intensity);
// }
// }
// }
// // Always update bars and peaks (decay/animation)
// for i in 0..NUM_BARS {
// // Smoothly move bars toward target
// let diff = self.state.target_bars[i] - self.state.bars[i];
// self.state.bars[i] += diff * 0.25;
// // Peak logic (ghost bar)
// if self.state.bars[i] > self.state.peaks[i] {
// self.state.peaks[i] = self.state.bars[i];
// self.state.peak_hold_timers[i] = 30; // Hold for ~0.5s at 60fps
// } else if self.state.peak_hold_timers[i] > 0 {
// self.state.peak_hold_timers[i] -= 1;
// } else {
// self.state.peaks[i] *= 0.95;
// }
// // Decay the target
// self.state.target_bars[i] *= 0.92;
// // Additional decay to bars
// self.state.bars[i] *= 0.96;
// // Clamp values
// self.state.bars[i] = self.state.bars[i].max(0.0).min(1.0);
// self.state.target_bars[i] = self.state.target_bars[i].max(0.0).min(1.0);
// self.state.peaks[i] = self.state.peaks[i].max(0.0).min(1.0);
// }
// }
// _ => {}
// }
// Task::none()
// }
// fn view(&self) -> Element<Message> {
// let content = column![
// button(
// text(
// if self.state.is_playing {
// "Pause"
// } else {
// "Play"
// }
// .to_uppercase()
// )
// .size(12)
// )
// .on_press(Message::TogglePlay),
// canvas(NowPlayingViewer {
// bars: self.state.bars,
// })
// .width(Fill)
// .height(Fill)
// ]
// .spacing(10)
// .padding(10);
// center(content).into()
// }
// }
// struct NowPlayingViewer {
// bars: [f32; NUM_BARS],
// }
// impl<Message> Program<Message> for NowPlayingViewer {
// type State = ();
// fn draw(
// &self,
// _state: &Self::State,
// renderer: &Renderer,
// theme: &Theme,
// bounds: Rectangle,
// _cursor: iced::mouse::Cursor,
// ) -> Vec<Geometry> {
// let mut frame = Frame::new(renderer, bounds.size());
// let bar_width = bounds.width / NUM_BARS as f32;
// let actual_bar_width = (bar_width - BAR_SPACING) / 2.;
// let palette = theme.extended_palette();
// let bar_color = palette.primary.base.color;
// for (i, &height) in self.bars.iter().enumerate() {
// let scaled_height = height * MAX_BAR_HEIGHT * bounds.height;
// let x = i as f32 * bar_width + BAR_SPACING ;
// let y = bounds.height - scaled_height;
// // Draw main bar
// // Create the Path for the rounded rectangle
// let rounded_rect_path = Path::new(|p| {
// p.rounded_rectangle(
// Point::new(x, y / 2.0),
// Size::new(actual_bar_width, scaled_height),
// 15.into(),
// );
// });
// // Draw (fill) the rounded rectangle
// frame.fill(&rounded_rect_path, bar_color);
// // let bar = Path::rectangle(
// // Point::new(x, y / 2.0),
// // Size::new(actual_bar_width, scaled_height),
// // );
// // Simple solid color for bars
// // frame.fill(&bar, bar_color);
// // Draw peak indicator
// // if self.peaks[i] > 0.01 {
// // let peak_y = bounds.height - (self.peaks[i] * MAX_BAR_HEIGHT * bounds.height);
// // let peak = Path::rectangle(
// // Point::new(x, peak_y - 2.0),
// // Size::new(actual_bar_width, 2.0),
// // );
// // frame.fill(&peak, peak_color);
// // }
// }
// vec![frame.into_geometry()]
// }
// }
// mod wave_loader;
use iced::{
widget::{button, canvas::{self, Canvas, Frame, Path, Program }, column, row}, Element, Length, Point, Renderer, Settings, Size, Subscription, Task, Theme
};
use std::time::{Duration, Instant};
use std::f32::consts::PI;
fn main() -> iced::Result {
iced::application(
SineWaveApp::new,
SineWaveApp::update,
SineWaveApp::view,
)
.subscription(SineWaveApp::subscription)
.title("iced • Now Playing")
.window_size([400.0, 200.0])
.theme(|_| iced::Theme::GruvboxDark)
.centered().run()
}
#[derive(Debug, Clone, Copy)]
enum Message {
Toggle,
Tick(Instant),
}
struct SineWaveApp {
playing: bool,
phase: f32,
last_tick: Instant,
}
impl SineWaveApp {
fn new() -> (Self, Task<Message>) {
(
SineWaveApp {
playing: false,
phase: 0.0,
last_tick: Instant::now(),
},
Task::none(),
)
}
fn title(&self) -> String {
String::from("Iced Sinewave Widget [master]")
}
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Toggle => {
self.playing = !self.playing;
}
Message::Tick(now) => {
if self.playing {
let delta = now.duration_since(self.last_tick).as_secs_f32();
self.phase = (self.phase + delta * 2.0 * PI * 0.6) % (2.0 * PI); // 0.5 Hz
}
self.last_tick = now;
}
}
Task::none()
}
fn view(&self) -> Element<Message> {
let sine_widget = Canvas::new(SineWaveWidget { phase: self.phase })
.width(Length::Fill)
.height(Length::FillPortion(3));
let btn = button(if self.playing { "Pause" } else { "Play" })
.on_press(Message::Toggle);
column![
sine_widget,
row![btn].spacing(20),
]
.padding(20)
.spacing(20)
.into()
}
fn subscription(&self) -> Subscription<Message> {
iced::time::every(Duration::from_millis(16))
.map(Message::Tick)
}
}
struct SineWaveWidget {
phase: f32,
}
impl<Message> Program<Message> for SineWaveWidget {
type State = ();
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
theme: &Theme,
bounds: iced::Rectangle,
_cursor: iced::mouse::Cursor,
) -> Vec<canvas::Geometry> {
let mut frame = Frame::new(renderer,bounds.size());
let width = bounds.width;
let height = bounds.height;
let loader_width = width * 0.8;
let loader_height = height * 0.6;
let x_start = (width - loader_width) / 2.0;
let y_base = height / 2.0;
let bar_count = 7;
let bar_width = loader_width / (bar_count as f32 * 2.0);
let bar_spacing = loader_width / (bar_count as f32);
for i in 0..bar_count {
let x = x_start + i as f32 * bar_spacing;
// Animate each bar with a phase offset
let offset = i as f32 * 0.4;
let wave = ((self.phase + offset).sin() + 1.0) / 2.0;
let bar_h = loader_height * (0.3 + 0.7 * wave);
let y = y_base - bar_h / 2.0;
let rect = iced::Rectangle {
x,
y,
width: bar_width,
height: bar_h,
};
let rounded_rect_path = Path::new(|p| {
p.rounded_rectangle(
Point::new(x, y / 2.0),
rect.size(),
15.into(),
);
});
let palette = theme.extended_palette();
let custom_color = palette.success.base.color;
frame.fill(
&rounded_rect_path,custom_color
);
// // Optional: round the ends
// frame.stroke_rectangle(
// rect.position(),
// rect.size(),
// canvas::Stroke {
// width: 1.5,
// // color: Color::from_rgb(0.1, 0.4, 0.7),
// ..Default::default()
// },
// );
}
vec![frame.into_geometry()]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment