-
Notifications
You must be signed in to change notification settings - Fork 114
Enable screenshots of animated components #421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This adds support for capturing animated PNGs of components annotated with `@ShowkaseComposable`, by specifying the new `screenshotConfig` parameter. For example, to capture an animation for 2 seconds with a framerate of 60fps:
```kotlin
@ShowkaseComposable(
name = "MyAnimatedComponent",
group = "AnimatedGroup",
defaultStyle = true,
screenshotCaptureType = ScreenshotCaptureType.SingleAnimatedImage,
captureDurationMillis = 2000,
captureFramerate = 60,
)
@composable
fun MyAnimatedComponentPreview_Default() {
var animatedComponentState by remember { mutableStateOf(...) }
LaunchedEffect(Unit) {
animatedComponentState = ...
}
MyAnimatedComponent(animatedComponentState)
}
```
Updates Paparazzi to `2.0.0-alpha02` to fix [this issue](cashapp/paparazzi#1877) with compileSdk 36.
| testClassName: String | ||
| ) { | ||
| val showkaseScreenshotTestClassName = "${testClassName}_PaparazziShowkaseTest" | ||
| val showkaseScreenshotTestClassName = "${testClassName}Impl" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: This contains the fixes in #419 to shorten the generated Paparazzi image filenames - due to changes in Paparazzi, we can now encounter java.io.FileNotFoundException: File name too long on some systems.
| /** | ||
| * Configuration for how screenshots of the annotated Composable should be captured. | ||
| */ | ||
| sealed interface ScreenshotConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
| * Used by Paparazzi snapshot testing to determine if the component has any animation, and how to capture | ||
| * the screenshot. | ||
| */ | ||
| val screenshotCaptureType: ScreenshotCaptureType = ScreenshotCaptureType.SingleStaticImage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wish there was a cleaner way (with a single annotation param) to do this so that it was more encapsulated. I think Arrays are the only choice but that's ugly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah unfortunately we're limited by what annotation classes support, hence the need to convert it to ScreenshotConfig later down the line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@allenchen1154 can you use nested annotations to encapsulate all animation related properties?
eg
annotation class Foo(val bar: Bar)
annotation class Bar(val x: Int)
@Foo(bar = Bar(x = 1))
class Baz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What @elihart is suggesting would be really neat!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@elihart thanks for the suggestion, moved these params into a wrapper annotation
|
What's the current behavior when it comes to the Showkase browser itself? How are those preview rendered in the browser? Additionally, if you are using offsets, should they show up as separate previews? I think that would be the ideal behavior but since you are using Paparazzi for the screenshots, I suspect that might not be possible without some additional work as the browser shows live composable instead. |
|
In an ideal case, we maintain parity between what you see in the browser and what gets screenshot tested. |
|
@vinaygaba Currently the previews that show in the Showkase browser would just play the animation as it's specified by the preview Composable. So in the example I added, it would simply animate the red square to the right one time. You're right that generating separate browser components for the offsets would require more work/codegen. I think having this discrepancy is OK for now, as I'm hoping the APNG approach will be preferred. |
|
@vinaygaba (The offset approach doesn't actually work right now due to a bug in Paparazzi: cashapp/paparazzi#1645) |
This was causing an error in the compilation tests when running in KAPT mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes LGTM. Would be nice to add test coverage - processor tests especially
This test checks that when `screenshotCaptureType` is provided, the generated`ShowkaseBrowserComponent` instances have the correct `ScreenshotConfig` values.
|
@vinaygaba I added a processor test, will merge and prepare a 1.1.0 release. |
Includes changes from #421 back-ported to work on older version of XProcessing library, plus necessary changes to use newer Paparazzi
This adds support for capturing animated PNGs of components annotated with
@ShowkaseComposable, by specifying the newscreenshotCaptureTypeparameter. For example, to capture an animation for 2 seconds with a framerate of 60fps:@ShowkaseComposable( name = "MyAnimatedComponent", group = "AnimatedGroup", defaultStyle = true, screenshotCaptureType = ScreenshotCaptureType.SingleAnimatedImage, captureDurationMillis = 2000, captureFramerate = 60, ) @Composable fun MyAnimatedComponentPreview_Default() { var animatedComponentState by remember { mutableStateOf(...) } LaunchedEffect(Unit) { animatedComponentState = ... } MyAnimatedComponent(animatedComponentState) }Updates Paparazzi to
2.0.0-alpha02to fix this issue with compileSdk 36.Screen.Recording.2025-09-05.at.21.19.47.mov