diff --git a/quickshell/Modules/DankBar/Widgets/Media.qml b/quickshell/Modules/DankBar/Widgets/Media.qml index 8171ee741..81821f9a8 100644 --- a/quickshell/Modules/DankBar/Widgets/Media.qml +++ b/quickshell/Modules/DankBar/Widgets/Media.qml @@ -358,38 +358,50 @@ BasePill { onTextChanged: { scrollOffset = 0; textShift = 0; + scrollTimer.reset(); textChangeAnimation.restart(); } - SequentialAnimation { - id: scrollAnimation + // Timer stepping, not NumberAnimation — a running animation commits frames every vsync (#2863) + Timer { + id: scrollTimer + readonly property real maxOffset: Math.max(0, mediaText.implicitWidth - textContainer.width + 5) + property int direction: 1 + property int holdTicks: 33 + + interval: 60 + repeat: true running: mediaText.needsScrolling && textContainer.visible && mediaText.onScreen && root._isPlaying - loops: Animation.Infinite - onStopped: mediaText.scrollOffset = 0 - PauseAnimation { - duration: 2000 + function reset() { + mediaText.scrollOffset = 0; + direction = 1; + holdTicks = 33; } - NumberAnimation { - target: mediaText - property: "scrollOffset" - from: 0 - to: mediaText.implicitWidth - textContainer.width + 5 - duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60) - easing.type: Easing.Linear + onRunningChanged: { + if (!running) + reset(); } - - PauseAnimation { - duration: 2000 - } - - NumberAnimation { - target: mediaText - property: "scrollOffset" - to: 0 - duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60) - easing.type: Easing.Linear + onTriggered: { + if (holdTicks > 0) { + holdTicks--; + return; + } + const next = mediaText.scrollOffset + direction; + if (next >= maxOffset) { + mediaText.scrollOffset = maxOffset; + direction = -1; + holdTicks = 33; + return; + } + if (next <= 0) { + mediaText.scrollOffset = 0; + direction = 1; + holdTicks = 33; + return; + } + mediaText.scrollOffset = next; } } diff --git a/quickshell/Widgets/DankAlbumArt.qml b/quickshell/Widgets/DankAlbumArt.qml index 90699b1e3..351704484 100644 --- a/quickshell/Widgets/DankAlbumArt.qml +++ b/quickshell/Widgets/DankAlbumArt.qml @@ -182,16 +182,12 @@ Item { } } - FrameAnimation { - running: blobEffect.visible - property real pending: 0 - onTriggered: { - pending += frameTime; - if (pending < 0.03) - return; - root.stepBlob(Math.min(pending, 0.05)); - pending = 0; - } + // Timer, not FrameAnimation — a running animation commits frames every vsync (#2863) + Timer { + running: blobEffect.visible && root.onScreen + interval: 33 + repeat: true + onTriggered: root.stepBlob(0.033) } ShaderEffect { diff --git a/quickshell/Widgets/DankSeekbar.qml b/quickshell/Widgets/DankSeekbar.qml index d84573247..e8250a2f7 100644 --- a/quickshell/Widgets/DankSeekbar.qml +++ b/quickshell/Widgets/DankSeekbar.qml @@ -147,6 +147,10 @@ Item { trackColor: MediaAccentService.accentTrack actualProgressColor: MediaAccentService.accentSubtle isPlaying: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing + onFrameTicked: { + if (!root.isSeeking) + activePlayer.positionChanged(); + } MouseArea { id: waveMouseArea diff --git a/quickshell/Widgets/M3WaveProgress.qml b/quickshell/Widgets/M3WaveProgress.qml index a35a4562f..92ff920c6 100644 --- a/quickshell/Widgets/M3WaveProgress.qml +++ b/quickshell/Widgets/M3WaveProgress.qml @@ -50,11 +50,15 @@ Item { fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wave_progress.frag.qsb") } + signal frameTicked + FrameAnimation { - running: root.visible && (root.isPlaying || root.currentAmp > 0) + running: root.visible && (root.isPlaying || root.currentAmp > 0) && (root.Window.window?.visible ?? false) onTriggered: { - if (root.isPlaying) - root.phase += 0.03 * frameTime * 60; + if (!root.isPlaying) + return; + root.phase = (root.phase + 0.03 * frameTime * 60) % 6.28318530718; + root.frameTicked(); } } }