Created
February 5, 2018 09:54
-
-
Save Popalay/92a66904ccb321de52153b7e3b662197 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.animation.Animator | |
import android.animation.AnimatorListenerAdapter | |
import android.animation.AnimatorSet | |
import android.animation.ObjectAnimator | |
import android.annotation.TargetApi | |
import android.graphics.Outline | |
import android.graphics.Rect | |
import android.os.Build | |
import android.transition.Transition | |
import android.transition.TransitionValues | |
import android.view.View | |
import android.view.ViewAnimationUtils | |
import android.view.ViewGroup | |
import android.view.ViewOutlineProvider | |
import android.view.animation.LinearInterpolator | |
import kotlin.math.roundToInt | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
class CircularReveal : Transition() { | |
companion object { | |
private const val DEFAULT_DURATION = 200L | |
private const val BOUNDS = "BOUNDS" | |
private val TRANSITION_PROPERTIES = arrayOf(BOUNDS) | |
} | |
init { | |
duration = DEFAULT_DURATION | |
interpolator = LinearInterpolator() | |
} | |
override fun getTransitionProperties() = TRANSITION_PROPERTIES | |
override fun captureStartValues(transitionValues: TransitionValues) { | |
captureValues(transitionValues) | |
} | |
override fun captureEndValues(transitionValues: TransitionValues) { | |
captureValues(transitionValues) | |
} | |
override fun createAnimator( | |
sceneRoot: ViewGroup, | |
startValues: TransitionValues?, | |
endValues: TransitionValues? | |
): Animator? { | |
if (startValues == null || endValues == null) return null | |
val startBounds = startValues.values[BOUNDS] as Rect | |
val endBounds = endValues.values[BOUNDS] as Rect | |
val isReveal = endBounds.width() > startBounds.width() | |
val view = endValues.view | |
val translationX = startBounds.centerX() - endBounds.centerX() | |
val translationY = startBounds.centerY() - endBounds.centerY() | |
if (isReveal) { | |
view.translationX = translationX.toFloat() | |
view.translationY = translationY.toFloat() | |
} | |
val translate = ObjectAnimator.ofFloat( | |
view, | |
View.TRANSLATION_X, | |
View.TRANSLATION_Y, | |
if (isReveal) pathMotion.getPath(translationX.toFloat(), translationY.toFloat(), 0f, 0f) | |
else pathMotion.getPath(0f, 0f, (-translationX).toFloat(), (-translationY).toFloat()) | |
) | |
val startScale = startBounds.width() / endBounds.width().toFloat() | |
val endScale = endBounds.width() / startBounds.width().toFloat() | |
val resize = AnimatorSet().apply { | |
if (isReveal) { | |
play(ObjectAnimator.ofFloat(view, View.SCALE_X, startScale, 1F)) | |
.with(ObjectAnimator.ofFloat(view, View.SCALE_Y, startScale, 1F)) | |
} else { | |
play(ObjectAnimator.ofFloat(view, View.SCALE_X, endScale)) | |
.with(ObjectAnimator.ofFloat(view, View.SCALE_Y, endScale)) | |
} | |
} | |
val circularReveal: Animator = if (isReveal) { | |
ViewAnimationUtils.createCircularReveal( | |
view, | |
view.width / 2, | |
view.height / 2, | |
startBounds.width() / 2F * endScale, | |
Math.hypot(endBounds.width().toDouble(), endBounds.height().toDouble()).toFloat() | |
) | |
} else { | |
view.layout(startBounds.left, startBounds.top, startBounds.right, startBounds.bottom) | |
ViewAnimationUtils.createCircularReveal( | |
view, | |
view.width / 2, | |
view.height / 2, | |
Math.hypot(startBounds.width().toDouble(), startBounds.height().toDouble()).toFloat(), | |
endBounds.width() / 2F * startScale | |
).apply { | |
addListener(object : AnimatorListenerAdapter() { | |
override fun onAnimationEnd(animation: Animator) { | |
view.outlineProvider = object : ViewOutlineProvider() { | |
override fun getOutline(view: View, outline: Outline) { | |
val left = ((view.width - endBounds.width() * startScale) / 2).roundToInt() | |
val top = ((view.height - endBounds.height() * startScale) / 2).roundToInt() | |
val right = left + Math.min(view.width, view.height) | |
val bottom = top + Math.min(view.width, view.height) | |
outline.setOval(left, top, right, bottom) | |
view.clipToOutline = true | |
} | |
} | |
} | |
}) | |
} | |
} | |
val transition = AnimatorSet().apply { | |
playTogether(translate, circularReveal, resize) | |
addListener(object : AnimatorListenerAdapter() { | |
override fun onAnimationEnd(animation: Animator) { | |
view.overlay.clear() | |
} | |
}) | |
} | |
return NoPauseAnimator(transition) | |
} | |
private fun captureValues(transitionValues: TransitionValues) { | |
val view = transitionValues.view | |
if (view == null || view.width <= 0 || view.height <= 0) return | |
transitionValues.values[BOUNDS] = Rect(view.left, view.top, view.right, view.bottom) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment