Skip to content

Instantly share code, notes, and snippets.

@radiofun
Created January 1, 2026 22:16
Show Gist options
  • Select an option

  • Save radiofun/943b67f19cfe9eb62729d77f113cc892 to your computer and use it in GitHub Desktop.

Select an option

Save radiofun/943b67f19cfe9eb62729d77f113cc892 to your computer and use it in GitHub Desktop.
Distortion Sample view to use Distortion Shader
import SwiftUI
struct DistortionSampleView: View {
@State private var dragP : CGPoint = .init(x: -1000, y: -1000)
var body: some View {
ZStack {
// Black Background - replace with image, gradient, whatever you like.
Color.black
// Visualizing the "100 control points" (10x10 grid)
Canvas { context, size in
let rows = 10
let cols = 10
// Horizontal lines
for r in 0..<rows {
var path = Path()
for c in 0..<cols {
let x = CGFloat(c) * size.width / CGFloat(cols - 1)
let y = CGFloat(r) * size.height / CGFloat(rows - 1)
if c == 0 {
path.move(to: CGPoint(x: x, y: y))
} else {
path.addLine(to: CGPoint(x: x, y: y))
}
}
context.stroke(path, with: .color(.white.opacity(0.5)), lineWidth: 0.2)
}
// Vertical lines
for c in 0..<cols {
var path = Path()
for r in 0..<rows {
let x = CGFloat(c) * size.width / CGFloat(cols - 1)
let y = CGFloat(r) * size.height / CGFloat(rows - 1)
if r == 0 {
path.move(to: CGPoint(x: x, y: y))
} else {
path.addLine(to: CGPoint(x: x, y: y))
}
}
context.stroke(path, with: .color(.white.opacity(0.5)), lineWidth: 0.2)
}
// Intersection dots
for r in 0..<rows {
for c in 0..<cols {
let x = CGFloat(c) * size.width / CGFloat(cols - 1)
let y = CGFloat(r) * size.height / CGFloat(rows - 1)
context.fill(Path(ellipseIn: CGRect(x: x-1, y: y-1, width: 1, height: 1)), with: .color(.white.opacity(1.0)))
}
}
}
}
.frame(width:320,height:320)
.cornerRadius(20)
.layerEffect(
ShaderLibrary.distortion(
.boundingRect,
.float2(dragP)
),
maxSampleOffset: CGSize(width: 100, height: 100)
)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
dragP = value.location
}
.onEnded { value in
let centerX: CGFloat = 160
let centerY: CGFloat = 160
let dx = value.location.x - centerX
let dy = value.location.y - centerY
let threshold: CGFloat = 100
var exitX: CGFloat = dx < 0 ? -1000 : 1000
var exitY: CGFloat = dy < 0 ? -1000 : 1000
if abs(dx) < threshold {
exitX = centerX
}
if abs(dy) < threshold {
exitY = centerY
}
// Fallback: If both axes are within threshold, force an exit in the direction of the larger delta
if abs(dx) < threshold && abs(dy) < threshold {
if abs(dx) < abs(dy) {
exitX = centerX
exitY = dy < 0 ? -1000 : 1000
} else {
exitX = dx < 0 ? -1000 : 1000
exitY = centerY
}
}
withAnimation(.linear(duration: 1.0)) {
dragP = CGPoint(x: exitX, y: exitY)
}
}
)
}
}
#Preview {
DistortionSampleView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment