-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathAnimatedCameraProxy.ts
More file actions
227 lines (207 loc) · 5.53 KB
/
AnimatedCameraProxy.ts
File metadata and controls
227 lines (207 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/**
* Copyright (c) 2017 Alibaba Group Holding Limited
*/
import { CameraProxy, CameraProxyProps } from './CameraProxy'
import { States, EasingFunc, GeographicStates, CartesianStates } from './interface'
import { lerp, easeSin01, clamp } from './util'
import { Timeline, Track } from 'ani-timeline'
export interface CameraAnimatorProps extends CameraProxyProps {
timeline: Timeline
}
export class AnimatedCameraProxy extends CameraProxy {
readonly config: CameraAnimatorProps
public timeline: Timeline // 缓动需要
/**
* 缓动锁定,避免同时出发多个缓动动画造成冲突
*/
public easingLock: boolean
constructor(props: CameraAnimatorProps) {
props = { ...props }
super(props)
this.timeline = props.timeline
}
/**
* 状态变化
* @param selfBase 是否以笛卡尔状态为准,取同步地图状态
* @param states 要修改的状态
* @param key
* @param value
* @param [duration=1000] 过程时长,如果为0则立刻执行立刻返回,>0 则插值
* @param [easeF=easeSin01] 缓动函数
* @return stop {function} 立刻停止缓动动画
*/
protected _setStateEase(
selfBase: boolean,
states: States,
key: string,
value: any,
duration = 1000,
easeF = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
): () => void {
if (this.easingLock) {
console.warn('另一个动画正在控制相机,请主动关闭,以免控制权争夺')
}
// 差值缓动
const oldValue = states[key]
const track = this.timeline.addTrack({
id: '相机缓动',
startTime: this.timeline.currentTime,
duration,
onStart: () => {
this.easingLock = true
},
onUpdate: (t, p) => {
states[key] = lerp(oldValue, value, p)
this.update(selfBase)
},
onEnd: () => {
this.easingLock = false
if (onEnd) {
onEnd()
}
},
easing: easeF || easeSin01,
})
// 让用户可以在外部控制立刻停掉这个动画
return () => {
track.alive = false // 立刻停掉动画
track.onEnd(this.timeline.currentTime) // 手动收尾
}
}
/**
* 状态控制元语
* @param v 目标状态
* @param [duration=1000] 缓动时间
* @param [easeF=easeSin01] 缓动函数 0~1
*/
public setCenterEase(
v: number[],
duration = 1000,
easeF: EasingFunc = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
) {
v = this.transInputVec(v)
return this._setStateEase(false, this.geoStates, 'center', v, duration, easeF, onStart, onEnd)
}
public setZoomEase(
v: number,
duration = 1000,
easeF: EasingFunc = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
) {
return this._setStateEase(false, this.geoStates, 'zoom', v, duration, easeF, onStart, onEnd)
}
public setPitchEase(
v: number,
duration = 1000,
easeF: EasingFunc = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
) {
return this._setStateEase(false, this.geoStates, 'pitch', v, duration, easeF, onStart, onEnd)
}
public setRotationEase(
v: number,
duration = 1000,
easeF: EasingFunc = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
) {
return this._setStateEase(false, this.geoStates, 'rotation', v, duration, easeF, onStart, onEnd)
}
/**
* 地图飞行效果的机位动画
* @todo 移动到 cameraman 类中
*/
public setGeographicStatesEase(
states: GeographicStates,
duration = 1000,
easeF = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
) {
const prevStates = this.getGeographicStates()
const prevDistance = this.decStates.distance
const targetDistance = this._getDistance(states.zoom, this.canvasHeight, this.fov, this.ratio)
const track = this.timeline.addTrack({
id: '相机缓动',
startTime: this.timeline.currentTime,
duration,
onStart: () => {
this.easingLock = true
if (onStart) {
onStart()
}
},
onUpdate: (t, p) => {
// zoom 按照屏幕像素尺寸运动
const middleZoom = lerp(prevStates.zoom, states.zoom, p)
// 换算回距离
const middleDistance = this._getDistance(
middleZoom,
this.canvasHeight,
this.fov,
this.ratio
)
// 换算成percent
const distancePercent = (middleDistance - prevDistance) / (targetDistance - prevDistance)
const p2 =
prevStates.zoom === states.zoom || targetDistance === prevDistance ? p : distancePercent
const middleStates: GeographicStates = {
center: lerp(prevStates.center, states.center, p2),
pitch: lerp(prevStates.pitch, states.pitch, p),
rotation: lerp(prevStates.rotation, states.rotation, p),
zoom: lerp(prevStates.zoom, states.zoom, p),
}
this.setGeographicStates(middleStates)
},
onEnd: () => {
this.easingLock = false
if (onEnd) {
onEnd()
}
},
easing: easeF || easeSin01,
})
// 让用户可以在外部控制立刻停掉这个动画
return () => {
track.alive = false // 立刻停掉动画
track.onEnd(this.timeline.currentTime) // 手动收尾
}
}
/**
* 缓动切换相机机位
* @param statesCode 机位状态
* @param duration 缓动时长
* @param easeF 缓动函数
* @param onStart
* @param onEnd
* @return stop {function} 立刻停止缓动动画
*/
public setStatesCodeEase(
statesCode: string,
duration = 1000,
easeF = easeSin01,
onStart: () => void = null,
onEnd: () => void = null
): () => void {
return this.setGeographicStatesEase(
this.codeToStates(statesCode),
duration,
easeF,
onStart,
onEnd
)
}
/**
* 清理
*/
public dispose() {
this.update = (selfBase) => {}
super.dispose()
}
}