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 "ui/abstract_button.h"
#include "ui/effects/radial_animation.h"
#include "ui/paint/blobs.h"
#include "ui/painter.h"
#include "ui/widgets/call_button.h"
@ -45,8 +44,26 @@ constexpr auto kGlowAlpha = 150;
constexpr auto kOverrideColorBgAlpha = 76;
constexpr auto kOverrideColorRippleAlpha = 50;
constexpr auto kSwitchStateDuration = 120;
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() {
return std::vector<Paint::Blobs::BlobData>{
@ -129,6 +146,9 @@ public:
[[nodiscard]] QRectF innerRect() const;
[[nodiscard]] float64 switchConnectingProgress() const;
void setSwitchConnectingProgress(float64 progress);
private:
void init();
@ -140,9 +160,16 @@ private:
int _center = 0;
QRectF _circleRect;
float64 _switchConnectingProgress = 0.;
crl::time _blobsLastTime = 0;
crl::time _blobsHideLastTime = 0;
float64 _blobsScaleEnter = 0.;
crl::time _blobsScaleLastTime = 0;
bool _hideBlobs = true;
Animations::Basic _animation;
};
@ -155,25 +182,20 @@ BlobsWidget::BlobsWidget(
, _circleRaidus(st::callMuteMainBlobMinRadius * kMainRadiusFactor)
, _blobBrush(Qt::transparent)
, _glowBrush(Qt::transparent)
, _blobsLastTime(crl::now()) {
, _blobsLastTime(crl::now())
, _blobsScaleLastTime(crl::now()) {
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(
hideBlobs
) | rpl::start_with_next([=](bool hide) {
if (_hideBlobs != hide) {
const auto now = crl::now();
if ((now - _blobsScaleLastTime) >= kBlobsScaleEnterDuration) {
_blobsScaleLastTime = now;
}
_hideBlobs = hide;
}
if (hide) {
setLevel(0.);
}
@ -199,7 +221,9 @@ void BlobsWidget::init() {
{
const auto &r = _circleRaidus;
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());
@ -219,23 +243,69 @@ void BlobsWidget::init() {
// Blobs.
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.
p.translate(-_center, -_center);
p.setPen(Qt::NoPen);
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());
_animation.init([=](crl::time now) {
if (const auto &last = _blobsHideLastTime; (last > 0)
&& (now - last >= Paint::Blobs::kHideBlobsDuration)) {
&& (now - last >= kBlobsScaleEnterDuration)) {
_animation.stop();
return false;
}
_blobs.updateLevel(now - _blobsLastTime);
_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();
return true;
});
@ -274,6 +344,14 @@ void BlobsWidget::setLevel(float level) {
_blobs.setLevel(level);
}
float64 BlobsWidget::switchConnectingProgress() const {
return _switchConnectingProgress;
}
void BlobsWidget::setSwitchConnectingProgress(float64 progress) {
_switchConnectingProgress = progress;
}
CallMuteButton::CallMuteButton(
not_null<RpWidget*> parent,
rpl::producer<bool> &&hideBlobs,
@ -337,6 +415,14 @@ void CallMuteButton::init() {
st::callConnectingRadial);
_radial->start();
}
if (value == 1.) {
_lastRadialState = std::nullopt;
} else {
if (_radial && !_lastRadialState.has_value()) {
_lastRadialState = _radial->computeState();
}
}
}, lifetime());
// State type.
@ -388,20 +474,24 @@ void CallMuteButton::init() {
setHandleMouseState(mouseState);
}
const auto fromConnecting = IsConnecting(previous);
const auto toConnecting = IsConnecting(type);
const auto crossFrom = IsMuted(previous) ? 0. : 1.;
const auto crossTo = IsMuted(type) ? 0. : 1.;
const auto radialShowFrom = IsConnecting(previous) ? 1. : 0.;
const auto radialShowTo = IsConnecting(type) ? 1. : 0.;
const auto radialShowFrom = fromConnecting ? 1. : 0.;
const auto radialShowTo = toConnecting ? 1. : 0.;
const auto from = _switchAnimation.animating()
const auto from = (_switchAnimation.animating() && !fromConnecting)
? (1. - _switchAnimation.value(0.))
: 0.;
const auto to = 1.;
auto callback = [=](float64 value) {
const auto brushProgress = fromConnecting ? 1. : value;
_blobs->setBlobBrush(QBrush(
linearGradients.gradient(previous, type, value)));
linearGradients.gradient(previous, type, brushProgress)));
_blobs->setGlowBrush(QBrush(
glows.gradient(previous, type, value)));
_blobs->update();
@ -419,6 +509,10 @@ void CallMuteButton::init() {
: anim::interpolateF(radialShowFrom, radialShowTo, value);
if (radialShowProgress != _radialShowProgress.current()) {
_radialShowProgress = radialShowProgress;
_blobs->setSwitchConnectingProgress(std::clamp(
radialShowProgress / kBlobsWidgetPartAnimation,
0.,
1.));
}
overridesColors(previous, type, value);
@ -429,7 +523,9 @@ void CallMuteButton::init() {
};
_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);
}, lifetime());
@ -456,8 +552,29 @@ void CallMuteButton::init() {
_muteIconRect.topLeft(),
1. - _crossLineProgress);
if (_radial) {
p.setOpacity(_radialShowProgress.current());
if (_lastRadialState.has_value() && _switchAnimation.animating()) {
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(
p,
_st.bgPosition,

View file

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

View file

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