Skip to content

Instantly share code, notes, and snippets.

@TylerMcCraw
Forked from surajsau/ParallaxScreen.kt
Created May 26, 2022 19:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TylerMcCraw/b9af98be21e31f09bf8835e2568f9e48 to your computer and use it in GitHub Desktop.
Save TylerMcCraw/b9af98be21e31f09bf8835e2568f9e48 to your computer and use it in GitHub Desktop.
Parallax effect with Jetpack Compose
val context = LocalContext.current
val scope = rememberCoroutineScope()
var data by remember { mutableStateOf<SensorData?>(null) }
DisposableEffect(Unit) {
val dataManager = SensorDataManager(context)
dataManager.init()
scope.launch {
dataManager.data
.receiveAsFlow()
.onEach { data = it }
.collect()
}
onDispose {
dataManager.cancel()
}
}
if (data != null) {
Box(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center)
) {
// Blurred image as glow shadow for the card
// Will move in opposite direction of Image Card to reveal itself when inclined
Image(
painter = painterResource(id = R.drawable.beach),
modifier = Modifier
.offset(
x = (-data!!.roll * 0.5).dp,
y = (data!!.pitch * 0.7).dp
)
.width(256.dp)
.height(356.dp)
.blur(radius = 24.dp, edgeTreatment = BlurredEdgeTreatment.Unbounded),
contentDescription = null,
contentScale = ContentScale.FillHeight,
)
// White edge shadow (should move slightly slower than the card)
Box(
modifier = Modifier
.offset(
x = (data!!.roll * 0.35).dp,
y = (-data!!.pitch * 0.35).dp
)
.width(300.dp)
.height(400.dp)
.background(
color = Color.White.copy(alpha = 0.3f),
shape = RoundedCornerShape(16.dp)
),
)
// Image Card (with slight parallax for the image itself)
Image(
painter = painterResource(id = R.drawable.beach),
modifier = Modifier
.offset(
x = (data!!.roll * 0.4).dp,
y = (-data!!.pitch * 0.4).dp
)
.width(300.dp)
.height(400.dp)
.clip(RoundedCornerShape(16.dp)),
contentDescription = null,
contentScale = ContentScale.FillHeight,
alignment = BiasAlignment(
horizontalBias = -(data!!.roll * 0.001).toFloat(),
verticalBias = 0f,
)
)
}
}
class SensorDataManager (context: Context): SensorEventListener {
private val sensorManager by lazy {
context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
fun init() {
Log.d("SensorDataManager", "init")
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
val magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI)
sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI)
}
private var gravity: FloatArray? = null
private var geomagnetic: FloatArray? = null
val data: Channel<SensorData> = Channel(Channel.UNLIMITED)
override fun onSensorChanged(event: SensorEvent?) {
if (event?.sensor?.type == Sensor.TYPE_GRAVITY)
gravity = event.values
if (event?.sensor?.type == Sensor.TYPE_MAGNETIC_FIELD)
geomagnetic = event.values
if (gravity != null && geomagnetic != null) {
var r = FloatArray(9)
var i = FloatArray(9)
if (SensorManager.getRotationMatrix(r, i, gravity, geomagnetic)) {
var orientation = FloatArray(3)
SensorManager.getOrientation(r, orientation)
data.trySend(
SensorData(
roll = orientation[2] * 180 / Math.PI,
pitch = orientation[1] * 180 / Math.PI
)
)
}
}
}
fun cancel() {
Log.d("SensorDataManager", "cancel")
sensorManager.unregisterListener(this)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
data class SensorData(
val roll: Double,
val pitch: Double
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment