52 #include <math.h> |
52 #include <math.h> |
53 |
53 |
54 QT_BEGIN_NAMESPACE |
54 QT_BEGIN_NAMESPACE |
55 |
55 |
56 |
56 |
57 |
57 class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate |
58 class QDeclarativeSpringAnimationPrivate : public QDeclarativeAbstractAnimationPrivate |
|
59 { |
58 { |
60 Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) |
59 Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) |
61 public: |
60 public: |
62 QDeclarativeSpringAnimationPrivate() |
61 |
63 : currentValue(0), to(0), from(0), maxVelocity(0), lastTime(0) |
62 |
64 , mass(1.0), spring(0.), damping(0.), velocity(0), epsilon(0.01) |
63 struct SpringAnimation { |
65 , modulus(0.0), useMass(false), haveModulus(false), enabled(true) |
64 SpringAnimation() |
66 , fromDefined(false), toDefined(false) |
65 : currentValue(0), to(0), velocity(0){} |
67 , mode(Track), clock(this) {} |
66 qreal currentValue; |
68 |
67 qreal to; |
69 qreal currentValue; |
68 qreal velocity; |
70 qreal to; |
69 }; |
71 qreal from; |
70 QHash<QDeclarativeProperty, SpringAnimation> activeAnimations; |
|
71 |
72 qreal maxVelocity; |
72 qreal maxVelocity; |
73 qreal velocityms; |
73 qreal velocityms; |
74 int lastTime; |
74 int lastTime; |
75 qreal mass; |
75 qreal mass; |
76 qreal spring; |
76 qreal spring; |
77 qreal damping; |
77 qreal damping; |
78 qreal velocity; |
|
79 qreal epsilon; |
78 qreal epsilon; |
80 qreal modulus; |
79 qreal modulus; |
81 |
80 |
82 bool useMass : 1; |
81 bool useMass : 1; |
83 bool haveModulus : 1; |
82 bool haveModulus : 1; |
84 bool enabled : 1; |
|
85 bool fromDefined : 1; |
|
86 bool toDefined : 1; |
|
87 |
83 |
88 enum Mode { |
84 enum Mode { |
89 Track, |
85 Track, |
90 Velocity, |
86 Velocity, |
91 Spring |
87 Spring |
92 }; |
88 }; |
93 Mode mode; |
89 Mode mode; |
94 |
90 |
95 void tick(int); |
91 QDeclarativeSpringAnimationPrivate() |
|
92 : maxVelocity(0), velocityms(0), lastTime(0) |
|
93 , mass(1.0), spring(0.), damping(0.), epsilon(0.01) |
|
94 , modulus(0.0), useMass(false), haveModulus(false) |
|
95 , mode(Track), clock(0) |
|
96 { } |
|
97 |
|
98 void tick(int time); |
|
99 bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed); |
96 void updateMode(); |
100 void updateMode(); |
97 |
101 |
98 QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> clock; |
102 typedef QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> Clock; |
|
103 Clock *clock; |
99 }; |
104 }; |
100 |
105 |
101 void QDeclarativeSpringAnimationPrivate::tick(int time) |
106 void QDeclarativeSpringAnimationPrivate::tick(int time) |
102 { |
107 { |
103 if (mode == Track) { |
108 if (mode == Track) { |
104 clock.stop(); |
109 clock->stop(); |
105 return; |
110 return; |
106 } |
111 } |
107 |
|
108 int elapsed = time - lastTime; |
112 int elapsed = time - lastTime; |
109 if (!elapsed) |
113 if (!elapsed) |
110 return; |
114 return; |
111 qreal srcVal = to; |
115 |
112 |
|
113 bool stop = false; |
|
114 |
|
115 if (haveModulus) { |
|
116 currentValue = fmod(currentValue, modulus); |
|
117 srcVal = fmod(srcVal, modulus); |
|
118 } |
|
119 if (mode == Spring) { |
116 if (mode == Spring) { |
120 if (elapsed < 16) // capped at 62fps. |
117 if (elapsed < 16) // capped at 62fps. |
121 return; |
118 return; |
|
119 int count = elapsed / 16; |
|
120 lastTime = time - (elapsed - count * 16); |
|
121 } else { |
|
122 lastTime = time; |
|
123 } |
|
124 |
|
125 QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations); |
|
126 while (it.hasNext()) { |
|
127 it.next(); |
|
128 if (animate(it.key(), it.value(), elapsed)) |
|
129 it.remove(); |
|
130 } |
|
131 |
|
132 if (activeAnimations.isEmpty()) |
|
133 clock->stop(); |
|
134 } |
|
135 |
|
136 bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed) |
|
137 { |
|
138 |
|
139 qreal srcVal = animation.to; |
|
140 |
|
141 bool stop = false; |
|
142 |
|
143 if (haveModulus) { |
|
144 animation.currentValue = fmod(animation.currentValue, modulus); |
|
145 srcVal = fmod(srcVal, modulus); |
|
146 } |
|
147 if (mode == Spring) { |
122 // Real men solve the spring DEs using RK4. |
148 // Real men solve the spring DEs using RK4. |
123 // We'll do something much simpler which gives a result that looks fine. |
149 // We'll do something much simpler which gives a result that looks fine. |
124 int count = elapsed / 16; |
150 int count = elapsed / 16; |
125 for (int i = 0; i < count; ++i) { |
151 for (int i = 0; i < count; ++i) { |
126 qreal diff = srcVal - currentValue; |
152 qreal diff = srcVal - animation.currentValue; |
127 if (haveModulus && qAbs(diff) > modulus / 2) { |
153 if (haveModulus && qAbs(diff) > modulus / 2) { |
128 if (diff < 0) |
154 if (diff < 0) |
129 diff += modulus; |
155 diff += modulus; |
130 else |
156 else |
131 diff -= modulus; |
157 diff -= modulus; |
132 } |
158 } |
133 if (useMass) |
159 if (useMass) |
134 velocity = velocity + (spring * diff - damping * velocity) / mass; |
160 animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass; |
135 else |
161 else |
136 velocity = velocity + spring * diff - damping * velocity; |
162 animation.velocity = animation.velocity + spring * diff - damping * animation.velocity; |
137 if (maxVelocity > 0.) { |
163 if (maxVelocity > 0.) { |
138 // limit velocity |
164 // limit velocity |
139 if (velocity > maxVelocity) |
165 if (animation.velocity > maxVelocity) |
140 velocity = maxVelocity; |
166 animation.velocity = maxVelocity; |
141 else if (velocity < -maxVelocity) |
167 else if (animation.velocity < -maxVelocity) |
142 velocity = -maxVelocity; |
168 animation.velocity = -maxVelocity; |
143 } |
169 } |
144 currentValue += velocity * 16.0 / 1000.0; |
170 animation.currentValue += animation.velocity * 16.0 / 1000.0; |
145 if (haveModulus) { |
171 if (haveModulus) { |
146 currentValue = fmod(currentValue, modulus); |
172 animation.currentValue = fmod(animation.currentValue, modulus); |
147 if (currentValue < 0.0) |
173 if (animation.currentValue < 0.0) |
148 currentValue += modulus; |
174 animation.currentValue += modulus; |
149 } |
175 } |
150 } |
176 } |
151 if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) { |
177 if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) { |
152 velocity = 0.0; |
178 animation.velocity = 0.0; |
153 currentValue = srcVal; |
179 animation.currentValue = srcVal; |
154 stop = true; |
180 stop = true; |
155 } |
181 } |
156 lastTime = time - (elapsed - count * 16); |
|
157 } else { |
182 } else { |
158 qreal moveBy = elapsed * velocityms; |
183 qreal moveBy = elapsed * velocityms; |
159 qreal diff = srcVal - currentValue; |
184 qreal diff = srcVal - animation.currentValue; |
160 if (haveModulus && qAbs(diff) > modulus / 2) { |
185 if (haveModulus && qAbs(diff) > modulus / 2) { |
161 if (diff < 0) |
186 if (diff < 0) |
162 diff += modulus; |
187 diff += modulus; |
163 else |
188 else |
164 diff -= modulus; |
189 diff -= modulus; |
165 } |
190 } |
166 if (diff > 0) { |
191 if (diff > 0) { |
167 currentValue += moveBy; |
192 animation.currentValue += moveBy; |
168 if (haveModulus) |
193 if (haveModulus) |
169 currentValue = fmod(currentValue, modulus); |
194 animation.currentValue = fmod(animation.currentValue, modulus); |
170 if (currentValue > to) { |
195 if (animation.currentValue > animation.to) { |
171 currentValue = to; |
196 animation.currentValue = animation.to; |
172 stop = true; |
197 stop = true; |
173 } |
198 } |
174 } else { |
199 } else { |
175 currentValue -= moveBy; |
200 animation.currentValue -= moveBy; |
176 if (haveModulus && currentValue < 0.0) |
201 if (haveModulus && animation.currentValue < 0.0) |
177 currentValue = fmod(currentValue, modulus) + modulus; |
202 animation.currentValue = fmod(animation.currentValue, modulus) + modulus; |
178 if (currentValue < to) { |
203 if (animation.currentValue < animation.to) { |
179 currentValue = to; |
204 animation.currentValue = animation.to; |
180 stop = true; |
205 stop = true; |
181 } |
206 } |
182 } |
207 } |
183 lastTime = time; |
208 } |
184 } |
209 |
185 |
210 qreal old_to = animation.to; |
186 qreal old_to = to; |
211 |
187 |
212 QDeclarativePropertyPrivate::write(property, animation.currentValue, |
188 QDeclarativePropertyPrivate::write(defaultProperty, currentValue, |
|
189 QDeclarativePropertyPrivate::BypassInterceptor | |
213 QDeclarativePropertyPrivate::BypassInterceptor | |
190 QDeclarativePropertyPrivate::DontRemoveBinding); |
214 QDeclarativePropertyPrivate::DontRemoveBinding); |
191 |
215 |
192 if (stop && old_to == to) // do not stop if we got restarted |
216 return (stop && old_to == animation.to); // do not stop if we got restarted |
193 clock.stop(); |
|
194 } |
217 } |
195 |
218 |
196 void QDeclarativeSpringAnimationPrivate::updateMode() |
219 void QDeclarativeSpringAnimationPrivate::updateMode() |
197 { |
220 { |
198 if (spring == 0. && maxVelocity == 0.) |
221 if (spring == 0. && maxVelocity == 0.) |
221 |
246 |
222 \snippet doc/src/snippets/declarative/springanimation.qml 0 |
247 \snippet doc/src/snippets/declarative/springanimation.qml 0 |
223 |
248 |
224 Like any other animation element, a SpringAnimation can be applied in a |
249 Like any other animation element, a SpringAnimation can be applied in a |
225 number of ways, including transitions, behaviors and property value |
250 number of ways, including transitions, behaviors and property value |
226 sources. The \l PropertyAnimation documentation shows a variety of methods |
251 sources. The \l {QML Animation} documentation shows a variety of methods |
227 for creating animations. |
252 for creating animations. |
228 |
253 |
229 \sa SmoothedAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} |
254 \sa SmoothedAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} |
230 */ |
255 */ |
231 |
256 |
232 QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) |
257 QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) |
233 : QDeclarativeAbstractAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) |
258 : QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) |
234 { |
259 { |
|
260 Q_D(QDeclarativeSpringAnimation); |
|
261 d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this); |
235 } |
262 } |
236 |
263 |
237 QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() |
264 QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() |
238 { |
265 { |
239 } |
266 } |
240 |
|
241 void QDeclarativeSpringAnimation::setTarget(const QDeclarativeProperty &property) |
|
242 { |
|
243 Q_D(QDeclarativeSpringAnimation); |
|
244 d->defaultProperty = property; |
|
245 d->currentValue = property.read().toReal(); |
|
246 if (!d->avoidPropertyValueSourceStart) { |
|
247 setRunning(true); |
|
248 } |
|
249 } |
|
250 |
|
251 qreal QDeclarativeSpringAnimation::to() const |
|
252 { |
|
253 Q_D(const QDeclarativeSpringAnimation); |
|
254 return d->toDefined ? d->to : 0; |
|
255 } |
|
256 |
|
257 /*! |
|
258 \qmlproperty real SpringAnimation::to |
|
259 |
|
260 This property holds the value at which the animation will end. |
|
261 |
|
262 If not set, the animation will continue until it reaches the |
|
263 value that is being tracked. |
|
264 */ |
|
265 |
|
266 void QDeclarativeSpringAnimation::setTo(qreal value) |
|
267 { |
|
268 Q_D(QDeclarativeSpringAnimation); |
|
269 if (d->to == value) |
|
270 return; |
|
271 |
|
272 d->to = value; |
|
273 d->toDefined = true; |
|
274 d->lastTime = 0; |
|
275 emit toChanged(value); |
|
276 } |
|
277 |
|
278 qreal QDeclarativeSpringAnimation::from() const |
|
279 { |
|
280 Q_D(const QDeclarativeSpringAnimation); |
|
281 return d->fromDefined ? d->from : 0; |
|
282 } |
|
283 |
|
284 /*! |
|
285 \qmlproperty real SpringAnimation::from |
|
286 |
|
287 This property holds the value from which the animation will begin. |
|
288 |
|
289 If not set, the animation will start whenever the tracked value has |
|
290 changed, regardless of its value. |
|
291 */ |
|
292 |
|
293 void QDeclarativeSpringAnimation::setFrom(qreal value) |
|
294 { |
|
295 Q_D(QDeclarativeSpringAnimation); |
|
296 if (d->from == value) |
|
297 return; |
|
298 |
|
299 d->currentValue = d->from = value; |
|
300 d->fromDefined = true; |
|
301 d->lastTime = 0; |
|
302 emit fromChanged(value); |
|
303 } |
|
304 |
|
305 |
267 |
306 /*! |
268 /*! |
307 \qmlproperty real SpringAnimation::velocity |
269 \qmlproperty real SpringAnimation::velocity |
308 |
270 |
309 This property holds the maximum velocity allowed when tracking the source. |
271 This property holds the maximum velocity allowed when tracking the source. |