🚀🌆🏙 Display differences or animate progress between 2 images or Composables with overlay and customization options, zoom, pan gestures, and progress to observe properties for animating before-after progress
Composables to display Images, or Composables as before and after composables to display differences or animate progress between 2 layouts or Composables with overlay and customization options and progress observe properties for animating before-after progress
To get a Git project into your build:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.SmartToolFactory:Compose-BeforeAfter:<version>'
}
Image that takes two `ImageBitmaps as parameter and displays them based on specified order. By default Labels and Overlay is provided and overload function that accept other Composables as overlay to customize
@Composable
fun BeforeAfterImage(
modifier: Modifier = Modifier,
beforeImage: ImageBitmap,
afterImage: ImageBitmap,
enableProgressWithTouch: Boolean = true,
enableZoom: Boolean = true,
contentOrder: ContentOrder = ContentOrder.BeforeAfter,
overlayStyle: OverlayStyle = OverlayStyle(),
beforeLabel: @Composable BoxScope.() -> Unit = { BeforeLabel(contentOrder = contentOrder) },
afterLabel: @Composable BoxScope.() -> Unit = { AfterLabel(contentOrder = contentOrder) },
contentScale: ContentScale = ContentScale.Fit,
alignment: Alignment = Alignment.Center,
contentDescription: String? = null,
) {
}
and
@Composable
fun BeforeAfterImage(
modifier: Modifier = Modifier,
beforeImage: ImageBitmap,
afterImage: ImageBitmap,
enableProgressWithTouch: Boolean = true,
enableZoom: Boolean = true,
contentOrder: ContentOrder = ContentOrder.BeforeAfter,
@FloatRange(from = 0.0, to = 100.0) progress: Float = 50f,
onProgressChange: ((Float) -> Unit)? = null,
overlayStyle: OverlayStyle = OverlayStyle(),
beforeLabel: @Composable BoxScope.() -> Unit = { BeforeLabel(contentOrder = contentOrder) },
afterLabel: @Composable BoxScope.() -> Unit = { AfterLabel(contentOrder = contentOrder) },
contentScale: ContentScale = ContentScale.Fit,
alignment: Alignment = Alignment.Center,
contentDescription: String? = null,
)
overloads has default DefaultOverlay
@Composable
internal fun DefaultOverlay(
width: Dp,
height: Dp,
position: Offset,
overlayStyle: OverlayStyle
)
@Composable
fun BeforeAfterImage(
modifier: Modifier = Modifier,
beforeImage: ImageBitmap,
afterImage: ImageBitmap,
enableProgressWithTouch: Boolean = true,
enableZoom: Boolean = true,
contentOrder: ContentOrder = ContentOrder.BeforeAfter,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
contentDescription: String? = null,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
beforeLabel: @Composable BoxScope.() -> Unit = { BeforeLabel(contentOrder = contentOrder) },
afterLabel: @Composable BoxScope.() -> Unit = { AfterLabel(contentOrder = contentOrder) },
overlay: @Composable BeforeAfterImageScope.() -> Unit
)
and
@Composable
fun BeforeAfterImage(
modifier: Modifier = Modifier,
beforeImage: ImageBitmap,
afterImage: ImageBitmap,
enableProgressWithTouch: Boolean = true,
enableZoom: Boolean = true,
contentOrder: ContentOrder = ContentOrder.BeforeAfter,
@FloatRange(from = 0.0, to = 100.0) progress: Float = 50f,
onProgressChange: ((Float) -> Unit)? = null,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
contentDescription: String? = null,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
beforeLabel: @Composable BoxScope.() -> Unit = { BeforeLabel(contentOrder = contentOrder) },
afterLabel: @Composable BoxScope.() -> Unit = { AfterLabel(contentOrder = contentOrder) },
overlay: @Composable BeforeAfterImageScope.() -> Unit
)
has overlay
parameters to add another Composable to draw overlay
BeforeAfterImage(
modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.fillMaxWidth()
.aspectRatio(4 / 3f),
beforeImage = imageBefore,
afterImage = imageAfter,
contentScale = contentScale
)
or
BeforeAfterImage(
modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.border(3.dp, Color(0xffE91E63), RoundedCornerShape(10.dp))
.fillMaxWidth()
.aspectRatio(4 / 3f),
beforeImage = imageBefore3,
afterImage = imageAfter3,
progress = progress,
onProgressChange = {},
contentScale = contentScale,
)
BoxWithConstraints
ContentScale.Fit
for instance maintains src ratio and scales image to fit inside the parent.beforeImage
from 0.0f to 1.0f representing fully transparent to fully opaque respectivelybeforeImage
when drawn into the destinationbeforeImage
when it is scaled and drawn into the destination. The default is FilterQuality.Low
which scales using a bilinear sampling algorithmbeforeImage
is drawn. This is useful for drawing thumbs, cropping or another layout that should match position with the image that is scaled is drawnLayout can draw any Composable, image, or video as before and after layout and can be zoomed and panned when [enableZoom] is true and returns a callback to animate before/after progress
@Composable
fun BeforeAfterLayout(
modifier: Modifier = Modifier,
enableProgressWithTouch: Boolean = true,
enableZoom: Boolean = true,
contentOrder: ContentOrder = ContentOrder.BeforeAfter,
overlayStyle: OverlayStyle = OverlayStyle(),
beforeContent: @Composable () -> Unit,
afterContent: @Composable () -> Unit,
beforeLabel: @Composable BoxScope.() -> Unit = { BeforeLabel(contentOrder = contentOrder) },
afterLabel: @Composable BoxScope.() -> Unit = { AfterLabel(contentOrder = contentOrder) },
)
and
@Composable
fun BeforeAfterLayout(
modifier: Modifier = Modifier,
enableProgressWithTouch: Boolean = true,
enableZoom: Boolean = true,
contentOrder: ContentOrder = ContentOrder.BeforeAfter,
@FloatRange(from = 0.0, to = 100.0) progress: Float = 50f,
onProgressChange: ((Float) -> Unit)? = null,
overlayStyle: OverlayStyle = OverlayStyle(),
beforeContent: @Composable () -> Unit,
afterContent: @Composable () -> Unit,
beforeLabel: @Composable BoxScope.() -> Unit = { BeforeLabel(contentOrder = contentOrder) },
afterLabel: @Composable BoxScope.() -> Unit = { AfterLabel(contentOrder = contentOrder) },
)
Customize
BeforeAfterLayout(
modifier = Modifier
.shadow(1.dp, RoundedCornerShape(10.dp))
.fillMaxWidth()
.aspectRatio(4 / 3f),
beforeContent = {
DemoImage(imageBitmap = imageBefore)
},
afterContent = {
DemoImage(imageBitmap = imageAfter)
},
overlayStyle = OverlayStyle(
dividerColor = Color(0xffF44336),
dividerWidth = 2.dp,
thumbShape = CutCornerShape(8.dp),
thumbBackgroundColor = Color.Red,
thumbTintColor = Color.White
)
)
Display difference between Composables with Material Design2 and M3
BeforeAfterLayout(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
progress = progress,
beforeContent = {
BeforeComposable(progress)
},
afterContent = {
AfterComposable(progress)
},
enableProgressWithTouch = false,
enableZoom = false,
beforeLabel = null,
afterLabel = null,
overlay = null
)
Animate like a ProgressBar
val transition: InfiniteTransition = rememberInfiniteTransition()
// Infinite progress animation
val progress by transition.animateFloat(
initialValue = 0f,
targetValue = 100f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 4000,
easing = FastOutSlowInEasing
),
repeatMode = RepeatMode.Reverse
)
)
BeforeAfterLayout(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
progress = progress,
beforeContent = {
BeforeComposable(progress)
},
afterContent = {
AfterComposable(progress)
},
enableProgressWithTouch = false,
enableZoom = false,
beforeLabel = null,
afterLabel = null,
overlay = null
)
Display before and after videos with Exoplayer
If you have a fix please open a PR or answer this question Both are appreciated greatly
BeforeAfterLayout(
modifier = Modifier
.fillMaxSize()
.aspectRatio(4 / 3f),
beforeContent = {
MyPlayer(
modifier = Modifier
.border(3.dp, Color.Red),
uri = "asset:///floodplain_dirty.mp4"
)
},
afterContent = {
MyPlayer(
modifier = Modifier
.border(3.dp, Color.Yellow),
uri = "asset:///floodplain_clean.mp4"
)
},
enableZoom = false
)