perf: smooth UI animations and reduce repaint overhead

Batch of performance and animation work: shared FadeHelper/MotionPrefs
primitives, hover animators, shadow pixmap caching, smooth scrolling,
seek time bubble, and widespread component repaint reductions.
This commit is contained in:
dela
2026-07-08 11:29:38 +08:00
parent 03bc33518c
commit ab1accc821
85 changed files with 2308 additions and 661 deletions
@@ -13,6 +13,8 @@
#include <cmath>
#include <config/config_keys.h>
#include <config/configstore.h>
#include "../tokens/AnimationTokens.h"
#include "../utils/motionprefs.h"
namespace
{
@@ -98,14 +100,14 @@ QWidget *PlayerLongPressHandler::createIndicator(
overlay->setGraphicsEffect(opacityEffect);
opacityAnim = new QPropertyAnimation(opacityEffect, "opacity", this);
opacityAnim->setEasingCurve(QEasingCurve::OutCubic);
opacityAnim->setEasingCurve(AnimationTokens::Curve::enter());
geometryAnim = new QPropertyAnimation(overlay, "geometry", this);
geometryAnim->setEasingCurve(QEasingCurve::OutCubic);
geometryAnim->setEasingCurve(AnimationTokens::Curve::enter());
iconAnim = new QPropertyAnimation(iconLabel, "pos", this);
iconAnim->setDuration(220);
iconAnim->setEasingCurve(QEasingCurve::InOutSine);
iconAnim->setDuration(AnimationTokens::DurationMs::kNormal);
iconAnim->setEasingCurve(AnimationTokens::Curve::oscillate());
connect(opacityAnim, &QPropertyAnimation::finished, overlay,
[overlay, opacityEffect]()
@@ -151,7 +153,7 @@ double PlayerLongPressHandler::nextPlaybackRate(double currentRate) const
bool PlayerLongPressHandler::canStartPropertyAnimation(const QPropertyAnimation *animation) const
{
return animation && animation->targetObject();
return animation && animation->targetObject() && !MotionPrefs::reduceMotion();
}
bool PlayerLongPressHandler::isMouseEdgeLongPressEnabled() const
@@ -545,10 +547,14 @@ void PlayerLongPressHandler::animateIndicator(
overlay->raise();
opacityEffect->setOpacity(startOpacity);
opacityAnim->setDuration(wasVisible ? 120 : 180);
opacityAnim->setDuration(wasVisible ? AnimationTokens::DurationMs::kMicro
: AnimationTokens::DurationMs::kFast);
opacityAnim->setStartValue(startOpacity);
opacityAnim->setEndValue(targetOpacity);
// Geometry lags the opacity slightly (140/200 vs 120/180) so the pill
// reads as sliding into place — tuned choreography, intentionally off
// the shared duration scale.
geometryAnim->setDuration(wasVisible ? 140 : 200);
geometryAnim->setStartValue(startGeometry);
geometryAnim->setEndValue(targetGeometry);
@@ -564,11 +570,11 @@ void PlayerLongPressHandler::animateIndicator(
const QRect currentGeometry = overlay->geometry();
overlay->setProperty("indicatorVisible", false);
opacityAnim->setDuration(120);
opacityAnim->setDuration(AnimationTokens::DurationMs::kMicro);
opacityAnim->setStartValue(opacityEffect->opacity());
opacityAnim->setEndValue(0.0);
geometryAnim->setDuration(120);
geometryAnim->setDuration(AnimationTokens::DurationMs::kMicro);
geometryAnim->setStartValue(currentGeometry);
geometryAnim->setEndValue(
currentGeometry.translated(direction < 0 ? -10 : 10, 0));
@@ -581,6 +587,12 @@ void PlayerLongPressHandler::animateIndicator(
else if (opacityEffect)
{
opacityEffect->setOpacity(visible ? (active ? 1.0 : 0.62) : 0.0);
if (!visible)
{
// The animated path hides via the opacityAnim finished handler;
// when snapping we must hide directly.
overlay->hide();
}
}
if (canStartPropertyAnimation(geometryAnim))
@@ -755,7 +767,7 @@ void PlayerLongPressHandler::updateIndicators()
m_rightIndicatorText, 1, rightActive,
rightActive ? indicatorText : QString());
if (m_pulseTimer && !m_pulseTimer->isActive())
if (m_pulseTimer && !m_pulseTimer->isActive() && !MotionPrefs::reduceMotion())
{
m_pulseForward = false;
updateIndicatorPulse();