Last active
October 20, 2024 02:43
-
-
Save daehyeonmun2021/21aea72f655da6c0ca8922191daade5e to your computer and use it in GitHub Desktop.
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, | |
DataSourceParam, | |
dist, | |
ImageShader, | |
rect, | |
RoundedRect, | |
rrect, | |
Shader, | |
Skia, | |
SkPoint, | |
SkRect, | |
Uniforms, | |
useImage, | |
vec, | |
} from '@shopify/react-native-skia'; | |
import { useMemo } from 'react'; | |
import { View } from 'react-native'; | |
import { Gesture, GestureDetector } from 'react-native-gesture-handler'; | |
import { useDerivedValue, useFrameCallback, useSharedValue } from 'react-native-reanimated'; | |
type Value = string | number; | |
type Values = Value[]; | |
export const glsl = (source: TemplateStringsArray, ...values: Values) => { | |
const processed = source.flatMap((s, i) => [s, values[i]]).filter(Boolean); | |
return processed.join(''); | |
}; | |
export const frag = (source: TemplateStringsArray, ...values: Values) => { | |
const code = glsl(source, ...values); | |
const rt = Skia.RuntimeEffect.Make(code); | |
if (rt === null) { | |
throw new Error("Couldn't Compile Shader"); | |
} | |
return rt; | |
}; | |
const ripple = frag` | |
uniform shader iImage; | |
uniform vec2 iResolution; | |
uniform vec2 iOrigin; | |
uniform float iRadius; | |
const float amplitude = 0.03; | |
const float frequency = 10.0; | |
const float decay = 2.0; | |
vec4 main(vec2 xy) { | |
float delta = 1.0 - smoothstep(0.0, iRadius, distance(xy, iOrigin)); | |
float rippleAmount = amplitude * sin(frequency * delta) * exp(-decay * delta); | |
xy += rippleAmount * iResolution; | |
vec3 color = iImage.eval(xy).rgb; | |
color += 0.3 * (rippleAmount / amplitude); | |
return vec4(color, 1.0); | |
} | |
`; | |
const SPEED = 12; | |
export const Ripple = ({ | |
source, | |
imageRect, | |
imageRectRadius, | |
}: { | |
source: DataSourceParam; | |
imageRect: SkRect; | |
imageRectRadius: number; | |
}) => { | |
const iOrigin = useSharedValue(vec(0)); | |
const image = useImage(source); | |
const rippleRadius = useSharedValue(0); | |
const rippleMaxRadius = useSharedValue(0); | |
const getMaxRadius = (pos: SkPoint) => { | |
'worklet'; | |
const spacing = 300; | |
const c1 = dist(vec(-spacing, -spacing), pos); | |
const c2 = dist(vec(imageRect.width + spacing, -spacing), pos); | |
const c3 = dist(vec(imageRect.width + spacing, imageRect.height + spacing), pos); | |
const c4 = dist(vec(-spacing, imageRect.height + spacing), pos); | |
return Math.max(c1, c2, c3, c4); | |
}; | |
const uniforms = useDerivedValue<Uniforms>(() => { | |
return { | |
iResolution: [imageRect.width, imageRect.height], | |
iOrigin: iOrigin.value, | |
iRadius: rippleRadius.value, | |
}; | |
}, []); | |
const tap = useMemo(() => { | |
return Gesture.Tap().onEnd(({ x, y }) => { | |
iOrigin.value = vec(x, y); | |
rippleRadius.value = 0; | |
rippleMaxRadius.value = getMaxRadius(iOrigin.value); | |
}); | |
}, []); | |
useFrameCallback(() => { | |
if (rippleRadius.value < rippleMaxRadius.value) { | |
rippleRadius.value += SPEED; | |
} | |
}, true); | |
if (!image) { | |
return null; | |
} | |
return ( | |
<GestureDetector gesture={tap}> | |
<Canvas | |
style={{ | |
width: imageRect.width, | |
height: imageRect.height, | |
shadowColor: '#000', | |
shadowOffset: { | |
width: 0, | |
height: 6, | |
}, | |
shadowOpacity: 0.37, | |
shadowRadius: 7.49, | |
elevation: 12, | |
}} | |
> | |
<RoundedRect rect={rrect(imageRect, imageRectRadius, imageRectRadius)}> | |
<Shader source={ripple} uniforms={uniforms}> | |
<ImageShader image={image} rect={imageRect} fit="cover" /> | |
</Shader> | |
</RoundedRect> | |
</Canvas> | |
</GestureDetector> | |
); | |
}; | |
const Page = () => { | |
return ( | |
<View | |
style={{ | |
flex: 1, | |
justifyContent: 'center', | |
alignItems: 'center', | |
}} | |
> | |
<Ripple | |
source="https://cdn.britannica.com/78/43678-050-F4DC8D93/Starry-Night-canvas-Vincent-van-Gogh-New-1889.jpg" | |
imageRect={rect(0, 0, 360, 500)} | |
imageRectRadius={20} | |
/> | |
</View> | |
); | |
}; | |
export default Page; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment