1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-12 08:42:13 -04:00

ripple: use a shader for ripple effect

This commit is contained in:
bbedward
2026-02-17 09:27:18 -05:00
parent da437e77fb
commit 44d836c975
3 changed files with 92 additions and 48 deletions

View File

@@ -0,0 +1,56 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float widthPx;
float heightPx;
float cornerRadiusPx;
float rippleCenterX;
float rippleCenterY;
float rippleRadius;
float rippleOpacity;
float offsetX;
float offsetY;
float parentWidth;
float parentHeight;
vec4 rippleCol;
} ubuf;
float sdRoundRect(vec2 p, vec2 b, float r) {
vec2 q = abs(p) - (b - vec2(r));
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
}
void main() {
vec2 px = qt_TexCoord0 * vec2(ubuf.widthPx, ubuf.heightPx) + vec2(ubuf.offsetX, ubuf.offsetY);
float aa = fwidth(length(px)) * 1.5;
float circleD = length(px - vec2(ubuf.rippleCenterX, ubuf.rippleCenterY)) - ubuf.rippleRadius;
float circleMask = 1.0 - smoothstep(-aa, aa, circleD);
if (circleMask <= 0.0) {
fragColor = vec4(0.0);
return;
}
float rrMask = 1.0;
if (ubuf.cornerRadiusPx > 0.0) {
vec2 halfSize = vec2(ubuf.parentWidth, ubuf.parentHeight) * 0.5;
float r = min(ubuf.cornerRadiusPx, min(halfSize.x, halfSize.y));
float rrD = sdRoundRect(px - halfSize, halfSize, r);
rrMask = 1.0 - smoothstep(-aa, aa, rrD);
}
float mask = circleMask * rrMask;
if (mask <= 0.0) {
fragColor = vec4(0.0);
return;
}
float a = ubuf.rippleCol.a * ubuf.rippleOpacity * mask * ubuf.qt_Opacity;
fragColor = vec4(ubuf.rippleCol.rgb * a, a);
}

Binary file not shown.

View File

@@ -1,5 +1,4 @@
import QtQuick import QtQuick
import QtQuick.Effects
import qs.Common import qs.Common
Item { Item {
@@ -11,7 +10,7 @@ Item {
property real _rippleX: 0 property real _rippleX: 0
property real _rippleY: 0 property real _rippleY: 0
property real _rippleSize: 0 property real _rippleMaxRadius: 0
readonly property alias animating: rippleAnim.running readonly property alias animating: rippleAnim.running
anchors.fill: parent anchors.fill: parent
@@ -24,7 +23,7 @@ Item {
_rippleY = y; _rippleY = y;
const dist = (ox, oy) => ox * ox + oy * oy; const dist = (ox, oy) => ox * ox + oy * oy;
_rippleSize = Math.sqrt(Math.max(dist(x, y), dist(x, height - y), dist(width - x, y), dist(width - x, height - y))) * 2; _rippleMaxRadius = Math.sqrt(Math.max(dist(x, y), dist(x, height - y), dist(width - x, y), dist(width - x, height - y)));
rippleAnim.restart(); rippleAnim.restart();
} }
@@ -33,45 +32,32 @@ Item {
id: rippleAnim id: rippleAnim
PropertyAction { PropertyAction {
target: ripple target: rippleFx
property: "x" property: "rippleCenterX"
value: root._rippleX value: root._rippleX
} }
PropertyAction { PropertyAction {
target: ripple target: rippleFx
property: "y" property: "rippleCenterY"
value: root._rippleY value: root._rippleY
} }
PropertyAction { PropertyAction {
target: ripple target: rippleFx
property: "implicitWidth" property: "rippleRadius"
value: 0 value: 0
} }
PropertyAction { PropertyAction {
target: ripple target: rippleFx
property: "implicitHeight" property: "rippleOpacity"
value: 0
}
PropertyAction {
target: ripple
property: "opacity"
value: 0.10 value: 0.10
} }
ParallelAnimation { ParallelAnimation {
DankAnim { DankAnim {
target: ripple target: rippleFx
property: "implicitWidth" property: "rippleRadius"
from: 0 from: 0
to: root._rippleSize to: root._rippleMaxRadius
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.bezierCurve: Theme.expressiveCurves.standardDecel
}
DankAnim {
target: ripple
property: "implicitHeight"
from: 0
to: root._rippleSize
duration: Theme.expressiveDurations.expressiveDefaultSpatial duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.bezierCurve: Theme.expressiveCurves.standardDecel easing.bezierCurve: Theme.expressiveCurves.standardDecel
} }
@@ -80,8 +66,8 @@ Item {
duration: Math.round(Theme.expressiveDurations.expressiveDefaultSpatial * 0.6) duration: Math.round(Theme.expressiveDurations.expressiveDefaultSpatial * 0.6)
} }
DankAnim { DankAnim {
target: ripple target: rippleFx
property: "opacity" property: "rippleOpacity"
to: 0 to: 0
duration: Theme.expressiveDurations.expressiveDefaultSpatial duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.bezierCurve: Theme.expressiveCurves.standard easing.bezierCurve: Theme.expressiveCurves.standard
@@ -90,27 +76,29 @@ Item {
} }
} }
// VRAM optimization: Use clip instead of layer+MultiEffect for rounded corners ShaderEffect {
// The layer.enabled approach creates an offscreen texture for every clickable element id: rippleFx
// with rounded corners, which adds 200-400MB VRAM across the UI. visible: rippleAnim.running
// Using clip: true is GPU-native and much cheaper.
Rectangle {
anchors.fill: parent
radius: root.cornerRadius
color: "transparent"
clip: root.cornerRadius > 0
Rectangle { property real rippleCenterX: 0
id: ripple property real rippleCenterY: 0
property real rippleRadius: 0
property real rippleOpacity: 0
radius: Math.min(width, height) / 2 x: Math.max(0, rippleCenterX - rippleRadius)
color: root.rippleColor y: Math.max(0, rippleCenterY - rippleRadius)
opacity: 0 width: Math.max(0, Math.min(root.width, rippleCenterX + rippleRadius) - x)
height: Math.max(0, Math.min(root.height, rippleCenterY + rippleRadius) - y)
transform: Translate { property real widthPx: width
x: -ripple.width / 2 property real heightPx: height
y: -ripple.height / 2 property real cornerRadiusPx: root.cornerRadius
} property real offsetX: x
} property real offsetY: y
property real parentWidth: root.width
property real parentHeight: root.height
property vector4d rippleCol: Qt.vector4d(root.rippleColor.r, root.rippleColor.g, root.rippleColor.b, root.rippleColor.a)
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/ripple.frag.qsb")
} }
} }