#[cfg(target_os = "macos")]
use core_animation::prelude::*;
#[cfg(target_os = "macos")]
use std::f64::consts::PI;
#[cfg(target_os = "macos")]
fn main() {
println!("Loading Spinner - Smooth Rotation\n");
let window = WindowBuilder::new()
.title("Loading Spinner")
.size(300.0, 300.0)
.centered()
.background_color(Color::rgb(0.1, 0.1, 0.15))
.build();
let (width, height) = window.size();
let center = CGPoint::new(width / 2.0, height / 2.0);
let spinner_radius = 50.0;
let dot_size = 12.0;
let num_dots = 8;
let spinner_color = Color::rgb(0.2, 0.6, 1.0);
let spinner_container = CALayerBuilder::new()
.bounds(CGRect::new(
CGPoint::ZERO,
CGSize::new(
spinner_radius * 2.0 + dot_size,
spinner_radius * 2.0 + dot_size,
),
))
.position(center)
.build();
let rotation_duration = 1200u64.millis();
let spin_anim = CABasicAnimationBuilder::new(KeyPath::TransformRotation)
.values(0.0, PI * 2.0)
.duration(rotation_duration)
.easing(Easing::Linear) .repeat(Repeat::Forever)
.build();
spinner_container.addAnimation_forKey(&spin_anim, Some(objc2_foundation::ns_string!("spin")));
let container_center = CGPoint::new(
spinner_radius + dot_size / 2.0,
spinner_radius + dot_size / 2.0,
);
let dot_path = unsafe {
CGPath::with_ellipse_in_rect(
CGRect::new(CGPoint::ZERO, CGSize::new(dot_size, dot_size)),
std::ptr::null(),
)
};
for i in 0..num_dots {
let angle = (i as f64 / num_dots as f64) * 2.0 * PI - PI / 2.0;
let x = container_center.x + spinner_radius * angle.cos();
let y = container_center.y + spinner_radius * angle.sin();
let opacity = 0.2 + 0.8 * (1.0 - i as f64 / num_dots as f64);
let scale = 0.6 + 0.4 * (1.0 - i as f64 / num_dots as f64);
let dot = CAShapeLayerBuilder::new()
.path(dot_path.clone())
.fill_color(spinner_color.with_alpha(opacity))
.bounds(CGRect::new(CGPoint::ZERO, CGSize::new(dot_size, dot_size)))
.position(CGPoint::new(x, y))
.opacity(opacity as f32)
.scale(scale)
.build();
spinner_container.addSublayer(&dot);
}
let inner_radius = 25.0;
let inner_dot_size = 8.0;
let inner_num_dots = 6;
let inner_container = CALayerBuilder::new()
.bounds(CGRect::new(
CGPoint::ZERO,
CGSize::new(
inner_radius * 2.0 + inner_dot_size,
inner_radius * 2.0 + inner_dot_size,
),
))
.position(center)
.build();
let inner_spin_anim = CABasicAnimationBuilder::new(KeyPath::TransformRotation)
.values(0.0, -PI * 2.0) .duration(1800u64.millis()) .easing(Easing::Linear)
.repeat(Repeat::Forever)
.build();
inner_container
.addAnimation_forKey(&inner_spin_anim, Some(objc2_foundation::ns_string!("spin")));
let inner_container_center = CGPoint::new(
inner_radius + inner_dot_size / 2.0,
inner_radius + inner_dot_size / 2.0,
);
let inner_dot_path = unsafe {
CGPath::with_ellipse_in_rect(
CGRect::new(CGPoint::ZERO, CGSize::new(inner_dot_size, inner_dot_size)),
std::ptr::null(),
)
};
for i in 0..inner_num_dots {
let angle = (i as f64 / inner_num_dots as f64) * 2.0 * PI - PI / 2.0;
let x = inner_container_center.x + inner_radius * angle.cos();
let y = inner_container_center.y + inner_radius * angle.sin();
let opacity = 0.3 + 0.7 * (1.0 - i as f64 / inner_num_dots as f64);
let dot = CAShapeLayerBuilder::new()
.path(inner_dot_path.clone())
.fill_color(spinner_color.with_alpha(opacity * 0.6))
.bounds(CGRect::new(
CGPoint::ZERO,
CGSize::new(inner_dot_size, inner_dot_size),
))
.position(CGPoint::new(x, y))
.opacity((opacity * 0.6) as f32)
.build();
inner_container.addSublayer(&dot);
}
let center_dot_size = 14.0;
let center_dot_path = unsafe {
CGPath::with_ellipse_in_rect(
CGRect::new(CGPoint::ZERO, CGSize::new(center_dot_size, center_dot_size)),
std::ptr::null(),
)
};
let center_dot = CAShapeLayerBuilder::new()
.path(center_dot_path)
.fill_color(spinner_color)
.bounds(CGRect::new(
CGPoint::ZERO,
CGSize::new(center_dot_size, center_dot_size),
))
.position(center)
.shadow_color(spinner_color)
.shadow_offset(0.0, 0.0)
.shadow_radius(8.0)
.shadow_opacity(0.8)
.animate("pulse", KeyPath::TransformScale, |a| {
a.values(0.8, 1.3)
.duration(800u64.millis())
.easing(Easing::InOut)
.autoreverses()
.repeat(Repeat::Forever)
})
.animate("opacity", KeyPath::Opacity, |a| {
a.values(0.6, 1.0)
.duration(800u64.millis())
.easing(Easing::InOut)
.autoreverses()
.repeat(Repeat::Forever)
})
.build();
window.container().add_sublayer(&inner_container);
window.container().add_sublayer(&spinner_container);
window.container().add_sublayer(¢er_dot);
println!("Watch the loading spinner for 10 seconds...");
println!("Notice the constant speed from Easing::Linear.\n");
window.show_for(10.seconds());
println!("Done!");
}
#[cfg(not(target_os = "macos"))]
fn main() {
eprintln!("This example only runs on macOS");
}