Created
April 10, 2024 08:59
-
-
Save AndreVero/f37609a50872d5bc4c7c48d0490ca87f to your computer and use it in GitHub Desktop.
Drag modifier
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
fun Modifier.drag( | |
state: TimePickerState, | |
onItemPicked: (TimeItem) -> Unit, | |
magnitudeFactor: Float = 0.25f | |
) = pointerInput(Unit) { | |
val center = Offset(x = this.size.width / 2f, this.size.height / 2f) | |
val decay = splineBasedDecay<Float>(this) | |
coroutineScope { | |
while (true) { | |
var startedAngle = 0f | |
val pointerInput = awaitPointerEventScope { | |
// Catch the down event | |
val pointer = awaitFirstDown() | |
// Calculate the angle where user started dragging and convert to degrees | |
startedAngle = -atan2( | |
center.x - pointer.position.x, | |
center.y - pointer.position.y, | |
) * (180f / PI.toFloat()).mod(360f) | |
pointer | |
} | |
// Stop previous animation | |
state.stop() | |
val tracker = VelocityTracker() | |
var changeAngle = 0f | |
awaitPointerEventScope { | |
// Catch drag event | |
drag(pointerInput.id) { change -> | |
// Calculate the angle after user drag event and convert to degrees | |
changeAngle = -atan2( | |
center.x - change.position.x, | |
center.y - change.position.y, | |
) * (180f / PI.toFloat()).mod(360f) | |
launch { | |
// Change the current angle (later will be added to each item angle) | |
state.snapTo((state.oldAngle + (startedAngle - changeAngle).mod(360f))) | |
} | |
// Pass the info about changes to the VelocityTracker for later calculations | |
tracker.addPosition(change.uptimeMillis, change.position) | |
if (change.positionChange() != Offset.Zero) change.consume() | |
} | |
// Get magnitude of velocity and multiply by factor (to decrease the speed) | |
var velocity = tracker.calculateVelocity().getMagnitudeOfLinearVelocity() * magnitudeFactor | |
// Calculate the fling side (left or right) | |
val difference = startedAngle - changeAngle | |
velocity = if (difference > 0) | |
velocity else -velocity | |
// Calculate new angle according to the velocity | |
val targetAngle = decay.calculateTargetValue( | |
state.angle, | |
velocity, | |
) | |
launch { | |
// Animate items to the new angle with velocity | |
state.decayTo( | |
angle = targetAngle, | |
velocity = velocity, | |
) | |
} | |
} | |
// In the end save the old angle for further calculations | |
state.oldAngle = state.angle.mod(360f) | |
} | |
} | |
} | |
// This is used to determine the speed of the user's drag gesture | |
private fun Velocity.getMagnitudeOfLinearVelocity(): Float { | |
return sqrt(this.x.pow(2) + this.y.pow(2)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment