mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-19 06:26:55 +00:00
Improve infinite radial animation.
This commit is contained in:
parent
1af2769209
commit
d15b0cdb08
@ -198,7 +198,8 @@ void ProxyRow::updateFields(View &&view) {
|
||||
if (state == State::Connecting || state == State::Checking) {
|
||||
if (!_progress) {
|
||||
_progress = std::make_unique<Ui::InfiniteRadialAnimation>(
|
||||
animation(this, &ProxyRow::step_radial));
|
||||
animation(this, &ProxyRow::step_radial),
|
||||
st::proxyCheckingAnimation);
|
||||
_progress->start();
|
||||
}
|
||||
} else {
|
||||
@ -213,11 +214,8 @@ void ProxyRow::updateFields(View &&view) {
|
||||
void ProxyRow::step_radial(TimeMs ms, bool timer) {
|
||||
if (timer) {
|
||||
update();
|
||||
} else if (_progress) {
|
||||
_progress->update(false, ms);
|
||||
if (!_progress->animating()) {
|
||||
_progress = nullptr;
|
||||
}
|
||||
} else if (_progress && !_progress->animating()) {
|
||||
_progress = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,8 +315,7 @@ void ProxyRow::paintEvent(QPaintEvent *e) {
|
||||
{
|
||||
st::proxyCheckingPosition.x() + statusLeft,
|
||||
st::proxyCheckingPosition.y() + top },
|
||||
width(),
|
||||
st::proxyCheckingAnimation);
|
||||
width());
|
||||
statusLeft += st::proxyCheckingPosition.x()
|
||||
+ st::proxyCheckingAnimation.size.width()
|
||||
+ st::proxyCheckingSkip;
|
||||
|
@ -84,37 +84,37 @@ void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, styl
|
||||
p.setOpacity(o);
|
||||
}
|
||||
|
||||
InfiniteRadialAnimation::InfiniteRadialAnimation(AnimationCallbacks &&callbacks)
|
||||
: _animation(std::move(callbacks)) {
|
||||
InfiniteRadialAnimation::InfiniteRadialAnimation(
|
||||
AnimationCallbacks &&callbacks,
|
||||
const style::InfiniteRadialAnimation &st)
|
||||
: _st(st)
|
||||
, _animation(std::move(callbacks)) {
|
||||
}
|
||||
|
||||
void InfiniteRadialAnimation::start() {
|
||||
_start = _changed = getms();
|
||||
_finished = false;
|
||||
_animation.start();
|
||||
}
|
||||
|
||||
void InfiniteRadialAnimation::update(bool finished, TimeMs ms) {
|
||||
if (_finished != finished) {
|
||||
_finished = finished;
|
||||
_changed = ms;
|
||||
const auto now = getms();
|
||||
if (_workFinished <= now) {
|
||||
_workStarted = now + _st.sineDuration;
|
||||
_workFinished = 0;
|
||||
}
|
||||
|
||||
auto dt = float64(ms - _changed);
|
||||
auto fulldt = float64(ms - _start);
|
||||
_opacity = qMin(fulldt / st::radialDuration, 1.);
|
||||
if (!finished) {
|
||||
} else if (dt >= st::radialDuration) {
|
||||
stop();
|
||||
} else {
|
||||
auto r = dt / st::radialDuration;
|
||||
_opacity *= 1 - r;
|
||||
if (!_animation.animating()) {
|
||||
_animation.start();
|
||||
}
|
||||
}
|
||||
|
||||
void InfiniteRadialAnimation::stop() {
|
||||
_start = _changed = 0;
|
||||
_animation.stop();
|
||||
const auto now = getms();
|
||||
if (!_workFinished) {
|
||||
const auto zero = _workStarted - _st.sineDuration;
|
||||
const auto index = (now - zero + _st.sinePeriod - _st.sineShift)
|
||||
/ _st.sinePeriod;
|
||||
_workFinished = zero
|
||||
+ _st.sineShift
|
||||
+ (index * _st.sinePeriod)
|
||||
+ _st.sineDuration;
|
||||
} else if (_workFinished <= now) {
|
||||
_animation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void InfiniteRadialAnimation::step(TimeMs ms) {
|
||||
@ -124,56 +124,148 @@ void InfiniteRadialAnimation::step(TimeMs ms) {
|
||||
void InfiniteRadialAnimation::draw(
|
||||
Painter &p,
|
||||
QPoint position,
|
||||
int outerWidth,
|
||||
const style::InfiniteRadialAnimation &st) {
|
||||
auto o = p.opacity();
|
||||
p.setOpacity(o * _opacity);
|
||||
int outerWidth) {
|
||||
const auto state = computeState();
|
||||
|
||||
auto pen = st.color->p;
|
||||
auto o = p.opacity();
|
||||
p.setOpacity(o * state.shown);
|
||||
|
||||
auto pen = _st.color->p;
|
||||
auto was = p.pen();
|
||||
pen.setWidth(st.thickness);
|
||||
pen.setWidth(_st.thickness);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
p.setPen(pen);
|
||||
|
||||
const auto time = (getms() - _start);
|
||||
const auto linear = (time * FullArcLength) / st.linearPeriod;
|
||||
const auto frontPeriods = time / st.sinePeriod;
|
||||
const auto frontCurrent = time % st.sinePeriod;
|
||||
const auto frontProgress = anim::sineInOut(
|
||||
st.arcMax - st.arcMin,
|
||||
std::min(frontCurrent, TimeMs(st.sineDuration))
|
||||
/ float64(st.sineDuration));
|
||||
const auto backTime = std::max(time - st.sineShift, 0LL);
|
||||
const auto backPeriods = backTime / st.sinePeriod;
|
||||
const auto backCurrent = backTime % st.sinePeriod;
|
||||
const auto backProgress = anim::sineInOut(
|
||||
st.arcMax - st.arcMin,
|
||||
std::min(backCurrent, TimeMs(st.sineDuration))
|
||||
/ float64(st.sineDuration));
|
||||
const auto front = linear + std::round((st.arcMin + frontProgress + frontPeriods * (st.arcMax - st.arcMin)) * FullArcLength);
|
||||
const auto from = linear + std::round((backProgress + backPeriods * (st.arcMax - st.arcMin)) * FullArcLength);
|
||||
const auto len = (front - from);
|
||||
|
||||
//if (rtl()) {
|
||||
// from = QuarterArcLength - (from - QuarterArcLength) - len;
|
||||
// if (from < 0) from += FullArcLength;
|
||||
//}
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawArc(
|
||||
rtlrect(
|
||||
position.x(),
|
||||
position.y(),
|
||||
st.size.width(),
|
||||
st.size.height(),
|
||||
_st.size.width(),
|
||||
_st.size.height(),
|
||||
outerWidth),
|
||||
from,
|
||||
len);
|
||||
state.arcFrom,
|
||||
state.arcLength);
|
||||
}
|
||||
|
||||
p.setPen(was);
|
||||
p.setOpacity(o);
|
||||
}
|
||||
|
||||
auto InfiniteRadialAnimation::computeState() -> State {
|
||||
const auto now = getms();
|
||||
const auto linear = int(((now * FullArcLength) / _st.linearPeriod)
|
||||
% FullArcLength);
|
||||
if (!_workStarted || (_workFinished && _workFinished <= now)) {
|
||||
const auto shown = 0.;
|
||||
_animation.stop();
|
||||
return {
|
||||
shown,
|
||||
linear,
|
||||
FullArcLength };
|
||||
}
|
||||
const auto min = int(std::round(FullArcLength * _st.arcMin));
|
||||
const auto max = int(std::round(FullArcLength * _st.arcMax));
|
||||
if (now <= _workStarted) {
|
||||
// zero .. _workStarted
|
||||
const auto zero = _workStarted - _st.sineDuration;
|
||||
const auto shown = (now - zero) / float64(_st.sineDuration);
|
||||
const auto length = anim::interpolate(
|
||||
FullArcLength,
|
||||
min,
|
||||
anim::sineInOut(1., snap(shown, 0., 1.)));
|
||||
return {
|
||||
shown,
|
||||
linear + (FullArcLength - length),
|
||||
length };
|
||||
} else if (!_workFinished || now <= _workFinished - _st.sineDuration) {
|
||||
// _workStared .. _workFinished - _st.sineDuration
|
||||
const auto shown = 1.;
|
||||
const auto cycles = (now - _workStarted) / _st.sinePeriod;
|
||||
const auto relative = (now - _workStarted) % _st.sinePeriod;
|
||||
const auto smallDuration = _st.sineShift - _st.sineDuration;
|
||||
const auto largeDuration = _st.sinePeriod
|
||||
- _st.sineShift
|
||||
- _st.sineDuration;
|
||||
const auto basic = int((linear
|
||||
+ (FullArcLength - min)
|
||||
+ cycles * (max - min)) % FullArcLength);
|
||||
if (relative <= smallDuration) {
|
||||
// localZero .. growStart
|
||||
return {
|
||||
shown,
|
||||
basic,
|
||||
min };
|
||||
} else if (relative <= smallDuration + _st.sineDuration) {
|
||||
// growStart .. growEnd
|
||||
const auto growLinear = (relative - smallDuration) /
|
||||
float64(_st.sineDuration);
|
||||
const auto growProgress = anim::sineInOut(1., growLinear);
|
||||
return {
|
||||
shown,
|
||||
basic,
|
||||
anim::interpolate(min, max, growProgress) };
|
||||
} else if (relative <= _st.sinePeriod - _st.sineDuration) {
|
||||
// growEnd .. shrinkStart
|
||||
return {
|
||||
shown,
|
||||
basic,
|
||||
max };
|
||||
} else {
|
||||
// shrinkStart .. shrinkEnd
|
||||
const auto shrinkLinear = (relative
|
||||
- (_st.sinePeriod - _st.sineDuration))
|
||||
/ float64(_st.sineDuration);
|
||||
const auto shrinkProgress = anim::sineInOut(1., shrinkLinear);
|
||||
const auto shrink = anim::interpolate(
|
||||
0,
|
||||
max - min,
|
||||
shrinkProgress);
|
||||
return {
|
||||
shown,
|
||||
basic + shrink,
|
||||
max - shrink }; // interpolate(max, min, shrinkProgress)
|
||||
}
|
||||
} else {
|
||||
// _workFinished - _st.sineDuration .. _workFinished
|
||||
const auto hidden = (now - (_workFinished - _st.sineDuration))
|
||||
/ float64(_st.sineDuration);
|
||||
const auto cycles = (_workFinished - _workStarted) / _st.sinePeriod;
|
||||
const auto basic = int((linear
|
||||
+ (FullArcLength - min)
|
||||
+ cycles * (max - min)) % FullArcLength);
|
||||
const auto length = anim::interpolate(
|
||||
min,
|
||||
FullArcLength,
|
||||
anim::sineInOut(1., snap(hidden, 0., 1.)));
|
||||
return {
|
||||
1. - hidden,
|
||||
basic,
|
||||
length };
|
||||
}
|
||||
//const auto frontPeriods = time / st.sinePeriod;
|
||||
//const auto frontCurrent = time % st.sinePeriod;
|
||||
//const auto frontProgress = anim::sineInOut(
|
||||
// st.arcMax - st.arcMin,
|
||||
// std::min(frontCurrent, TimeMs(st.sineDuration))
|
||||
// / float64(st.sineDuration));
|
||||
//const auto backTime = std::max(time - st.sineShift, 0LL);
|
||||
//const auto backPeriods = backTime / st.sinePeriod;
|
||||
//const auto backCurrent = backTime % st.sinePeriod;
|
||||
//const auto backProgress = anim::sineInOut(
|
||||
// st.arcMax - st.arcMin,
|
||||
// std::min(backCurrent, TimeMs(st.sineDuration))
|
||||
// / float64(st.sineDuration));
|
||||
//const auto front = linear + std::round((st.arcMin + frontProgress + frontPeriods * (st.arcMax - st.arcMin)) * FullArcLength);
|
||||
//const auto from = linear + std::round((backProgress + backPeriods * (st.arcMax - st.arcMin)) * FullArcLength);
|
||||
//const auto length = (front - from);
|
||||
|
||||
//return {
|
||||
// _opacity,
|
||||
// from,
|
||||
// length
|
||||
//};
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
@ -48,17 +48,20 @@ private:
|
||||
|
||||
class InfiniteRadialAnimation {
|
||||
public:
|
||||
InfiniteRadialAnimation(AnimationCallbacks &&callbacks);
|
||||
struct State {
|
||||
float64 shown = 0.;
|
||||
int arcFrom = 0;
|
||||
int arcLength = FullArcLength;
|
||||
};
|
||||
InfiniteRadialAnimation(
|
||||
AnimationCallbacks &&callbacks,
|
||||
const style::InfiniteRadialAnimation &st);
|
||||
|
||||
float64 opacity() const {
|
||||
return _opacity;
|
||||
}
|
||||
bool animating() const {
|
||||
return _animation.animating();
|
||||
}
|
||||
|
||||
void start();
|
||||
void update(bool finished, TimeMs ms);
|
||||
void stop();
|
||||
|
||||
void step(TimeMs ms);
|
||||
@ -69,14 +72,15 @@ public:
|
||||
void draw(
|
||||
Painter &p,
|
||||
QPoint position,
|
||||
int outerWidth,
|
||||
const style::InfiniteRadialAnimation &st);
|
||||
int outerWidth);
|
||||
|
||||
State computeState();
|
||||
|
||||
private:
|
||||
float64 _opacity = 0.;
|
||||
TimeMs _start = 0;
|
||||
TimeMs _changed = 0;
|
||||
bool _finished = false;
|
||||
const style::InfiniteRadialAnimation &_st;
|
||||
float64 _shown = 0.;
|
||||
TimeMs _workStarted = 0;
|
||||
TimeMs _workFinished = 0;
|
||||
BasicAnimation _animation;
|
||||
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_history.h"
|
||||
#include "dialogs/dialogs_layout.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_session.h"
|
||||
@ -163,8 +164,7 @@ void HistoryDownButton::setUnreadCount(int unreadCount) {
|
||||
|
||||
EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st)
|
||||
: RippleButton(parent, st.ripple)
|
||||
, _st(st)
|
||||
, _a_loading(animation(this, &EmojiButton::step_loading)) {
|
||||
, _st(st) {
|
||||
resize(_st.width, _st.height);
|
||||
setCursor(style::cur_pointer);
|
||||
}
|
||||
@ -177,8 +177,10 @@ void EmojiButton::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(e->rect(), st::historyComposeAreaBg);
|
||||
paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms, _rippleOverride ? &(*_rippleOverride)->c : nullptr);
|
||||
|
||||
auto loading = a_loading.current(ms, _loading ? 1 : 0);
|
||||
p.setOpacity(1 - loading);
|
||||
const auto loadingState = _loading
|
||||
? _loading->computeState()
|
||||
: Ui::InfiniteRadialAnimation::State{ 0., 0, FullArcLength };
|
||||
p.setOpacity(1. - loadingState.shown);
|
||||
|
||||
auto over = isOver();
|
||||
auto icon = _iconOverride ? _iconOverride : &(over ? _st.iconOver : _st.icon);
|
||||
@ -193,25 +195,23 @@ void EmojiButton::paintEvent(QPaintEvent *e) {
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
QRect inner(QPoint((width() - st::historyEmojiCircle.width()) / 2, st::historyEmojiCircleTop), st::historyEmojiCircle);
|
||||
if (loading > 0) {
|
||||
int32 full = FullArcLength;
|
||||
int32 start = qRound(full * float64(ms % st::historyEmojiCirclePeriod) / st::historyEmojiCirclePeriod), part = qRound(loading * full / st::historyEmojiCirclePart);
|
||||
p.drawArc(inner, start, full - part);
|
||||
if (loadingState.arcLength < FullArcLength) {
|
||||
p.drawArc(inner, loadingState.arcFrom, loadingState.arcLength);
|
||||
} else {
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiButton::setLoading(bool loading) {
|
||||
if (_loading != loading) {
|
||||
_loading = loading;
|
||||
auto from = loading ? 0. : 1., to = loading ? 1. : 0.;
|
||||
a_loading.start([this] { update(); }, from, to, st::historyEmojiCircleDuration);
|
||||
if (loading) {
|
||||
_a_loading.start();
|
||||
} else {
|
||||
_a_loading.stop();
|
||||
}
|
||||
if (loading && !_loading) {
|
||||
_loading = std::make_unique<Ui::InfiniteRadialAnimation>(
|
||||
animation(this, &EmojiButton::step_loading),
|
||||
st::defaultInfiniteRadialAnimation);
|
||||
}
|
||||
if (loading) {
|
||||
_loading->start();
|
||||
} else {
|
||||
_loading->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Ui {
|
||||
class InfiniteRadialAnimation;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
class Feed;
|
||||
} // namespace Data
|
||||
@ -69,9 +73,7 @@ private:
|
||||
|
||||
const style::IconButton &_st;
|
||||
|
||||
bool _loading = false;
|
||||
Animation a_loading;
|
||||
BasicAnimation _a_loading;
|
||||
std::unique_ptr<Ui::InfiniteRadialAnimation> _loading;
|
||||
|
||||
const style::icon *_iconOverride = nullptr;
|
||||
const style::color *_colorOverride = nullptr;
|
||||
|
Loading…
Reference in New Issue
Block a user