nosignal-shell/modules/dashboard/media/BackgroundShapes.qml
2026-05-04 22:16:44 +10:00

90 lines
3 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import M3Shapes
import qs.components
import qs.services
Item {
id: root
readonly property list<int> shapePool: [MaterialShape.Circle, MaterialShape.Cookie4Sided, MaterialShape.Cookie6Sided, MaterialShape.Cookie7Sided, MaterialShape.Cookie9Sided, MaterialShape.Cookie12Sided, MaterialShape.Sunny, MaterialShape.VerySunny, MaterialShape.SoftBurst, MaterialShape.Pentagon, MaterialShape.Gem, MaterialShape.Arch, MaterialShape.Arrow, MaterialShape.Pill, MaterialShape.Triangle, MaterialShape.Fan, MaterialShape.Oval]
property int count: 14
property real minSize: 36
property real maxSize: 124
property real minSpeed: 4
property real maxSpeed: 18
property real minRotSpeed: -12
property real maxRotSpeed: 12
property real shapeOpacity: 0.16
function rand(min: real, max: real): real {
return min + Math.random() * (max - min);
}
function signedRand(min: real, max: real): real {
return rand(min, max) * (Math.random() < 0.5 ? -1 : 1);
}
clip: true
Component.onCompleted: shapes.model = count
Repeater {
id: shapes
DriftingShape {}
}
FrameAnimation {
running: root.visible && root.width > 0 && root.height > 0 && (Players.active?.isPlaying ?? false)
onTriggered: {
const dt = frameTime;
for (let i = 0; i < shapes.count; i++) {
const s = shapes.itemAt(i) as DriftingShape;
if (!s)
continue;
s.x += s.vx * dt;
s.y += s.vy * dt;
s.rotation += s.vr * dt;
if (s.x + s.width < 0)
s.x = root.width;
else if (s.x > root.width)
s.x = -s.width;
if (s.y + s.height < 0)
s.y = root.height;
else if (s.y > root.height)
s.y = -s.height;
}
}
}
component DriftingShape: MaterialShape {
id: shapeItem
required property int index
property real vx: root.signedRand(root.minSpeed, root.maxSpeed)
property real vy: root.signedRand(root.minSpeed, root.maxSpeed)
property real vr: root.rand(root.minRotSpeed, root.maxRotSpeed)
readonly property int colourIdx: Math.floor(Math.random() * 3)
implicitSize: root.rand(root.minSize, root.maxSize)
shape: root.shapePool[Math.floor(Math.random() * root.shapePool.length)]
color: [Colours.palette.m3primaryContainer, Colours.palette.m3secondaryContainer, Colours.palette.m3outlineVariant][colourIdx]
opacity: root.shapeOpacity
rotation: root.rand(0, 360)
Component.onCompleted: {
shapeItem.x = root.rand(-shapeItem.implicitSize / 2, root.width - shapeItem.implicitSize / 2);
shapeItem.y = root.rand(-shapeItem.implicitSize / 2, root.height - shapeItem.implicitSize / 2);
}
Behavior on color {
CAnim {}
}
}
}