oleksandrbalan / pagecurl

This library allows to create an effect of turning pages, which can be used in book reader applications, custom on-boarding screens or elsewhere.
Apache License 2.0
345 stars 32 forks source link

Need some help in implementing some additions / listener / 2 column #32

Open monsterbrain opened 6 months ago

monsterbrain commented 6 months ago

Hi, Thank you for the amazing library.

I would like to add some features, or use in some specific ways. Could you please point me in the right direction since I'm kind of a compose rookie :).

  1. How to listen to page curl end events ? Is there any way to observe the updatedCurrent or current value or do I have to expose the onChange callback to the config or as extra parameter to PageCurl composable ?

  2. On implementing a 2 page layout (2 column layout) using the page curl.

    • I plan to add 2 PageCurls (left page and right page) in a row and manually updating with odd/even row pages after each page curl (allowing one direction on each pages)
    • For the right page the current curl behavior is OK, but for left curl, the curl behavior should be in the opposite direction. I think I need to modify the library to reverse the behaviour
    • For the back page, I need to render the next page as the back page, instead of mirror image of current page. Can I achieve this by passing and drawing another composable as back page ?

Sorry for lot of questions. But any help or hints are appreciated. Thank you.

oleksandrbalan commented 5 months ago

Hey, sorry for the late reply.

As to the listener part I guess you do not need any onChange callback, as you can easily observe the current value and react accordingly.

For example:

val pages = remember { HowToPageData.simpleHowToPages }
val state = rememberPageCurlState()
val context = LocalContext.current
LaunchedEffect(state.current) {
    Toast.makeText(context, "New current: ${state.current}", Toast.LENGTH_SHORT).show()
}
PageCurl(
    count = pages.size,
    state = state,
) { index ->
    HowToPage(index, pages[index])
}

curl-current.webm

As to the 2 page layout, that is a really interesting idea to use Row and 2 separate PageCurls 🤔

But as for me it would be hard to keep the state in sync.

For example if I do a simple test:

val leftState = rememberPageCurlState()
val rightState = rememberPageCurlState()
LaunchedEffect(leftState.current) {
    rightState.snapTo(leftState.current)
}
LaunchedEffect(rightState.current) {
    leftState.snapTo(rightState.current)
}
Row {
    PageCurl(
        count = pages.size,
        state = leftState,
        modifier = Modifier.weight(1f),
    ) { index ->
        HowToPage(index, pages[index])
    }
    PageCurl(
        count = pages.size,
        state = rightState,
        modifier = Modifier.weight(1f),
    ) { index ->
        HowToPage(index, pages[index])
    }
}

There is a visible 1 frame delay, when page finishes it's movement and disappears, but the left part is not updated accordingly 😕

curl-two-sided.webm

As for me it should be one component with a single state, so that you can control when to update each part (left / right). However I hope you can at least reuse some parts of the library, such as gesture detection (DragCommonGesture.kt), some drawing (CurlDraw.kt) and state handling 🤞

monsterbrain commented 5 months ago

As to the listener part I guess you do not need any onChange callback, as you can easily observe the current value and react accordingly.

Thanks for this. For this, I have written a callback in config and called in onChange 🙏(compose rookie here). This is neat.

As for 2 Page layout, I have been going through the library for a few days now and I have implemented (with the help of my team) a 2 page layout system with back page (instead of inverted same page) using some horizontal flipping hacks.

I will post the details here soon, in case it will help someone.

monsterbrain commented 5 months ago

2 Page implementation (part 1)

disclaimer: Consider this as a hack implementation, not a proper implementation.

I added 2 pageCurl component in a Row component. The Right Page Curl works fine out of the box. The Curled page overlap across the left page. After curling, I added a onPageCurled callback, from which I switched the left page curl to show the curled page. (Both page curls share the same list of pages).

Showing the Back page on curling

Current library is showing an inverted, alpha overlayed, same page as the curling page, which makes sense for walkthroughs and simple pages. In case of showing the next page as curling, curl content needs to be changed from inverted image to next page image.

The hack I did was, to split the content drawing and curl drawing, and for curl drawing passing the content(current + 1), ie the next page.

But as per the current calculation, the content was mirrored or inverted horizontally.

So to show the actual page orientation, I have passed a Box with scaleX = -1f (inverted) and got it working somehow ;)

On Page Curl Flicker issue

There was a flicker when after curling the right page, and switching the left Page Curl (as seen in the @oleksandrbalan 's video above). It may be due to delay in loading the next page, while after finger is removed, the curl instantly disappears.

I fixed it by adding a fixed delay in ending a curl (finger raising), and launching the page change just before the page curl ends, so the flicker doesn't look okay (It can be seen that the bottom page change when curl is ending, but it looks kind of OK for now, until I find some better method to fix this issue)

..........

monsterbrain commented 3 months ago

2 Page implementation (part 2)

disclaimer: Consider this as a hack implementation, not a proper implementation.

continuing from the above ..

Z-Index problem

I guess this is due to using 2 composables. The problem being, when you curl, the left page goes under the right page. So I needed to dynamically change the z-Index, when the left page curl component starts curling.

Pinch Zoom

We needed to implement pinch zoom behaviour. So I intercept the touch interaction and when it is single touch, page curl handle the curling and when there is two touches, it switches to pinch zoom mode (implemented via another zoom modifier). But this has issue since the 2 pages are 2 composables and pinch zoom only work when pinching in either of the page. If the Pinching touches are in both left and right, it doesn't handle well. (You can literally page curl both at the same time!!) Any solutions to this @oleksandrbalan , I would be happy to hear from you.

Till then. Cheers.