Skip to content

Instantly share code, notes, and snippets.

@JRR-OSU
Last active August 11, 2023 08:27
Show Gist options
  • Save JRR-OSU/667dc80e85a860726fd4c1ed56b31c07 to your computer and use it in GitHub Desktop.
Save JRR-OSU/667dc80e85a860726fd4c1ed56b31c07 to your computer and use it in GitHub Desktop.
SwiftUI Crossfade and Flip Shader Example
import SwiftUI
struct DemoView: View {
@State var effectValue: Double
let end = 2.75
init() {
self._effectValue = State(initialValue: end)
}
var body: some View {
VStack {
Slider(value: $effectValue, in: 0.3...end) {
EmptyView()
}.padding()
ZStack {
TimelineView(.animation(minimumInterval: 1 / 120)) { context in
ZStack {
WeatherCard1(currentTime: context.date)
WeatherCard2(currentTime: context.date)
.visualEffect { content, proxy in
content
.layerEffect(ShaderLibrary.fadeAndFlip(
.float(effectValue),
.float2(proxy.size)), maxSampleOffset: proxy.size)
}
}
}
}
.drawingGroup()
.frame(width: 250, height: 250)
.clipShape(RoundedRectangle(cornerRadius: 8))
.onTapGesture {
withAnimation(.interpolatingSpring(duration: 1.5)) {
reverse()
}
}
}
}
func reverse() {
effectValue = effectValue == end ? 0.3 : end
}
}
#include <metal_stdlib>
#include <SwiftUI/SwiftUI_Metal.h>
using namespace metal;
#define M_PI 3.14159265358979323846264338327950288
float2 plane(float3 p, float3 d, float3 normal)
{
float3 up = float3(0, 1, 0);
float3 right = cross(up, normal);
float dn = dot(d, normal);
float pn = dot(p, normal);
float3 hit = p - d / dn * pn;
float2 uv;
uv.x = dot(hit, right);
uv.y = dot(hit, up);
return uv;
}
float2 mod(float2 x, float f) {
return float2(fmod(x.x, f), fmod(x.y, f));
}
float mod(float x, float y) {
return fmod(x, y);
}
float radians(float degrees) {
return degrees * (M_PI / 180.0);
}
[[ stitchable ]] half4 fadeAndFlip(float2 position, SwiftUI::Layer layer, float time2, float2 size) {
float scaleFactor = 1.0;
half4 pixelColor = layer.sample(position);
// THIS IS CRAZY but this needs to be divided by a negative in order to translate to the proper coordinate system...🤦
float2 xy = position - size.xy / -2.0;
float grid_width = 32;
xy /= grid_width;
float2 grid = floor(xy);
xy = mod(xy, 1.0) - 0.5;
float alpha = 0.0;
float offset = (grid.x - grid.y)*0.1;
float time = (time2 * 1.0 - offset) * scaleFactor;
// This allows it to flip just once
//time = mod(time, 6.0);
time = clamp(time - 1., 0., 1.);
alpha += smoothstep(0.0, 1.0, time);
alpha += 1.0 - smoothstep(3.0, 4.0, time);
alpha = abs(mod(alpha, 2.0) - 1.0);
// This can also be just "step" but I think smoothstep looks nicer
float side = step( 0.5, alpha);
alpha = radians(alpha*180.0);
float4 n = float4(cos(alpha),0,sin(alpha),-sin(alpha));
float3 d = float3(1.0,xy.y,xy.x);
float3 p = float3(-1.0+n.w/4.0,0,0);
float2 uv = plane(p, d, n.xyz);
uv += 0.5;
if (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 || uv.y > 1.0) {
return half4(1.0, 1.0, 1.0, 1.0); // Returning a transparent pixel
}
half4 c1 = 0.0;
half4 c2 = pixelColor;
pixelColor = mix(c1, c2, side);
return pixelColor;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment