feat(quickshell): "Take your time." glows and flickers
A new GlowText component lights a line from inside: a soft two-layer halo that breathes slowly, and every 4-14 seconds a short, uneven stutter like a tube catching. Only opacity and the halo move, so the glyphs stay on the pixel grid. The lock screen and the power menu use it; the power menu's stops while it is closed.
This commit is contained in:
1 parent
c36a74e861
commit
e9735d1129
4 files changed
+124
-16
No files matched your search
@@ -0,0 +1,110 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import "root:/config"
|
||||
|
||||
// A line of type lit from inside, like a tube in a dark console: a soft halo
|
||||
// that breathes slowly, and every so often a short, uneven stutter where the
|
||||
// light catches — two or three dips, then steady again for a while. The
|
||||
// stutters come at random intervals so it never settles into a rhythm you
|
||||
// could count.
|
||||
//
|
||||
// Only the halo and the opacity move, never the geometry, so the glyphs stay
|
||||
// on the pixel grid. Set `active` false while the text is off screen to stop
|
||||
// the animation.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string text: ""
|
||||
property color color: Theme.accent
|
||||
property color glowColor: root.color
|
||||
property string family: Theme.fontDisplay
|
||||
property int pixelSize: Theme.fsLarge
|
||||
property int weight: Font.Light
|
||||
property real letterSpacing: 0
|
||||
property bool active: true
|
||||
|
||||
// 0..1, how lit the tube is at this instant.
|
||||
property real lit: 1
|
||||
// Slow breathing on the halo alone, between `low` and 1.
|
||||
property real breath: 1
|
||||
|
||||
implicitWidth: label.implicitWidth
|
||||
implicitHeight: label.implicitHeight
|
||||
|
||||
Text {
|
||||
id: label
|
||||
visible: false
|
||||
text: root.text
|
||||
color: root.color
|
||||
font.family: root.family
|
||||
font.pixelSize: root.pixelSize
|
||||
font.weight: root.weight
|
||||
font.letterSpacing: root.letterSpacing
|
||||
// Greyscale antialiasing: the glyphs are drawn into the effect's
|
||||
// texture, and native subpixel rendering there leaves colour fringes.
|
||||
renderType: Text.CurveRendering
|
||||
}
|
||||
|
||||
// A wide, faint halo under a tight, brighter one, so the light both
|
||||
// spreads into the dark and hugs the strokes.
|
||||
MultiEffect {
|
||||
source: label
|
||||
anchors.fill: label
|
||||
autoPaddingEnabled: true
|
||||
opacity: root.lit
|
||||
|
||||
shadowEnabled: true
|
||||
shadowColor: root.glowColor
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 0
|
||||
shadowBlur: 1.0
|
||||
blurMax: Math.max(32, Math.round(root.pixelSize * 2))
|
||||
shadowOpacity: 0.7 * root.breath
|
||||
shadowScale: 1.02
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
source: label
|
||||
anchors.fill: label
|
||||
autoPaddingEnabled: true
|
||||
opacity: 0.55 + 0.45 * root.lit
|
||||
|
||||
shadowEnabled: true
|
||||
shadowColor: root.glowColor
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 0
|
||||
shadowBlur: 1.0
|
||||
blurMax: Math.max(8, Math.round(root.pixelSize * 0.35))
|
||||
shadowOpacity: 0.9 * root.lit
|
||||
}
|
||||
|
||||
SequentialAnimation on breath {
|
||||
running: root.active
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.6; duration: 2600; easing.type: Easing.InOutSine }
|
||||
NumberAnimation { to: 1.0; duration: 2600; easing.type: Easing.InOutSine }
|
||||
}
|
||||
|
||||
// One stutter: uneven dips of uneven length, then back to full.
|
||||
SequentialAnimation {
|
||||
id: stutter
|
||||
NumberAnimation { target: root; property: "lit"; to: 0.25; duration: 40 }
|
||||
NumberAnimation { target: root; property: "lit"; to: 0.9; duration: 60 }
|
||||
PauseAnimation { duration: 70 }
|
||||
NumberAnimation { target: root; property: "lit"; to: 0.4; duration: 30 }
|
||||
NumberAnimation { target: root; property: "lit"; to: 1.0; duration: 180; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: nextStutter
|
||||
running: root.active
|
||||
repeat: true
|
||||
interval: 6000
|
||||
onTriggered: {
|
||||
stutter.restart();
|
||||
nextStutter.interval = 4000 + Math.random() * 10000;
|
||||
}
|
||||
}
|
||||
|
||||
onActiveChanged: if (!root.active) { stutter.stop(); root.lit = 1; }
|
||||
}
|
||||
Reference in new issue
Block a user