Skip to content

Commit 51f3134

Browse files
committed
fix(zoom): use ZTP formula for zoom in and out transition
1 parent 9da8c0f commit 51f3134

1 file changed

Lines changed: 49 additions & 52 deletions

File tree

Reframed/Editor/ZoomTimeline.swift

Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,48 @@ final class ZoomTimeline: @unchecked Sendable {
3333
return CGRect(x: 0, y: 0, width: 1, height: 1)
3434
}
3535

36-
let zoom = interpolatedZoom(at: time, keyframes: kfs)
36+
if time <= kfs.first!.t {
37+
return ztpRect(for: kfs.first!)
38+
}
39+
if time >= kfs.last!.t {
40+
return ztpRect(for: kfs.last!)
41+
}
3742

38-
if zoom.zoomLevel <= 1.0 {
39-
return CGRect(x: 0, y: 0, width: 1, height: 1)
43+
var lo = 0
44+
var hi = kfs.count - 1
45+
while lo < hi - 1 {
46+
let mid = (lo + hi) / 2
47+
if kfs[mid].t <= time {
48+
lo = mid
49+
} else {
50+
hi = mid
51+
}
4052
}
4153

42-
let visibleW = 1.0 / zoom.zoomLevel
43-
let visibleH = 1.0 / zoom.zoomLevel
54+
let k0 = kfs[lo]
55+
let k1 = kfs[hi]
56+
let span = k1.t - k0.t
57+
guard span > 0 else {
58+
return ztpRect(for: k1)
59+
}
60+
61+
let linearT = (time - k0.t) / span
62+
let t = easeInOut(linearT)
63+
64+
let inv0 = 1.0 / k0.zoomLevel
65+
let inv1 = 1.0 / k1.zoomLevel
66+
let zoom = 1.0 / (inv0 + (inv1 - inv0) * t)
4467

45-
var originX = zoom.centerX - visibleW / 2
46-
var originY = zoom.centerY - visibleH / 2
68+
if zoom <= 1.0 {
69+
return CGRect(x: 0, y: 0, width: 1, height: 1)
70+
}
4771

48-
originX = max(0, min(1 - visibleW, originX))
49-
originY = max(0, min(1 - visibleH, originY))
72+
let cx = k0.centerX + (k1.centerX - k0.centerX) * t
73+
let cy = k0.centerY + (k1.centerY - k0.centerY) * t
74+
let visibleW = 1.0 / zoom
75+
let visibleH = 1.0 / zoom
76+
let originX = cx * (1 - visibleW)
77+
let originY = cy * (1 - visibleH)
5078

5179
return CGRect(x: originX, y: originY, width: visibleW, height: visibleH)
5280
}
@@ -87,54 +115,23 @@ final class ZoomTimeline: @unchecked Sendable {
87115
return empty
88116
}
89117

90-
private func interpolatedZoom(at time: Double, keyframes kfs: [ZoomKeyframe]) -> (zoomLevel: Double, centerX: Double, centerY: Double) {
91-
guard !kfs.isEmpty else {
92-
return (1.0, 0.5, 0.5)
93-
}
94-
95-
if time <= kfs.first!.t {
96-
let k = kfs.first!
97-
return (k.zoomLevel, k.centerX, k.centerY)
98-
}
99-
if time >= kfs.last!.t {
100-
let k = kfs.last!
101-
return (k.zoomLevel, k.centerX, k.centerY)
102-
}
103-
104-
var lo = 0
105-
var hi = kfs.count - 1
106-
while lo < hi - 1 {
107-
let mid = (lo + hi) / 2
108-
if kfs[mid].t <= time {
109-
lo = mid
110-
} else {
111-
hi = mid
112-
}
113-
}
114-
115-
let k0 = kfs[lo]
116-
let k1 = kfs[hi]
117-
let span = k1.t - k0.t
118-
guard span > 0 else {
119-
return (k1.zoomLevel, k1.centerX, k1.centerY)
118+
private func ztpRect(for k: ZoomKeyframe) -> CGRect {
119+
if k.zoomLevel <= 1.0 {
120+
return CGRect(x: 0, y: 0, width: 1, height: 1)
120121
}
121-
122-
let linearT = (time - k0.t) / span
123-
let t = easeInOut(linearT)
124-
125-
let inv0 = 1.0 / k0.zoomLevel
126-
let inv1 = 1.0 / k1.zoomLevel
127-
let zoom = 1.0 / (inv0 + (inv1 - inv0) * t)
128-
let cx = k0.centerX + (k1.centerX - k0.centerX) * t
129-
let cy = k0.centerY + (k1.centerY - k0.centerY) * t
130-
131-
return (zoom, cx, cy)
122+
let visibleW = 1.0 / k.zoomLevel
123+
let visibleH = 1.0 / k.zoomLevel
124+
let originX = k.centerX * (1 - visibleW)
125+
let originY = k.centerY * (1 - visibleH)
126+
return CGRect(x: originX, y: originY, width: visibleW, height: visibleH)
132127
}
133128

134129
static func followCursor(_ rect: CGRect, cursorPosition: CGPoint) -> CGRect {
135130
guard rect.width < 1.0 || rect.height < 1.0 else { return rect }
136-
let originX = max(0, min(1 - rect.width, cursorPosition.x - rect.width / 2))
137-
let originY = max(0, min(1 - rect.height, cursorPosition.y - rect.height / 2))
131+
let cx = max(0, min(1, cursorPosition.x))
132+
let cy = max(0, min(1, cursorPosition.y))
133+
let originX = cx * (1 - rect.width)
134+
let originY = cy * (1 - rect.height)
138135
return CGRect(x: originX, y: originY, width: rect.width, height: rect.height)
139136
}
140137

0 commit comments

Comments
 (0)