Skip to content
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

Feature request: dismiss target when clicking outside of it #13

Closed
ZacSweers opened this issue Jan 22, 2023 · 4 comments
Closed

Feature request: dismiss target when clicking outside of it #13

ZacSweers opened this issue Jan 22, 2023 · 4 comments

Comments

@ZacSweers
Copy link

Currently this library only dismisses targets if the user clicks the exact target space, but this isn't normal behavior when the target being showcased isn't normally clickable. It would be ideal if the library allowed dismissal of targets when tapping outside of the target area.

@sepidevatankhah
Copy link

sepidevatankhah commented Apr 26, 2023

Thank you for adjusting the library with Jetpack Compose.

I have the same request, could you pls give us the onclick event for this area(outside the target area), then the developer can decide to close the helping hand with onclick in this area or not !

Thank you in advance for taking care. cp-radhika-s
Jimmy Sanghani
Jan Bína

Best,
Sepi

@sepidevatankhah
Copy link

cp-radhika-s
Jimmy Sanghani
Jan Bína

If you just add the .clickable { onShowcaseCompleted() } inside the TargetContent function, it will fix the problem
Look :

@Composable
fun TargetContent(
    target: IntroShowcaseTargets,
    modifier: Modifier = Modifier,
    onShowcaseCompleted: () -> Unit,
) {
    val screenHeight = LocalConfiguration.current.screenHeightDp
    val targetCords = target.coordinates
    val gutterArea = 88.dp
    val targetRect = targetCords.boundsInRoot()

    val yOffset = with(LocalDensity.current) {
        targetCords.positionInRoot().y.toDp()
    }

    val isTargetInGutter = gutterArea > yOffset || yOffset > screenHeight.dp.minus(gutterArea)

    val maxDimension =
        max(targetCords.size.width.absoluteValue, targetCords.size.height.absoluteValue)
    val targetRadius = maxDimension / 2f + 40f

    val animationSpec = infiniteRepeatable<Float>(
        animation = tween(2000, easing = FastOutLinearInEasing),
        repeatMode = RepeatMode.Restart,
    )

    var outerOffset by remember {
        mutableStateOf(Offset(0f, 0f))
    }

    var outerRadius by remember {
        mutableStateOf(0f)
    }

    val outerAnimatable = remember { Animatable(0.6f) }

    val animatables = listOf(
        remember { Animatable(0f) },
        remember { Animatable(0f) }
    )

    LaunchedEffect(target) {
        outerAnimatable.snapTo(0.6f)

        outerAnimatable.animateTo(
            targetValue = 1f,
            animationSpec = tween(
                durationMillis = 500,
                easing = FastOutSlowInEasing,
            ),
        )
    }

    animatables.forEachIndexed { index, animatable ->
        LaunchedEffect(animatable) {
            delay(index * 1000L)
            animatable.animateTo(
                targetValue = 1f, animationSpec = animationSpec
            )
        }
    }

    val dys = animatables.map { it.value }
    Box {
        Canvas(
            modifier = modifier
                .fillMaxSize()
                .pointerInput(target) {
                    detectTapGestures { tapOffeset ->
                        if (targetRect.contains(tapOffeset)) {
                            onShowcaseCompleted()
                        }
                    }
                }
                .clickable { onShowcaseCompleted() }
                .graphicsLayer(alpha = 0.99f)
        ) {

            drawCircle(
                color = target.style.backgroundColor,
                center = outerOffset,
                radius = outerRadius * outerAnimatable.value,
                alpha = target.style.backgroundAlpha
            )

            dys.forEach { dy ->
                drawCircle(
                    color = target.style.targetCircleColor,
                    radius = maxDimension * dy * 2f,
                    center = targetRect.center,
                    alpha = 1 - dy
                )
            }

            drawCircle(
                color = target.style.targetCircleColor,
                radius = targetRadius,
                center = targetRect.center,
                blendMode = BlendMode.Xor
            )
        }

        ShowCaseText(target, targetRect, targetRadius) { textCoords ->
            val contentRect = textCoords.boundsInParent()
            val outerRect = getOuterRect(contentRect, targetRect, isTargetInGutter)
            val possibleOffset = getOuterCircleCenter(targetRect, contentRect, targetRadius)

            outerOffset = if (isTargetInGutter) {
                outerRect.center
            } else {
                possibleOffset
            }

            outerRadius = getOuterRadius(outerRect) + targetRadius
        }
    }
}

@wongcain
Copy link

Alternatively, expose a method on IntroShowCaseState for incrementing currentTargetIndex. Something like this:

class IntroShowCaseState internal constructor(
  initialIndex: Int,
) {

  internal var targets = mutableStateMapOf<Int, IntroShowcaseTargets>()

  var currentTargetIndex by mutableStateOf(initialIndex)
    internal set

  val currentTarget: IntroShowcaseTargets?
    get() = targets[currentTargetIndex]

  /* NEW METHOD */
  fun onShowcaseStepCompleted() {
    if (currentTarget != null) {
      currentTargetIndex++
    }
  }
}

@cp-radhika-s
Copy link
Member

Closing this issue since this has been fixed in version 2.0.0. Thanks for reporting this issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants