basicMarquee

Functions summary

Modifier
Modifier.basicMarquee(
    iterations: Int,
    animationMode: MarqueeAnimationMode,
    repeatDelayMillis: Int,
    initialDelayMillis: Int,
    spacing: MarqueeSpacing,
    velocity: Dp
)

Applies an animated marquee effect to the modified content if it's too wide to fit in the available space.

Cmn

Functions

Modifier.basicMarquee

fun Modifier.basicMarquee(
    iterations: Int = Iterations,
    animationMode: MarqueeAnimationMode = Immediately,
    repeatDelayMillis: Int = RepeatDelayMillis,
    initialDelayMillis: Int = if (animationMode == Immediately) repeatDelayMillis else 0,
    spacing: MarqueeSpacing = Spacing,
    velocity: Dp = Velocity
): Modifier

Applies an animated marquee effect to the modified content if it's too wide to fit in the available space. This modifier has no effect if the content fits in the max constraints. The content will be measured with unbounded width.

When the animation is running, it will restart from the initial state any time:

  • any of the parameters to this modifier change, or

  • the content or container size change.

The animation only affects the drawing of the content, not its position. The offset returned by the LayoutCoordinates of anything inside the marquee is undefined relative to anything outside the marquee, and may not match its drawn position on screen. This modifier also does not currently support content that accepts position-based input such as pointer events.

To only animate when the composable is focused, specify animationMode and make the composable focusable. This modifier does not add any visual effects aside from scrolling, but you can add your own by placing modifiers before this one.

import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

// Marquee only animates when the content doesn't fit in the max width.
Column(Modifier.width(30.dp)) { Text("hello world", Modifier.basicMarquee()) }
import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp

val edgeWidth = 32.dp
fun ContentDrawScope.drawFadedEdge(leftEdge: Boolean) {
    val edgeWidthPx = edgeWidth.toPx()
    drawRect(
        topLeft = Offset(if (leftEdge) 0f else size.width - edgeWidthPx, 0f),
        size = Size(edgeWidthPx, size.height),
        brush =
            Brush.horizontalGradient(
                colors = listOf(Color.Transparent, Color.Black),
                startX = if (leftEdge) 0f else size.width,
                endX = if (leftEdge) edgeWidthPx else size.width - edgeWidthPx,
            ),
        blendMode = BlendMode.DstIn,
    )
}

Text(
    "the quick brown fox jumped over the lazy dogs",
    Modifier.widthIn(max = edgeWidth * 4)
        // Rendering to an offscreen buffer is required to get the faded edges' alpha to be
        // applied only to the text, and not whatever is drawn below this composable (e.g. the
        // window).
        .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
        .drawWithContent {
            drawContent()
            drawFadedEdge(leftEdge = true)
            drawFadedEdge(leftEdge = false)
        }
        .basicMarquee(
            // Animate forever.
            iterations = Int.MAX_VALUE,
            spacing = MarqueeSpacing(0.dp),
        )
        .padding(start = edgeWidth),
)
import androidx.compose.foundation.MarqueeAnimationMode
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp

val focusRequester = remember { FocusRequester() }

// Marquee only animates when the content doesn't fit in the max width.
Column(Modifier.width(30.dp)) {
    Text(
        "hello world",
        Modifier.clickable { focusRequester.requestFocus() }
            .basicMarquee(animationMode = MarqueeAnimationMode.WhileFocused)
            .focusRequester(focusRequester)
            .focusable(),
    )
}
Parameters
iterations: Int = Iterations

The number of times to repeat the animation. Int.MAX_VALUE will repeat forever, and 0 will disable animation.

animationMode: MarqueeAnimationMode = Immediately

Whether the marquee should start animating Immediately or only WhileFocused. In WhileFocused mode, the modified node or the content must be made focusable. Note that the initialDelayMillis is part of the animation, so this parameter determines when that initial delay starts counting down, not when the content starts to actually scroll.

repeatDelayMillis: Int = RepeatDelayMillis

The duration to wait before starting each subsequent iteration, in millis.

initialDelayMillis: Int = if (animationMode == Immediately) repeatDelayMillis else 0

The duration to wait before starting the first iteration of the animation, in millis. By default, there will be no initial delay if animationMode is WhileFocused, otherwise the initial delay will be repeatDelayMillis.

spacing: MarqueeSpacing = Spacing

A MarqueeSpacing that specifies how much space to leave at the end of the content before showing the beginning again.

velocity: Dp = Velocity

The speed of the animation in dps / second. A positive velocity means that the marquee will animate in the direction of the current LayoutDirection.