From 09078cf66dc4800a2d44011ded76752eb5887992 Mon Sep 17 00:00:00 2001 From: aaravlu Date: Wed, 12 Feb 2025 13:06:21 +0800 Subject: [PATCH 1/7] Add a generic spin loader --- src/shared/styles.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index ac2cba47..f4f6ac60 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -190,4 +190,40 @@ live_design! { } } } + + pub RobrixSpinLoader = { + width: Fill, height: Fill + show_bg: true, + draw_bg: { + color: #000000AF + fn pixel(self) -> vec4 { + // Normalize and adjust for aspect ratio to ensure circular shape. + let aspect = self.rect_size.x / self.rect_size.y; + + let pos = vec2( + (self.pos.x - 0.5) * max(aspect, 1.), + (self.pos.y - 0.5) * max(1. / aspect, 1.) + ) * 2.8; + + let radius = length(pos); + + // Rotate over time + let angle = atan(pos.y, pos.x) + self.time * PI * 2.0; + + // Create a circular shape with thickness + let inner_radius = 0.5; + let outer_radius = 0.7; + let thickness = outer_radius - inner_radius; + + let edge = abs(radius - (inner_radius + thickness * 0.5)) - thickness * 0.5; + let d = smoothstep(0.01, -0.01, edge); + + // 5 segments for the spinner. + // Use sin to create an effect where parts of the circle fade in and out. + let alpha = sin(angle * 5.0) * 0.5 + 0.5; + + return vec4(self.color.rgb, d * alpha); + } + } + } } From c31ec6e387ebc0c449128cdf7972b94ad7d8b879 Mon Sep 17 00:00:00 2001 From: aaravlu Date: Wed, 12 Feb 2025 13:19:38 +0800 Subject: [PATCH 2/7] Add doc for spin loader --- src/shared/styles.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index f4f6ac60..f71236cd 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -191,6 +191,7 @@ live_design! { } } + // A generic spinner widget styled for Robrix. pub RobrixSpinLoader = { width: Fill, height: Fill show_bg: true, From 628ae06081ca39f7bb5f12abf5b588b7e7dfd9c7 Mon Sep 17 00:00:00 2001 From: aaravlu Date: Fri, 14 Feb 2025 10:06:31 +0800 Subject: [PATCH 3/7] Make spin loader configurable --- src/shared/styles.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index f71236cd..b3a02e8e 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -196,20 +196,22 @@ live_design! { width: Fill, height: Fill show_bg: true, draw_bg: { - color: #000000AF + color: #00000000 + + instance rotation_speed: 1. + instance num_segments: 5. fn pixel(self) -> vec4 { // Normalize and adjust for aspect ratio to ensure circular shape. let aspect = self.rect_size.x / self.rect_size.y; - - let pos = vec2( - (self.pos.x - 0.5) * max(aspect, 1.), - (self.pos.y - 0.5) * max(1. / aspect, 1.) - ) * 2.8; + let normalized_pos_x = (self.pos.x - 0.5) * max(aspect, 1.); + let normalized_pos_y = (self.pos.y - 0.5) * max(1. / aspect, 1.); + let pos = vec2(normalized_pos_x, normalized_pos_y) * 2.8; let radius = length(pos); // Rotate over time - let angle = atan(pos.y, pos.x) + self.time * PI * 2.0; + let angle_offset = self.time * self.rotation_speed * PI * 2.0; + let angle = atan(pos.y, pos.x) + angle_offset; // Create a circular shape with thickness let inner_radius = 0.5; @@ -221,9 +223,9 @@ live_design! { // 5 segments for the spinner. // Use sin to create an effect where parts of the circle fade in and out. - let alpha = sin(angle * 5.0) * 0.5 + 0.5; + let alpha = (sin(angle * self.num_segments) * 0.5 + 0.5) * d; - return vec4(self.color.rgb, d * alpha); + return vec4(self.color.rgb, alpha); } } } From dc0c5d758a6af0e38a9dbd41b2da888dcad5b72a Mon Sep 17 00:00:00 2001 From: aaravlu Date: Mon, 24 Feb 2025 09:11:17 +0800 Subject: [PATCH 4/7] Checkout color to white and shrink the diameter of the ring --- src/shared/styles.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index b3a02e8e..835c8ac5 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -192,40 +192,39 @@ live_design! { } // A generic spinner widget styled for Robrix. - pub RobrixSpinLoader = { + pub SpinLoader = { width: Fill, height: Fill show_bg: true, draw_bg: { - color: #00000000 - + color: #FFFFFF00 instance rotation_speed: 1. instance num_segments: 5. fn pixel(self) -> vec4 { - // Normalize and adjust for aspect ratio to ensure circular shape. + // Normalization and aspect ratio adjustment to ensure circular display let aspect = self.rect_size.x / self.rect_size.y; let normalized_pos_x = (self.pos.x - 0.5) * max(aspect, 1.); let normalized_pos_y = (self.pos.y - 0.5) * max(1. / aspect, 1.); - let pos = vec2(normalized_pos_x, normalized_pos_y) * 2.8; + let pos = vec2(normalized_pos_x, normalized_pos_y) * 1.5; + // Calculate the distance from the pixel point to the center. let radius = length(pos); - // Rotate over time + // Calculate the angle of rotation over time let angle_offset = self.time * self.rotation_speed * PI * 2.0; let angle = atan(pos.y, pos.x) + angle_offset; - // Create a circular shape with thickness - let inner_radius = 0.5; + let inner_radius = 0.6; let outer_radius = 0.7; let thickness = outer_radius - inner_radius; - let edge = abs(radius - (inner_radius + thickness * 0.5)) - thickness * 0.5; - let d = smoothstep(0.01, -0.01, edge); + let d = smoothstep(0.005, -0.005, edge); - // 5 segments for the spinner. - // Use sin to create an effect where parts of the circle fade in and out. - let alpha = (sin(angle * self.num_segments) * 0.5 + 0.5) * d; + // Creating distinct paragraph effects. + let segment = fract(angle / (2.0 * PI) * self.num_segments); + let segment_alpha = smoothstep(0.0, 0.2, segment) * smoothstep(0.8, 0.6, segment); + let alpha = segment_alpha * d; - return vec4(self.color.rgb, alpha); + return vec4(self.color.rgb * alpha, alpha); } } } From 3674fc93f37237bb7dcc931d7c0c131458d7448b Mon Sep 17 00:00:00 2001 From: aaravlu Date: Thu, 27 Feb 2025 09:56:14 +0800 Subject: [PATCH 5/7] Remove segements and make it more soft --- src/shared/styles.rs | 54 ++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index 835c8ac5..d08ff697 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -198,31 +198,57 @@ live_design! { draw_bg: { color: #FFFFFF00 instance rotation_speed: 1. - instance num_segments: 5. fn pixel(self) -> vec4 { - // Normalization and aspect ratio adjustment to ensure circular display + // Position normalization and adjusting the aspect ratio let aspect = self.rect_size.x / self.rect_size.y; let normalized_pos_x = (self.pos.x - 0.5) * max(aspect, 1.); let normalized_pos_y = (self.pos.y - 0.5) * max(1. / aspect, 1.); let pos = vec2(normalized_pos_x, normalized_pos_y) * 1.5; - // Calculate the distance from the pixel point to the center. + // Calculate pixel-to-center distance and angle let radius = length(pos); + let angle = atan(pos.y, pos.x); - // Calculate the angle of rotation over time - let angle_offset = self.time * self.rotation_speed * PI * 2.0; - let angle = atan(pos.y, pos.x) + angle_offset; + // Define inner and outer radii of the circle + let inner_radius = 0.6; // r + let outer_radius = 0.7; // R + let ring_thickness = outer_radius - inner_radius; // R - r + let cap_radius = ring_thickness / 2.0; - let inner_radius = 0.6; - let outer_radius = 0.7; - let thickness = outer_radius - inner_radius; - let edge = abs(radius - (inner_radius + thickness * 0.5)) - thickness * 0.5; + // Calculate the transparency of the circle + let ring_center = (inner_radius + outer_radius) / 2.0; + let edge = abs(radius - ring_center) - ring_thickness / 2.0; let d = smoothstep(0.005, -0.005, edge); - // Creating distinct paragraph effects. - let segment = fract(angle / (2.0 * PI) * self.num_segments); - let segment_alpha = smoothstep(0.0, 0.2, segment) * smoothstep(0.8, 0.6, segment); - let alpha = segment_alpha * d; + // Calculate the head-tail angle + let tail_speed = self.rotation_speed; + let head_speed = self.rotation_speed * 1.5; + let tail_angle = mod(self.time * tail_speed * 2.0 * PI, 2.0 * PI); + let head_angle = mod(self.time * head_speed * 2.0 * PI, 2.0 * PI); + + // Calculate the pixel angle relative to the tail + let relative_angle = mod(angle - tail_angle, 2.0 * PI); + let head_relative = mod(head_angle - tail_angle, 2.0 * PI); + + // Calculate the center of the semicircle (at the center of the circle at the head and tail) + let cap_center_tail = vec2(cos(tail_angle), sin(tail_angle)) * ring_center; + let cap_center_head = vec2(cos(head_angle), sin(head_angle)) * ring_center; + + // Calculate the distance from the pixel to the center of the head and tail semicircles + let dist_to_tail_cap = length(pos - cap_center_tail); + let dist_to_head_cap = length(pos - cap_center_head); + + // Determine if the pixel is inside the head and tail semicircles + let in_tail_cap = dist_to_tail_cap < cap_radius; + let in_head_cap = dist_to_head_cap < cap_radius; + + // Calculate the transparency of the body of the circle + let arc_alpha = step(0.0, relative_angle) * step(relative_angle, head_relative); + + // Merging the transparency of arcs and semicircles + let alpha = (arc_alpha + float(in_tail_cap) + float(in_head_cap)) * d; + + alpha = min(alpha, 1.0); return vec4(self.color.rgb * alpha, alpha); } From 8016236638166200758f88e28e4dc68beeac6fe9 Mon Sep 17 00:00:00 2001 From: aaravlu Date: Wed, 5 Mar 2025 15:00:38 +0800 Subject: [PATCH 6/7] Fix animation --- src/shared/styles.rs | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index d08ff697..87f03caa 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -199,56 +199,55 @@ live_design! { color: #FFFFFF00 instance rotation_speed: 1. fn pixel(self) -> vec4 { - // Position normalization and adjusting the aspect ratio let aspect = self.rect_size.x / self.rect_size.y; let normalized_pos_x = (self.pos.x - 0.5) * max(aspect, 1.); let normalized_pos_y = (self.pos.y - 0.5) * max(1. / aspect, 1.); let pos = vec2(normalized_pos_x, normalized_pos_y) * 1.5; - // Calculate pixel-to-center distance and angle let radius = length(pos); let angle = atan(pos.y, pos.x); - // Define inner and outer radii of the circle let inner_radius = 0.6; // r let outer_radius = 0.7; // R let ring_thickness = outer_radius - inner_radius; // R - r let cap_radius = ring_thickness / 2.0; - // Calculate the transparency of the circle let ring_center = (inner_radius + outer_radius) / 2.0; let edge = abs(radius - ring_center) - ring_thickness / 2.0; let d = smoothstep(0.005, -0.005, edge); - // Calculate the head-tail angle - let tail_speed = self.rotation_speed; - let head_speed = self.rotation_speed * 1.5; - let tail_angle = mod(self.time * tail_speed * 2.0 * PI, 2.0 * PI); - let head_angle = mod(self.time * head_speed * 2.0 * PI, 2.0 * PI); + // Define the maximum arc length and oscillation frequency + // Maximum arc length, slightly less than 2π + let max_arc = 1.93 * PI; + // Matches the original chase frequency + let frequency = 0.75 * self.rotation_speed * 2.0 * PI; + let arc_length = (1.0 - cos(self.time * frequency)) * max_arc / 2.0; - // Calculate the pixel angle relative to the tail + // Calculate tail and head angles + let tail_angle = mod(self.time * self.rotation_speed * 2.0 * PI, 2.0 * PI); + let head_angle = mod(tail_angle + arc_length, 2.0 * PI); + + // Calculate the angle of the pixel relative to the tail let relative_angle = mod(angle - tail_angle, 2.0 * PI); - let head_relative = mod(head_angle - tail_angle, 2.0 * PI); - // Calculate the center of the semicircle (at the center of the circle at the head and tail) + // Calculate the transparency of the arc body + let arc_alpha = step(0.0, relative_angle) * step(relative_angle, arc_length); + + // Calculate the center of the head and tail semicircles let cap_center_tail = vec2(cos(tail_angle), sin(tail_angle)) * ring_center; let cap_center_head = vec2(cos(head_angle), sin(head_angle)) * ring_center; - // Calculate the distance from the pixel to the center of the head and tail semicircles + // Calculate the distance from the pixel to the center of the semicircle let dist_to_tail_cap = length(pos - cap_center_tail); let dist_to_head_cap = length(pos - cap_center_head); - // Determine if the pixel is inside the head and tail semicircles + // Determine if a pixel is within the head or tail semicircle let in_tail_cap = dist_to_tail_cap < cap_radius; let in_head_cap = dist_to_head_cap < cap_radius; - // Calculate the transparency of the body of the circle - let arc_alpha = step(0.0, relative_angle) * step(relative_angle, head_relative); - - // Merging the transparency of arcs and semicircles + // Transparency of merged arcs and semicircles let alpha = (arc_alpha + float(in_tail_cap) + float(in_head_cap)) * d; - - alpha = min(alpha, 1.0); + let alpha = min(alpha, 1.0); return vec4(self.color.rgb * alpha, alpha); } From 40ec69280210df8e7655ce2f94f78b7848bafa4e Mon Sep 17 00:00:00 2001 From: aaravlu Date: Thu, 6 Mar 2025 15:00:54 +0800 Subject: [PATCH 7/7] Optimize performance --- src/shared/styles.rs | 53 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/shared/styles.rs b/src/shared/styles.rs index 87f03caa..4f91a676 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -197,57 +197,62 @@ live_design! { show_bg: true, draw_bg: { color: #FFFFFF00 - instance rotation_speed: 1. + instance rotation_speed: 1.0 + instance inner_radius: 0.6 // r + instance outer_radius: 0.7 // R + fn pixel(self) -> vec4 { let aspect = self.rect_size.x / self.rect_size.y; - let normalized_pos_x = (self.pos.x - 0.5) * max(aspect, 1.); - let normalized_pos_y = (self.pos.y - 0.5) * max(1. / aspect, 1.); - let pos = vec2(normalized_pos_x, normalized_pos_y) * 1.5; + let pos = (self.pos - 0.5) * vec2(max(aspect, 1.), max(1. / aspect, 1.)) * 1.5; let radius = length(pos); let angle = atan(pos.y, pos.x); - let inner_radius = 0.6; // r - let outer_radius = 0.7; // R - let ring_thickness = outer_radius - inner_radius; // R - r + let ring_thickness = self.outer_radius - self.inner_radius; // R - r let cap_radius = ring_thickness / 2.0; - let ring_center = (inner_radius + outer_radius) / 2.0; + let ring_center = (self.inner_radius + self.outer_radius) / 2.0; let edge = abs(radius - ring_center) - ring_thickness / 2.0; let d = smoothstep(0.005, -0.005, edge); - // Define the maximum arc length and oscillation frequency - // Maximum arc length, slightly less than 2π + // Define maximum arc length and oscillation frequency let max_arc = 1.93 * PI; - // Matches the original chase frequency let frequency = 0.75 * self.rotation_speed * 2.0 * PI; - let arc_length = (1.0 - cos(self.time * frequency)) * max_arc / 2.0; - // Calculate tail and head angles + // Calculate tail angle (always rotate clockwise) let tail_angle = mod(self.time * self.rotation_speed * 2.0 * PI, 2.0 * PI); - let head_angle = mod(tail_angle + arc_length, 2.0 * PI); - // Calculate the angle of the pixel relative to the tail - let relative_angle = mod(angle - tail_angle, 2.0 * PI); + // Calculate the direction and magnitude of the arc length + let theta = self.time * frequency; + + // Direction factor: >0 increases, <0 decreases + let direction = sign(sin(theta)); + let arc_length = (1.0 - cos(theta)) * max_arc / 2.0; + let signed_arc_length = direction * arc_length; + + // Defining the start and end points of an arc + let start_angle = tail_angle + min(signed_arc_length, 0.0); + let end_angle = tail_angle + max(signed_arc_length, 0.0); - // Calculate the transparency of the arc body - let arc_alpha = step(0.0, relative_angle) * step(relative_angle, arc_length); + // Calculate the angle of the pixel relative to the starting point + let relative_angle = mod(angle - start_angle, 2.0 * PI); + let arc_length_abs = abs(signed_arc_length); + let arc_alpha = float(relative_angle < arc_length_abs); - // Calculate the center of the head and tail semicircles - let cap_center_tail = vec2(cos(tail_angle), sin(tail_angle)) * ring_center; - let cap_center_head = vec2(cos(head_angle), sin(head_angle)) * ring_center; + // Calculate the angle of the pixel relative to the starting point + let cap_center_tail = vec2(cos(start_angle), sin(start_angle)) * ring_center; + let cap_center_head = vec2(cos(end_angle), sin(end_angle)) * ring_center; // Calculate the distance from the pixel to the center of the semicircle let dist_to_tail_cap = length(pos - cap_center_tail); let dist_to_head_cap = length(pos - cap_center_head); - // Determine if a pixel is within the head or tail semicircle + // Determine if the pixel is within the head or tail semicircle let in_tail_cap = dist_to_tail_cap < cap_radius; let in_head_cap = dist_to_head_cap < cap_radius; // Transparency of merged arcs and semicircles - let alpha = (arc_alpha + float(in_tail_cap) + float(in_head_cap)) * d; - let alpha = min(alpha, 1.0); + let alpha = clamp((arc_alpha + float(in_tail_cap) + float(in_head_cap)) * d, 0.0, 1.0); return vec4(self.color.rgb * alpha, alpha); }