Marquee

Rust/UI component that displays an infinite scrolling component that can be used to display text, images, or videos.

  • Rust/UI Icons - CopyCopy Demo
Jack

@jack

1. Just love it
Jack

@jack

2. Just love it
Jack

@jack

3. Just love it
Jack

@jack

4. Just love it
Jack

@jack

5. Just love it
Jack

@jack

6. Just love it
Jack

@jack

7. Just love it
Jack

@jack

8. Just love it
Jack

@jack

9. Just love it
Jack

@jack

10. Just love it
Jack

@jack

11. Just love it
Jack

@jack

12. Just love it
Jack

@jack

[II] 1. Just love it
Jack

@jack

[II] 2. Just love it
Jack

@jack

[II] 3. Just love it
Jack

@jack

[II] 4. Just love it
Jack

@jack

[II] 5. Just love it
Jack

@jack

[II] 6. Just love it
Jack

@jack

[II] 7. Just love it
Jack

@jack

[II] 8. Just love it
Jack

@jack

[II] 9. Just love it
Jack

@jack

[II] 10. Just love it
Jack

@jack

[II] 11. Just love it
Jack

@jack

[II] 12. Just love it
use leptos::prelude::*;

use crate::components::extensions::marquee::{Marquee, MarqueeRow, MarqueeWrapper};

#[component]
pub fn DemoMarquee() -> impl IntoView {
    view! {
        <MarqueeWrapper class="max-w-4xl">
            <Marquee>
                <MarqueeRow>
                    <CardFigureExample blockquote="1. Just love it".to_string() />
                    <CardFigureExample blockquote="2. Just love it".to_string() />
                    <CardFigureExample blockquote="3. Just love it".to_string() />
                </MarqueeRow>
                <MarqueeRow>
                    <CardFigureExample blockquote="4. Just love it".to_string() />
                    <CardFigureExample blockquote="5. Just love it".to_string() />
                    <CardFigureExample blockquote="6. Just love it".to_string() />
                </MarqueeRow>
                <MarqueeRow>
                    <CardFigureExample blockquote="7. Just love it".to_string() />
                    <CardFigureExample blockquote="8. Just love it".to_string() />
                    <CardFigureExample blockquote="9. Just love it".to_string() />
                </MarqueeRow>
                <MarqueeRow>
                    <CardFigureExample blockquote="10. Just love it".to_string() />
                    <CardFigureExample blockquote="11. Just love it".to_string() />
                    <CardFigureExample blockquote="12. Just love it".to_string() />
                </MarqueeRow>
            </Marquee>

            // --------------------------------------
            // --------------------------------------
            // --------------------------------------
            // --------------------------------------
            // --------------------------------------
            // --------------------------------------

            <Marquee>
                <MarqueeRow class="[animation-direction:reverse]">
                    <CardFigureExample blockquote="[II] 1. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 2. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 3. Just love it".to_string() />
                </MarqueeRow>
                <MarqueeRow class="[animation-direction:reverse]">
                    <CardFigureExample blockquote="[II] 4. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 5. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 6. Just love it".to_string() />
                </MarqueeRow>
                <MarqueeRow class="[animation-direction:reverse]">
                    <CardFigureExample blockquote="[II] 7. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 8. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 9. Just love it".to_string() />
                </MarqueeRow>
                <MarqueeRow class="[animation-direction:reverse]">
                    <CardFigureExample blockquote="[II] 10. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 11. Just love it".to_string() />
                    <CardFigureExample blockquote="[II] 12. Just love it".to_string() />
                </MarqueeRow>
            </Marquee>

        </MarqueeWrapper>
    }
}

/* ========================================================== */
/*                     ✨ FUNCTIONS ✨                        */
/* ========================================================== */

#[component]
fn CardFigureExample(blockquote: String) -> impl IntoView {
    view! {
        <figure class="overflow-hidden relative p-4 w-64 rounded-xl border cursor-pointer border-gray-950/[.1] bg-gray-950/[.01] dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15] hover:bg-gray-950/[.05]">
            <div class="flex gap-2 items-center">
                <img class="rounded-full" width="32" height="32" alt="" src="https://avatar.vercel.sh/jack" />
                <div class="flex flex-col">
                    <figcaption class="text-sm font-medium dark:text-white">Jack</figcaption>
                    <p class="text-xs font-medium dark:text-white/40">@jack</p>
                </div>
            </div>
            <blockquote class="mt-2 text-sm">{blockquote}</blockquote>
        </figure>
    }
}

Installation

You can run either of the following commands:

# cargo install ui-cli --force
ui add demo_marquee
ui add marquee

Update the imports to match your project setup.

Copy and paste the following code into your project:

components/ui/marquee.rs

use leptos::prelude::*;
use leptos_ui::clx;
use tw_merge::*;

use crate::registry::ui::mask::{Mask, MaskSide};

// TODO UI. Separate the mask from the marquee.

mod components {
    use super::*;
    clx! {MarqueeRow, div,
        "animate__marquee__row",
        "flex flex-row justify-around shrink-0 [gap:var(--gap)]"
    }
}

pub use components::*;

/* ========================================================== */
/*                     ✨ FUNCTIONS ✨                        */
/* ========================================================== */

#[component]
pub fn Marquee(children: Children) -> impl IntoView {
    view! {
        <style>
            {"@keyframes marquee_horizontal {
            from { transform: translateX(0); }
            to { transform: translateX(calc(-100% - var(--gap))); }
            }
            
            @keyframes marquee_vertical {
            from { transform: translateY(0); }
            to { transform: translateY(calc(-100% - var(--gap))); }
            }
            
            .animate__marquee__row {
            animation-name: marquee_horizontal;
            animation-duration: var(--duration);
            animation-timing-function: linear;
            animation-iteration-count: infinite;
            }
            
            .group:hover .animate__marquee__row {
            animation-play-state: paused;
            }
            "}
        </style>

        <div
            data-name="Marquee"
            class="flex overflow-hidden flex-row p-2 group [--gap:1rem] [gap:var(--gap)] [--duration:20s]"
        >
            {children()}
        </div>
    }
}

#[component]
pub fn MarqueeWrapper(#[prop(into, optional)] class: String, children: Children) -> impl IntoView {
    let merged_class = tw_merge!(
        "flex overflow-hidden relative flex-col justify-center items-center p-20 w-full h-full md:shadow-xl min-h-[300px] bg-background",
        class
    );

    view! { <div class=merged_class>{children()} <Mask side=MaskSide::Left /> <Mask side=MaskSide::Right /></div> }
}

Update the imports to match your project setup.

Usage

// Coming soon 🦀