Improved y-axis animation again to look much better.

This commit is contained in:
23rd 2023-07-15 01:56:47 +03:00 committed by John Preston
parent b1ed8cd1b1
commit c8e95f7297
2 changed files with 74 additions and 64 deletions

View file

@ -27,7 +27,7 @@ namespace Statistic {
namespace { namespace {
constexpr auto kHeightLimitsUpdateTimeout = crl::time(320); constexpr auto kHeightLimitsUpdateTimeout = crl::time(320);
constexpr auto kExpandingDelay = crl::time(100); constexpr auto kExpandingDelay = crl::time(1);
inline float64 InterpolationRatio(float64 from, float64 to, float64 result) { inline float64 InterpolationRatio(float64 from, float64 to, float64 result) {
return (result - from) / (to - from); return (result - from) / (to - from);
@ -516,7 +516,11 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
maxValueFull = std::max(l.maxValue, maxValueFull); maxValueFull = std::max(l.maxValue, maxValueFull);
minValueFull = std::min(l.minValue, minValueFull); minValueFull = std::min(l.minValue, minValueFull);
} }
_previousFullHeightLimits = _finalHeightLimits;
_finalHeightLimits = { float64(minValue), float64(maxValue) }; _finalHeightLimits = { float64(minValue), float64(maxValue) };
if (!_previousFullHeightLimits.max) {
_previousFullHeightLimits = _finalHeightLimits;
}
if (!chartLinesViewContext.isFinished()) { if (!chartLinesViewContext.isFinished()) {
_animationValueFooterHeightMin = anim::value( _animationValueFooterHeightMin = anim::value(
_animationValueFooterHeightMin.current(), _animationValueFooterHeightMin.current(),
@ -539,30 +543,46 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
_finalHeightLimits.max); _finalHeightLimits.max);
{ {
const auto currentDelta = _animationValueHeightMax.current() const auto previousDelta = _previousFullHeightLimits.max
- _animationValueHeightMin.current(); - _previousFullHeightLimits.min;
auto k = currentDelta auto k = previousDelta
/ float64(_finalHeightLimits.max - _finalHeightLimits.min); / float64(_finalHeightLimits.max - _finalHeightLimits.min);
if (k > 1.) { if (k > 1.) {
k = 1. / k; k = 1. / k;
} }
constexpr auto kDtHeightSpeed1 = 0.03 / 2; constexpr auto kDtHeightSpeed1 = 0.03 * 2;
constexpr auto kDtHeightSpeed2 = 0.03 / 2; constexpr auto kDtHeightSpeed2 = 0.03 * 2;
constexpr auto kDtHeightSpeed3 = 0.045 / 2; constexpr auto kDtHeightSpeed3 = 0.045 * 2;
constexpr auto kDtHeightSpeedFilter = kDtHeightSpeed1 / 1.2;
constexpr auto kDtHeightSpeedThreshold1 = 0.7; constexpr auto kDtHeightSpeedThreshold1 = 0.7;
constexpr auto kDtHeightSpeedThreshold2 = 0.1; constexpr auto kDtHeightSpeedThreshold2 = 0.1;
constexpr auto kDtHeightInstantThreshold = 0.97; constexpr auto kDtHeightInstantThreshold = 0.97;
_dtHeightSpeed = (k > kDtHeightSpeedThreshold1) if (k < 1.) {
auto &alpha = _animationValueHeightAlpha;
alpha = anim::value(
(alpha.current() == alpha.to()) ? 0. : alpha.current(),
1.);
_dtHeight.currentAlpha = 0.;
_addHorizontalLineRequests.fire({});
}
_dtHeight.speed = (!chartLinesViewContext.isFinished())
? kDtHeightSpeedFilter
: (k > kDtHeightSpeedThreshold1)
? kDtHeightSpeed1 ? kDtHeightSpeed1
: (k < kDtHeightSpeedThreshold2) : (k < kDtHeightSpeedThreshold2)
? kDtHeightSpeed2 ? kDtHeightSpeed2
: kDtHeightSpeed3; : kDtHeightSpeed3;
if (k < kDtHeightInstantThreshold) { if (k < kDtHeightInstantThreshold) {
_dtCurrent = { 0., 0. }; _dtHeight.current = { 0., 0. };
} }
} }
} }
auto ChartWidget::ChartAnimationController::addHorizontalLineRequests() const
-> rpl::producer<> {
return _addHorizontalLineRequests.events();
}
void ChartWidget::ChartAnimationController::start() { void ChartWidget::ChartAnimationController::start() {
if (!_animation.animating()) { if (!_animation.animating()) {
_animation.start(); _animation.start();
@ -577,15 +597,10 @@ void ChartWidget::ChartAnimationController::finish() {
_animationValueHeightMax.finish(); _animationValueHeightMax.finish();
_animationValueFooterHeightMin.finish(); _animationValueFooterHeightMin.finish();
_animationValueFooterHeightMax.finish(); _animationValueFooterHeightMax.finish();
_animValueYAlpha.finish(); _animationValueHeightAlpha.finish();
_benchmark = {}; _benchmark = {};
} }
void ChartWidget::ChartAnimationController::resetAlpha() {
_alphaAnimationStartedAt = 0;
_animValueYAlpha = anim::value(0., 1.);
}
void ChartWidget::ChartAnimationController::restartBottomLineAlpha() { void ChartWidget::ChartAnimationController::restartBottomLineAlpha() {
_bottomLineAlphaAnimationStartedAt = crl::now(); _bottomLineAlphaAnimationStartedAt = crl::now();
_animValueBottomLineAlpha = anim::value(0., 1.); _animValueBottomLineAlpha = anim::value(0., 1.);
@ -603,15 +618,6 @@ void ChartWidget::ChartAnimationController::tick(
constexpr auto kXExpandingDuration = 200.; constexpr auto kXExpandingDuration = 200.;
constexpr auto kAlphaExpandingDuration = 200.; constexpr auto kAlphaExpandingDuration = 200.;
if (!_heightAnimationStarted
&& ((now - _lastUserInteracted) >= kExpandingDelay)) {
_heightAnimationStarts.fire({});
_heightAnimationStarted = true;
}
if (!_alphaAnimationStartedAt) {
_alphaAnimationStartedAt = now;
}
{ {
constexpr auto kIdealFPS = float64(60); constexpr auto kIdealFPS = float64(60);
const auto currentFPS = _benchmark.lastTickedAt const auto currentFPS = _benchmark.lastTickedAt
@ -627,16 +633,15 @@ void ChartWidget::ChartAnimationController::tick(
const auto k = (kIdealFPS / currentFPS) const auto k = (kIdealFPS / currentFPS)
// Speed up to reduce ugly frames count. // Speed up to reduce ugly frames count.
* (_benchmark.lastFPSSlow ? 2. : 1.); * (_benchmark.lastFPSSlow ? 2. : 1.);
_dtCurrent.min = std::min(_dtCurrent.min + _dtHeightSpeed * k, 1.); const auto speed = _dtHeight.speed * k;
_dtCurrent.max = std::min(_dtCurrent.max + _dtHeightSpeed * k, 1.); _dtHeight.current.min = std::min(_dtHeight.current.min + speed, 1.);
_dtHeight.current.max = std::min(_dtHeight.current.max + speed, 1.);
_dtHeight.currentAlpha = std::min(_dtHeight.currentAlpha + speed, 1.);
} }
const auto dtX = std::min( const auto dtX = std::min(
(now - _animation.started()) / kXExpandingDuration, (now - _animation.started()) / kXExpandingDuration,
1.); 1.);
const auto dtAlpha = std::min(
(now - _alphaAnimationStartedAt) / kAlphaExpandingDuration,
1.);
const auto dtBottomLineAlpha = std::min( const auto dtBottomLineAlpha = std::min(
(now - _bottomLineAlphaAnimationStartedAt) / kAlphaExpandingDuration, (now - _bottomLineAlphaAnimationStartedAt) / kAlphaExpandingDuration,
1.); 1.);
@ -649,20 +654,26 @@ void ChartWidget::ChartAnimationController::tick(
&& isFinished(_animationValueXMax); && isFinished(_animationValueXMax);
const auto yFinished = isFinished(_animationValueHeightMin) const auto yFinished = isFinished(_animationValueHeightMin)
&& isFinished(_animationValueHeightMax); && isFinished(_animationValueHeightMax);
const auto alphaFinished = isFinished(_animValueYAlpha); const auto alphaFinished = isFinished(_animationValueHeightAlpha)
&& isFinished(_animationValueHeightMax);
const auto bottomLineAlphaFinished = isFinished( const auto bottomLineAlphaFinished = isFinished(
_animValueBottomLineAlpha); _animValueBottomLineAlpha);
const auto footerMinFinished = isFinished(_animationValueFooterHeightMin);
const auto footerMaxFinished = isFinished(_animationValueFooterHeightMax);
chartLinesViewContext.tick(now); chartLinesViewContext.tick(now);
if (xFinished if (xFinished
&& yFinished && yFinished
&& alphaFinished && alphaFinished
&& bottomLineAlphaFinished && bottomLineAlphaFinished
&& footerMinFinished
&& footerMaxFinished
&& chartLinesViewContext.isFinished()) { && chartLinesViewContext.isFinished()) {
const auto &lines = horizontalLines.back().lines; const auto &lines = horizontalLines.back().lines;
if ((lines.front().absoluteValue == _animationValueHeightMin.to()) if ((_finalHeightLimits.min == _animationValueHeightMin.to())
&& lines.back().absoluteValue == _animationValueHeightMax.to()) { && _finalHeightLimits.max == _animationValueHeightMax.to()) {
_animation.stop(); _animation.stop();
_benchmark = {}; _benchmark = {};
} }
@ -682,15 +693,12 @@ void ChartWidget::ChartAnimationController::tick(
dtBottomLineAlpha, dtBottomLineAlpha,
anim::easeInCubic); anim::easeInCubic);
} }
if (_heightAnimationStarted) { if (!yFinished) {
_animationValueHeightMin.update(_dtCurrent.min, anim::easeInCubic); _animationValueHeightMin.update(
_animationValueHeightMax.update(_dtCurrent.max, anim::easeInCubic); _dtHeight.current.min,
_animValueYAlpha.update(dtAlpha, anim::easeInCubic);
_animationValueFooterHeightMin.update(
_dtCurrent.min,
anim::easeInCubic); anim::easeInCubic);
_animationValueFooterHeightMax.update( _animationValueHeightMax.update(
_dtCurrent.max, _dtHeight.current.max,
anim::easeInCubic); anim::easeInCubic);
for (auto &horizontalLine : horizontalLines) { for (auto &horizontalLine : horizontalLines) {
@ -699,9 +707,22 @@ void ChartWidget::ChartAnimationController::tick(
_animationValueHeightMin.current()); _animationValueHeightMin.current());
} }
} }
if (!footerMinFinished) {
_animationValueFooterHeightMin.update(
_dtHeight.current.min,
anim::sineInOut);
}
if (!footerMaxFinished) {
_animationValueFooterHeightMax.update(
_dtHeight.current.max,
anim::sineInOut);
}
if (dtAlpha >= 0. && dtAlpha <= 1.) { if (!alphaFinished) {
const auto value = _animValueYAlpha.current(); _animationValueHeightAlpha.update(
_dtHeight.currentAlpha,
anim::easeInCubic);
const auto value = _animationValueHeightAlpha.current();
for (auto &horizontalLine : horizontalLines) { for (auto &horizontalLine : horizontalLines) {
horizontalLine.alpha = horizontalLine.fixedAlpha * (1. - value); horizontalLine.alpha = horizontalLine.fixedAlpha * (1. - value);
@ -732,11 +753,6 @@ void ChartWidget::ChartAnimationController::tick(
dateLines.push_back(data); dateLines.push_back(data);
} }
} }
if (yFinished && alphaFinished) {
_alphaAnimationStartedAt = 0;
_heightAnimationStarted = false;
}
} }
Limits ChartWidget::ChartAnimationController::currentXLimits() const { Limits ChartWidget::ChartAnimationController::currentXLimits() const {
@ -799,11 +815,6 @@ bool ChartWidget::ChartAnimationController::isFPSSlow() const {
return _benchmark.lastFPSSlow; return _benchmark.lastFPSSlow;
} }
auto ChartWidget::ChartAnimationController::heightAnimationStarts() const
-> rpl::producer<> {
return _heightAnimationStarts.events();
}
ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent) ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
: Ui::RpWidget(parent) : Ui::RpWidget(parent)
, _chartArea(base::make_unique_q<RpMouseWidget>(this)) , _chartArea(base::make_unique_q<RpMouseWidget>(this))
@ -1074,11 +1085,8 @@ void ChartWidget::setupFooter() {
} }
}); });
rpl::merge( _animationController.addHorizontalLineRequests(
_animationController.heightAnimationStarts(),
_footer->userInteractionFinished()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
_animationController.resetAlpha();
addHorizontalLine(_animationController.finalHeightLimits(), true); addHorizontalLine(_animationController.finalHeightLimits(), true);
_animationController.start(); _animationController.start();
}, _footer->lifetime()); }, _footer->lifetime());
@ -1097,7 +1105,6 @@ void ChartWidget::setupFooter() {
return; return;
} }
_lastHeightLimitsChanged = now; _lastHeightLimitsChanged = now;
_animationController.resetAlpha();
addHorizontalLine(_animationController.finalHeightLimits(), true); addHorizontalLine(_animationController.finalHeightLimits(), true);
}, _footer->lifetime()); }, _footer->lifetime());
} }

View file

@ -74,15 +74,13 @@ private:
[[nodiscard]] bool footerAnimating() const; [[nodiscard]] bool footerAnimating() const;
[[nodiscard]] bool isFPSSlow() const; [[nodiscard]] bool isFPSSlow() const;
[[nodiscard]] rpl::producer<> heightAnimationStarts() const; [[nodiscard]] rpl::producer<> addHorizontalLineRequests() const;
private: private:
Ui::Animations::Basic _animation; Ui::Animations::Basic _animation;
crl::time _lastUserInteracted = 0; crl::time _lastUserInteracted = 0;
crl::time _alphaAnimationStartedAt = 0;
crl::time _bottomLineAlphaAnimationStartedAt = 0; crl::time _bottomLineAlphaAnimationStartedAt = 0;
bool _heightAnimationStarted = false;
anim::value _animationValueXMin; anim::value _animationValueXMin;
anim::value _animationValueXMax; anim::value _animationValueXMax;
@ -92,22 +90,27 @@ private:
anim::value _animationValueFooterHeightMin; anim::value _animationValueFooterHeightMin;
anim::value _animationValueFooterHeightMax; anim::value _animationValueFooterHeightMax;
anim::value _animValueYAlpha; anim::value _animationValueHeightAlpha;
anim::value _animValueBottomLineAlpha; anim::value _animValueBottomLineAlpha;
Limits _finalHeightLimits; Limits _finalHeightLimits;
Limits _currentXIndices; Limits _currentXIndices;
float _dtHeightSpeed = 0.; struct {
Limits _dtCurrent; float speed = 0.;
Limits current;
float64 currentAlpha = 0.;
} _dtHeight;
Limits _previousFullHeightLimits;
struct { struct {
crl::time lastTickedAt = 0; crl::time lastTickedAt = 0;
bool lastFPSSlow = false; bool lastFPSSlow = false;
} _benchmark; } _benchmark;
rpl::event_stream<> _heightAnimationStarts; rpl::event_stream<> _addHorizontalLineRequests;
}; };