Improved animation from Connecting state in mute button.

This commit is contained in:
23rd 2020-12-17 10:40:29 +03:00
parent c0e1dfcc8d
commit ac71aabba2
3 changed files with 151 additions and 32 deletions

View file

@ -8,7 +8,6 @@
#include "base/flat_map.h" #include "base/flat_map.h"
#include "ui/abstract_button.h" #include "ui/abstract_button.h"
#include "ui/effects/radial_animation.h"
#include "ui/paint/blobs.h" #include "ui/paint/blobs.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/widgets/call_button.h" #include "ui/widgets/call_button.h"
@ -45,8 +44,26 @@ constexpr auto kGlowAlpha = 150;
constexpr auto kOverrideColorBgAlpha = 76; constexpr auto kOverrideColorBgAlpha = 76;
constexpr auto kOverrideColorRippleAlpha = 50; constexpr auto kOverrideColorRippleAlpha = 50;
constexpr auto kSwitchStateDuration = 120;
constexpr auto kShiftDuration = crl::time(300); constexpr auto kShiftDuration = crl::time(300);
constexpr auto kSwitchStateDuration = crl::time(120);
// Switch state from Connecting animation.
constexpr auto kSwitchRadialDuration = crl::time(225);
constexpr auto kSwitchCirclelDuration = crl::time(275);
constexpr auto kBlobsScaleEnterDuration = crl::time(400);
constexpr auto kSwitchStateFromConnectingDuration = kSwitchRadialDuration
+ kSwitchCirclelDuration
+ kBlobsScaleEnterDuration;
constexpr auto kRadialEndPartAnimation = float(kSwitchRadialDuration)
/ kSwitchStateFromConnectingDuration;
constexpr auto kBlobsWidgetPartAnimation = 1. - kRadialEndPartAnimation;
constexpr auto kFillCirclePartAnimation = float(kSwitchCirclelDuration)
/ (kSwitchCirclelDuration + kBlobsScaleEnterDuration);
constexpr auto kBlobPartAnimation = float(kBlobsScaleEnterDuration)
/ (kSwitchCirclelDuration + kBlobsScaleEnterDuration);
constexpr auto kOverlapProgressRadialHide = 1.2;
auto MuteBlobs() { auto MuteBlobs() {
return std::vector<Paint::Blobs::BlobData>{ return std::vector<Paint::Blobs::BlobData>{
@ -129,6 +146,9 @@ public:
[[nodiscard]] QRectF innerRect() const; [[nodiscard]] QRectF innerRect() const;
[[nodiscard]] float64 switchConnectingProgress() const;
void setSwitchConnectingProgress(float64 progress);
private: private:
void init(); void init();
@ -140,9 +160,16 @@ private:
int _center = 0; int _center = 0;
QRectF _circleRect; QRectF _circleRect;
float64 _switchConnectingProgress = 0.;
crl::time _blobsLastTime = 0; crl::time _blobsLastTime = 0;
crl::time _blobsHideLastTime = 0; crl::time _blobsHideLastTime = 0;
float64 _blobsScaleEnter = 0.;
crl::time _blobsScaleLastTime = 0;
bool _hideBlobs = true;
Animations::Basic _animation; Animations::Basic _animation;
}; };
@ -155,25 +182,20 @@ BlobsWidget::BlobsWidget(
, _circleRaidus(st::callMuteMainBlobMinRadius * kMainRadiusFactor) , _circleRaidus(st::callMuteMainBlobMinRadius * kMainRadiusFactor)
, _blobBrush(Qt::transparent) , _blobBrush(Qt::transparent)
, _glowBrush(Qt::transparent) , _glowBrush(Qt::transparent)
, _blobsLastTime(crl::now()) { , _blobsLastTime(crl::now())
, _blobsScaleLastTime(crl::now()) {
init(); init();
for (auto i = 0; i < _blobs.size(); i++) {
const auto radiuses = _blobs.radiusesAt(i);
auto radiusesChange = rpl::duplicate(
hideBlobs
) | rpl::distinct_until_changed(
) | rpl::map([=](bool hide) -> Radiuses {
return hide
? Radiuses{ radiuses.min, radiuses.min }
: radiuses;
});
_blobs.setRadiusesAt(std::move(radiusesChange), i);
}
std::move( std::move(
hideBlobs hideBlobs
) | rpl::start_with_next([=](bool hide) { ) | rpl::start_with_next([=](bool hide) {
if (_hideBlobs != hide) {
const auto now = crl::now();
if ((now - _blobsScaleLastTime) >= kBlobsScaleEnterDuration) {
_blobsScaleLastTime = now;
}
_hideBlobs = hide;
}
if (hide) { if (hide) {
setLevel(0.); setLevel(0.);
} }
@ -199,7 +221,9 @@ void BlobsWidget::init() {
{ {
const auto &r = _circleRaidus; const auto &r = _circleRaidus;
const auto left = (size.width() - r * 2.) / 2.; const auto left = (size.width() - r * 2.) / 2.;
_circleRect = QRectF(left, left, r * 2, r * 2); const auto add = st::callConnectingRadial.thickness / 2;
_circleRect = QRectF(left, left, r * 2, r * 2).marginsAdded(
style::margins(add, add, add, add));
} }
}, lifetime()); }, lifetime());
@ -219,23 +243,69 @@ void BlobsWidget::init() {
// Blobs. // Blobs.
p.translate(_center, _center); p.translate(_center, _center);
_blobs.paint(p, _blobBrush); const auto scale = (_switchConnectingProgress > 0.)
? anim::easeOutBack(
1.,
_blobsScaleEnter * (1. - std::clamp(
(_switchConnectingProgress / kBlobPartAnimation),
0.,
1.)))
: _blobsScaleEnter;
_blobs.paint(p, _blobBrush, scale);
// Main circle. // Main circle.
p.translate(-_center, -_center);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.setBrush(_blobBrush); p.setBrush(_blobBrush);
p.drawEllipse(QPointF(), _circleRaidus, _circleRaidus); p.drawEllipse(_circleRect);
if (_switchConnectingProgress > 0.) {
p.resetTransform();
const auto circleProgress = std::clamp(
(_switchConnectingProgress - kBlobPartAnimation),
0.,
1.)
/ kFillCirclePartAnimation;
const auto mF = (_circleRect.width() / 2) * (1. - circleProgress);
const auto cutOutRect = _circleRect.marginsRemoved(
QMarginsF(mF, mF, mF, mF));
p.setPen(Qt::NoPen);
p.setBrush(st::callConnectingRadial.color);
p.setOpacity(circleProgress);
p.drawEllipse(_circleRect);
p.setOpacity(1.);
p.setBrush(st::callIconBg);
p.save();
p.setCompositionMode(QPainter::CompositionMode_Source);
p.drawEllipse(cutOutRect);
p.restore();
p.drawEllipse(cutOutRect);
}
}, lifetime()); }, lifetime());
_animation.init([=](crl::time now) { _animation.init([=](crl::time now) {
if (const auto &last = _blobsHideLastTime; (last > 0) if (const auto &last = _blobsHideLastTime; (last > 0)
&& (now - last >= Paint::Blobs::kHideBlobsDuration)) { && (now - last >= kBlobsScaleEnterDuration)) {
_animation.stop(); _animation.stop();
return false; return false;
} }
_blobs.updateLevel(now - _blobsLastTime); _blobs.updateLevel(now - _blobsLastTime);
_blobsLastTime = now; _blobsLastTime = now;
const auto dt = std::clamp(
(now - _blobsScaleLastTime) / float64(kBlobsScaleEnterDuration),
0.,
1.);
_blobsScaleEnter = _hideBlobs
? (1. - anim::linear(1., dt))
: anim::easeOutBack(1., dt);
update(); update();
return true; return true;
}); });
@ -274,6 +344,14 @@ void BlobsWidget::setLevel(float level) {
_blobs.setLevel(level); _blobs.setLevel(level);
} }
float64 BlobsWidget::switchConnectingProgress() const {
return _switchConnectingProgress;
}
void BlobsWidget::setSwitchConnectingProgress(float64 progress) {
_switchConnectingProgress = progress;
}
CallMuteButton::CallMuteButton( CallMuteButton::CallMuteButton(
not_null<RpWidget*> parent, not_null<RpWidget*> parent,
rpl::producer<bool> &&hideBlobs, rpl::producer<bool> &&hideBlobs,
@ -337,6 +415,14 @@ void CallMuteButton::init() {
st::callConnectingRadial); st::callConnectingRadial);
_radial->start(); _radial->start();
} }
if (value == 1.) {
_lastRadialState = std::nullopt;
} else {
if (_radial && !_lastRadialState.has_value()) {
_lastRadialState = _radial->computeState();
}
}
}, lifetime()); }, lifetime());
// State type. // State type.
@ -388,20 +474,24 @@ void CallMuteButton::init() {
setHandleMouseState(mouseState); setHandleMouseState(mouseState);
} }
const auto fromConnecting = IsConnecting(previous);
const auto toConnecting = IsConnecting(type);
const auto crossFrom = IsMuted(previous) ? 0. : 1.; const auto crossFrom = IsMuted(previous) ? 0. : 1.;
const auto crossTo = IsMuted(type) ? 0. : 1.; const auto crossTo = IsMuted(type) ? 0. : 1.;
const auto radialShowFrom = IsConnecting(previous) ? 1. : 0.; const auto radialShowFrom = fromConnecting ? 1. : 0.;
const auto radialShowTo = IsConnecting(type) ? 1. : 0.; const auto radialShowTo = toConnecting ? 1. : 0.;
const auto from = _switchAnimation.animating() const auto from = (_switchAnimation.animating() && !fromConnecting)
? (1. - _switchAnimation.value(0.)) ? (1. - _switchAnimation.value(0.))
: 0.; : 0.;
const auto to = 1.; const auto to = 1.;
auto callback = [=](float64 value) { auto callback = [=](float64 value) {
const auto brushProgress = fromConnecting ? 1. : value;
_blobs->setBlobBrush(QBrush( _blobs->setBlobBrush(QBrush(
linearGradients.gradient(previous, type, value))); linearGradients.gradient(previous, type, brushProgress)));
_blobs->setGlowBrush(QBrush( _blobs->setGlowBrush(QBrush(
glows.gradient(previous, type, value))); glows.gradient(previous, type, value)));
_blobs->update(); _blobs->update();
@ -419,6 +509,10 @@ void CallMuteButton::init() {
: anim::interpolateF(radialShowFrom, radialShowTo, value); : anim::interpolateF(radialShowFrom, radialShowTo, value);
if (radialShowProgress != _radialShowProgress.current()) { if (radialShowProgress != _radialShowProgress.current()) {
_radialShowProgress = radialShowProgress; _radialShowProgress = radialShowProgress;
_blobs->setSwitchConnectingProgress(std::clamp(
radialShowProgress / kBlobsWidgetPartAnimation,
0.,
1.));
} }
overridesColors(previous, type, value); overridesColors(previous, type, value);
@ -429,7 +523,9 @@ void CallMuteButton::init() {
}; };
_switchAnimation.stop(); _switchAnimation.stop();
const auto duration = (1. - from) * kSwitchStateDuration; const auto duration = (1. - from) * ((fromConnecting || toConnecting)
? kSwitchStateFromConnectingDuration
: kSwitchStateDuration);
_switchAnimation.start(std::move(callback), from, to, duration); _switchAnimation.start(std::move(callback), from, to, duration);
}, lifetime()); }, lifetime());
@ -456,8 +552,29 @@ void CallMuteButton::init() {
_muteIconRect.topLeft(), _muteIconRect.topLeft(),
1. - _crossLineProgress); 1. - _crossLineProgress);
if (_radial) { if (_lastRadialState.has_value() && _switchAnimation.animating()) {
p.setOpacity(_radialShowProgress.current()); const auto radialProgress = (1. - _radialShowProgress.current())
/ kRadialEndPartAnimation;
auto r = *_lastRadialState;
r.arcLength = anim::interpolate(
r.arcLength,
-RadialState::kFull,
std::clamp(radialProgress, 0., 1.));
const auto opacity = (radialProgress > kOverlapProgressRadialHide)
? 0.
: _blobs->switchConnectingProgress();
p.setOpacity(opacity);
InfiniteRadialAnimation::Draw(
p,
r,
_st.bgPosition,
st::callConnectingRadial.size,
_content->width(),
QPen(st::callConnectingRadial.color),
st::callConnectingRadial.thickness);
} else if (_radial) {
_radial->draw( _radial->draw(
p, p,
_st.bgPosition, _st.bgPosition,

View file

@ -10,6 +10,7 @@
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/effects/cross_line.h" #include "ui/effects/cross_line.h"
#include "ui/effects/gradient.h" #include "ui/effects/gradient.h"
#include "ui/effects/radial_animation.h"
namespace Ui { namespace Ui {
@ -17,7 +18,6 @@ class BlobsWidget;
class AbstractButton; class AbstractButton;
class FlatLabel; class FlatLabel;
class InfiniteRadialAnimation;
class RpWidget; class RpWidget;
struct CallButtonColors; struct CallButtonColors;
@ -92,6 +92,8 @@ private:
QRect _muteIconRect; QRect _muteIconRect;
HandleMouseState _handleMouseState = HandleMouseState::Enabled; HandleMouseState _handleMouseState = HandleMouseState::Enabled;
std::optional<RadialState> _lastRadialState = std::nullopt;
const style::CallButton &_st; const style::CallButton &_st;
const base::unique_qptr<BlobsWidget> _blobs; const base::unique_qptr<BlobsWidget> _blobs;

View file

@ -1409,10 +1409,10 @@ defaultToast: Toast {
callMuteMainBlobMinRadius: 57px; callMuteMainBlobMinRadius: 57px;
callMuteMainBlobMaxRadius: 63px; callMuteMainBlobMaxRadius: 63px;
callMuteMinorBlobMinRadius: 62px; callMuteMinorBlobMinRadius: 64px;
callMuteMinorBlobMaxRadius: 72px; callMuteMinorBlobMaxRadius: 74px;
callMuteMajorBlobMinRadius: 65px; callMuteMajorBlobMinRadius: 67px;
callMuteMajorBlobMaxRadius: 75px; callMuteMajorBlobMaxRadius: 77px;
callMuteButtonActiveIcon: icon {{ "calls/voice_unmuted_large", groupCallIconFg }}; callMuteButtonActiveIcon: icon {{ "calls/voice_unmuted_large", groupCallIconFg }};
callMuteButtonActiveInner: IconButton { callMuteButtonActiveInner: IconButton {