Improved animation from Connecting state in mute button.
This commit is contained in:
parent
c0e1dfcc8d
commit
ac71aabba2
3 changed files with 151 additions and 32 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue