Skip to content

Commit

Permalink
Fix background parallax with deferred reads
Browse files Browse the repository at this point in the history
  • Loading branch information
mlykotom committed Nov 27, 2023
1 parent e52dfd0 commit a985151
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

Expand All @@ -45,20 +47,37 @@ fun Modifier.offsetGradientBackground(
colors: List<Color>,
width: Float,
offset: Float = 0f
) = background(
) = this then background(
Brush.horizontalGradient(
colors,
colors = colors,
startX = -offset,
endX = width - offset,
tileMode = TileMode.Mirror
)
)

fun Modifier.offsetGradientBackground(
colors: List<Color>,
width: Density.() -> Float,
offset: Density.() -> Float = { 0f }
) = this then drawBehind {
val actualOffset = offset()

drawRect(
Brush.horizontalGradient(
colors = colors,
startX = -actualOffset,
endX = width() - actualOffset,
tileMode = TileMode.Mirror
)
)
}

fun Modifier.diagonalGradientBorder(
colors: List<Color>,
borderSize: Dp = 2.dp,
shape: Shape
) = border(
) = this then border(
width = borderSize,
brush = Brush.linearGradient(colors),
shape = shape
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
Expand All @@ -53,6 +53,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
Expand All @@ -67,13 +68,7 @@ import com.example.jetsnack.ui.utils.mirroringIcon

private val HighlightCardWidth = 170.dp
private val HighlightCardPadding = 16.dp

// The Cards show a gradient which spans 3 cards and scrolls with parallax.
private val gradientWidth
@Composable
get() = with(LocalDensity.current) {
(3 * (HighlightCardWidth + HighlightCardPadding).toPx())
}
private val Density.cardWidthWithPaddingPx get() = (HighlightCardWidth + HighlightCardPadding).toPx()

@Composable
fun SnackCollection(
Expand Down Expand Up @@ -129,28 +124,33 @@ private fun HighlightedSnacks(
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
) {
val scroll = rememberScrollState(0)
val rowState = rememberLazyListState()

val highlightedCardWidthPx = with(LocalDensity.current) { cardWidthWithPaddingPx }

val scrollProvider = {
// Simple calculation of scroll distance for homogenous item types with the same width.
highlightedCardWidthPx * rowState.firstVisibleItemIndex + rowState.firstVisibleItemScrollOffset
}

val gradient = when ((index / 2) % 2) {
0 -> JetsnackTheme.colors.gradient6_1
else -> JetsnackTheme.colors.gradient6_2
}
// The Cards show a gradient which spans 3 cards and scrolls with parallax.
val gradientWidth = with(LocalDensity.current) {
(6 * (HighlightCardWidth + HighlightCardPadding).toPx())
}

LazyRow(
state = rowState,
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(start = 24.dp, end = 24.dp)
) {
itemsIndexed(snacks) { index, snack ->
HighlightSnackItem(
snack,
onSnackClick,
index,
gradient,
gradientWidth,
scroll.value
snack = snack,
onSnackClick = onSnackClick,
index = index,
gradient = gradient,
scrollProvider = scrollProvider
)
}
}
Expand Down Expand Up @@ -214,17 +214,13 @@ private fun HighlightSnackItem(
onSnackClick: (Long) -> Unit,
index: Int,
gradient: List<Color>,
gradientWidth: Float,
scroll: Int,
scrollProvider: () -> Float,
modifier: Modifier = Modifier
) {
val left = index * with(LocalDensity.current) {
(HighlightCardWidth + HighlightCardPadding).toPx()
}
JetsnackCard(
modifier = modifier
.size(
width = 170.dp,
width = HighlightCardWidth,
height = 250.dp
)
.padding(bottom = 16.dp)
Expand All @@ -239,12 +235,22 @@ private fun HighlightSnackItem(
.height(160.dp)
.fillMaxWidth()
) {
val gradientOffset = left - (scroll / 3f)
Box(
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.offsetGradientBackground(gradient, gradientWidth, gradientOffset)
.offsetGradientBackground(
colors = gradient,
width = {
// The Cards show a gradient which spans 6 cards and scrolls with parallax.
6 * cardWidthWithPaddingPx
},
offset = {
val left = index * cardWidthWithPaddingPx
val gradientOffset = left - (scrollProvider() / 3f)
gradientOffset
}
)
)
SnackImage(
imageUrl = snack.imageUrl,
Expand Down Expand Up @@ -312,8 +318,7 @@ fun SnackCardPreview() {
onSnackClick = { },
index = 0,
gradient = JetsnackTheme.colors.gradient6_1,
gradientWidth = gradientWidth,
scroll = 0
scrollProvider = { 0f }
)
}
}

0 comments on commit a985151

Please sign in to comment.