makepad_widgets/
loading_spinner.rs

1use crate::makepad_draw::*;
2
3live_design!{
4    link widgets;
5    use makepad_draw::shader::std::*;
6    use link::widgets::View;
7
8    pub LoadingSpinner = <View> {
9        width: Fill, height: Fill
10        show_bg: true,
11        draw_bg: {
12            color: #FFFFFF00
13            instance rotation_speed: 1.0
14            instance inner_radius: 0.6 // r
15            instance outer_radius: 0.7 // R
16
17            fn pixel(self) -> vec4 {
18                let aspect = self.rect_size.x / self.rect_size.y;
19                let pos = (self.pos - 0.5) * vec2(max(aspect, 1.), max(1. / aspect, 1.)) * 1.5;
20
21                let radius = length(pos);
22                let angle = atan(pos.y, pos.x);
23
24                let ring_thickness = self.outer_radius - self.inner_radius; // R - r
25                let cap_radius = ring_thickness / 2.0;
26
27                let ring_center = (self.inner_radius + self.outer_radius) / 2.0;
28                let edge = abs(radius - ring_center) - ring_thickness / 2.0;
29                let d = smoothstep(0.005, -0.005, edge);
30
31                // Define maximum arc length and oscillation frequency
32                let max_arc = 1.93 * PI;
33                let frequency = 0.75 * self.rotation_speed * 2.0 * PI;
34
35                // Calculate tail angle (always rotate clockwise)
36                let tail_angle = mod(self.time * self.rotation_speed * 2.0 * PI, 2.0 * PI);
37
38                // Calculate the direction and magnitude of the arc length
39                let theta = self.time * frequency;
40
41                // Direction factor: >0 increases, <0 decreases
42                let direction = sign(sin(theta));
43                let arc_length = (1.0 - cos(theta)) * max_arc / 2.0;
44                let signed_arc_length = direction * arc_length;
45
46                // Defining the start and end points of an arc
47                let start_angle = tail_angle + min(signed_arc_length, 0.0);
48                let end_angle = tail_angle + max(signed_arc_length, 0.0);
49
50                // Calculate the angle of the pixel relative to the starting point
51                let relative_angle = mod(angle - start_angle, 2.0 * PI);
52                let arc_length_abs = abs(signed_arc_length);
53                let arc_alpha = float(relative_angle < arc_length_abs);
54
55                // Calculate the angle of the pixel relative to the starting point
56                let cap_center_tail = vec2(cos(start_angle), sin(start_angle)) * ring_center;
57                let cap_center_head = vec2(cos(end_angle), sin(end_angle)) * ring_center;
58
59                // Calculate the distance from the pixel to the center of the semicircle
60                let dist_to_tail_cap = length(pos - cap_center_tail);
61                let dist_to_head_cap = length(pos - cap_center_head);
62
63                // Determine if the pixel is within the head or tail semicircle
64                let in_tail_cap = dist_to_tail_cap < cap_radius;
65                let in_head_cap = dist_to_head_cap < cap_radius;
66
67                // Transparency of merged arcs and semicircles
68                let alpha = clamp((arc_alpha + float(in_tail_cap) + float(in_head_cap)) * d, 0.0, 1.0);
69
70                return vec4(self.color.rgb * alpha, alpha);
71            }
72        }
73    }
74}