fix: Move to use HTML for text

Previously we had the bubbles drawn on the avatar canvas. Now it's
actually text, so is the label.
This commit is contained in:
Evan Carroll 2026-01-26 00:05:41 -06:00
parent 23374ee024
commit 66368fe274
6 changed files with 650 additions and 403 deletions

View file

@ -0,0 +1,74 @@
//! HTML-based username label component.
//!
//! Renders display names as HTML/CSS elements instead of canvas,
//! allowing independent updates without triggering avatar redraws.
use leptos::prelude::*;
use uuid::Uuid;
use super::avatar_canvas::AvatarBoundsStore;
/// Base text size multiplier. Text at 100% slider = base_sizes * 1.4
const BASE_TEXT_SCALE: f64 = 1.4;
/// Individual username label component.
///
/// Renders as HTML/CSS for efficient updates independent of avatar canvas.
/// Reads avatar position from the shared bounds store written by AvatarCanvas.
#[component]
pub fn UsernameLabel(
/// The user ID this label belongs to (for reading bounds from store).
user_id: Uuid,
/// The display name to show.
display_name: String,
/// Shared store containing avatar bounds (written by AvatarCanvas).
avatar_bounds_store: AvatarBoundsStore,
/// Text size multiplier.
#[prop(default = 1.0.into())]
text_em_size: Signal<f64>,
/// Optional opacity for fading members.
#[prop(default = 1.0)]
opacity: f64,
) -> impl IntoView {
// Compute style based on avatar bounds
let style = Memo::new(move |_| {
let te = text_em_size.get();
let text_scale = te * BASE_TEXT_SCALE;
let font_size = 12.0 * text_scale;
let avatar_bounds = avatar_bounds_store
.get()
.get(&user_id)
.copied()
.unwrap_or_default();
// If bounds are all zero, avatar hasn't rendered yet
if avatar_bounds.content_center_x == 0.0 && avatar_bounds.content_top_y == 0.0 {
return "display: none;".to_string();
}
let x = avatar_bounds.content_center_x;
let y = avatar_bounds.content_bottom_y + 15.0 * text_scale;
format!(
"position: absolute; \
left: {}px; \
top: {}px; \
transform: translateX(-50%); \
--font-size: {}px; \
opacity: {}; \
z-index: 99998;",
x, y, font_size, opacity
)
});
view! {
<div
class="username-label"
style=style
data-user-id=user_id.to_string()
>
{display_name}
</div>
}
}