Added particle trail effect

macos-click-through
Jon Valdés 5 years ago
parent 5c6d1ffa42
commit 6fcd50289a

@ -1,7 +1,7 @@
use skulpin::skia_safe::{paint::Style, BlendMode, Canvas, Color, Paint, Point, Rect};
use crate::editor::{Colors, Cursor};
use super::animation_utils::*;
use crate::editor::{Colors, Cursor};
pub trait CursorVFX {
fn update(&mut self, current_cursor_destination: Point, dt: f32) -> bool;
@ -35,7 +35,7 @@ impl PointHighlight {
impl CursorVFX for PointHighlight {
fn update(&mut self, _current_cursor_destination: Point, dt: f32) -> bool {
self.t = (self.t + dt * 5.0).min(1.0); // TODO - speed config
return self.t < 1.0;
self.t < 1.0
}
fn restart(&mut self, position: Point) {
@ -82,3 +82,110 @@ impl CursorVFX for PointHighlight {
}
}
}
#[derive(Clone)]
struct ParticleData {
pos: Point,
speed: Point,
lifetime: f32,
}
pub struct ParticleTrail {
particles: Vec<ParticleData>,
previous_cursor_dest: Point,
}
impl ParticleTrail {
pub fn new() -> ParticleTrail {
ParticleTrail {
particles: vec![],
previous_cursor_dest: Point::new(0.0, 0.0),
}
}
fn add_particle(&mut self, pos: Point, speed: Point, lifetime: f32) {
self.particles.push(ParticleData {
pos,
speed,
lifetime,
});
}
// Note this method doesn't keep particles in order
fn remove_particle(&mut self, idx: usize) {
self.particles[idx] = self.particles[self.particles.len() - 1].clone();
self.particles.pop();
}
}
const PARTICLE_DENSITY: f32 = 0.05; // TODO - density should be based on font size too
const PARTICLE_LIFETIME: f32 = 1.0;
impl CursorVFX for ParticleTrail {
fn update(&mut self, current_cursor_dest: Point, dt: f32) -> bool {
// Update lifetimes and remove dead particles
let mut i = 0;
while i < self.particles.len() {
let particle: &mut ParticleData = &mut self.particles[i];
particle.lifetime -= dt;
if particle.lifetime <= 0.0 {
self.remove_particle(i);
} else {
i += 1;
}
}
for i in 0..self.particles.len() {
let particle = &mut self.particles[i];
particle.pos += particle.speed * dt;
}
// Spawn new particles
if current_cursor_dest != self.previous_cursor_dest {
let travel = current_cursor_dest - self.previous_cursor_dest;
let travel_distance = travel.length();
let particle_count = (travel_distance * PARTICLE_DENSITY) as usize;
let prev_p = self.previous_cursor_dest;
for i in 0..particle_count {
let t = i as f32 / (particle_count as f32 - 1.0);
let rand_seed = t * std::f32::consts::E * 20.0;
let rand = Point::new(rand_seed.sin(), rand_seed.cos());
let pos = prev_p + travel * (t + 0.3 * rand.x / particle_count as f32);
self.add_particle(pos, rand * 10.0, t * PARTICLE_LIFETIME);
}
self.previous_cursor_dest = current_cursor_dest;
}
// Keep animating as long as there are particles alive
!self.particles.is_empty()
}
fn restart(&mut self, position: Point) {}
fn render(&self, canvas: &mut Canvas, cursor: &Cursor, colors: &Colors, font_size: (f32, f32)) {
let mut paint = Paint::new(skulpin::skia_safe::colors::WHITE, None);
paint.set_style(Style::Stroke);
paint.set_stroke_width(font_size.1 * 0.2);
let base_color: Color = cursor.background(&colors).to_color();
paint.set_blend_mode(BlendMode::SrcOver);
self.particles.iter().for_each(|particle| {
let l = particle.lifetime / PARTICLE_LIFETIME;
let alpha = (l * 255.0) as u8;
let color = Color::from_argb(alpha, base_color.r(), base_color.g(), base_color.b());
paint.set_color(color);
let radius = font_size.0 * 0.5 * l;
let hr = radius * 0.5;
let rect = Rect::from_xywh(particle.pos.x - hr, particle.pos.y - hr, radius, radius);
canvas.draw_oval(&rect, &paint);
//canvas.draw_rect(&rect, &paint);
});
}
}

@ -194,7 +194,8 @@ impl CursorRenderer {
command_line_delay: 0,
blink_status: BlinkStatus::new(),
previous_cursor_shape: None,
cursor_vfx: Box::new(PointHighlight::new(Point{x:0.0, y:0.0}, HighlightMode::Ripple)),
//cursor_vfx: Box::new(PointHighlight::new(Point{x:0.0, y:0.0}, HighlightMode::Ripple)),
cursor_vfx: Box::new(ParticleTrail::new()),
};
renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE);
renderer

Loading…
Cancel
Save