Created
December 29, 2024 06:31
-
-
Save Saadnajmi/9d46dcd093068404239ee32402d93e41 to your computer and use it in GitHub Desktop.
Ripple Effect Shader with react-native-skia
This file contains 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 { Canvas, Circle, Group, Rect, Shader, Skia, useClock, RuntimeShader, SkRuntimeEffect, RoundedRect } from "@shopify/react-native-skia"; | |
import { SafeAreaView, StyleSheet } from "react-native"; | |
import { useDerivedValue, useSharedValue } from "react-native-reanimated"; | |
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler'; | |
const styles = StyleSheet.create({ | |
centered: { | |
flex: 1, | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
}); | |
const rippleShader = Skia.RuntimeEffect.Make(` | |
uniform float2 u_origin; | |
uniform float u_time; | |
uniform float u_amplitude; | |
uniform float u_frequency; | |
uniform float u_decay; | |
uniform float u_speed; | |
uniform shader image; | |
half4 main(float2 position) { | |
// The distance of the current pixel position from origin | |
float dist = distance(position, u_origin); | |
// The amount of time it takes for the ripple to arrive at the current pixel position. | |
float delay = dist / u_speed; | |
// Adjust for delay, clamp to 0. | |
float time = u_time - delay; | |
time -= delay; | |
time = max(0.0, time); | |
// The ripple is a sine wave that scales by an exponential decay function. | |
float rippleAmount = u_amplitude * sin(u_frequency * time) * exp(-u_decay * time); | |
// A vector of length amplitude that points away from position. | |
float2 n = normalize(position - u_origin); | |
// Scale n by the ripple amount at the current pixel position and add it | |
// to the current pixel position. | |
// | |
// This new position moves toward or away from origin based on the | |
// sign and magnitude of rippleAmount. | |
float2 newPosition = position + rippleAmount * n; | |
// Sample the layer at the new position. | |
half4 color = image.eval(newPosition).rgba; | |
// Lighten or darken the color based on the ripple amount and its alpha component. | |
color.rgb += 0.3 * (rippleAmount / u_amplitude) * color.a; | |
return color; | |
} | |
`) as SkRuntimeEffect; | |
export const Ripple = () => { | |
const width = 300; | |
const height = 600; | |
const duration = 2; | |
const clock = useClock(); | |
const uniforms = useDerivedValue(() => ({ iTime: clock.value / 1000 }), [clock]); | |
const touchX = useSharedValue(0); | |
const touchY = useSharedValue(0); | |
const touchStart = useSharedValue(0); | |
const touchPoint = useDerivedValue(() => ( | |
{ x: touchX.value, y: touchY.value } | |
), [touchX, touchY]); | |
const elapsedTime = useDerivedValue(() => ( | |
(clock.value - touchStart.value) / 1000 | |
), [clock, touchStart]); | |
const shaderEnabled = useDerivedValue(() => { | |
return rippleShader && 0 < elapsedTime.value && (elapsedTime.value < duration); | |
}, [rippleShader, elapsedTime]); | |
const rippleUniforms = useDerivedValue(() => ({ | |
u_origin: touchPoint.value, | |
u_time: elapsedTime.value, | |
u_amplitude: 12, | |
u_frequency: 15, | |
u_decay: 10, | |
u_speed: 1200, | |
}), [clock, touchStart, touchPoint]); | |
const tap = Gesture.Tap().onBegin((e) => { | |
touchX.value = e.x; | |
touchY.value = e.y; | |
touchStart.value = clock.value; | |
console.log('tap', e.x, e.y, 'time', clock.value); | |
}); | |
return ( | |
<SafeAreaView style={styles.centered}> | |
<GestureHandlerRootView> | |
<GestureDetector gesture={tap}> | |
<Canvas style={{width: width, height: height}}> | |
{shaderEnabled && <RuntimeShader source={rippleShader} uniforms={rippleUniforms} />} | |
<RoundedRect x={10} y={10} r={20} width={250} height={500} color={'red'} /> | |
</Canvas> | |
</GestureDetector> | |
</GestureHandlerRootView> | |
</SafeAreaView> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment