diff --git a/CMakeLists.txt b/CMakeLists.txt index 04738bda..cab8f6bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -798,6 +798,7 @@ set(QML_SOURCES resources/qml/pages/WelcomePage.qml resources/qml/ui/NhekoSlider.qml resources/qml/ui/Ripple.qml + resources/qml/ui/RippleImpl.qml resources/qml/ui/Snackbar.qml resources/qml/ui/Spinner.qml resources/qml/ui/animations/BlinkAnimation.qml diff --git a/resources/qml/ui/Ripple.qml b/resources/qml/ui/Ripple.qml index 9d871419..d062002f 100644 --- a/resources/qml/ui/Ripple.qml +++ b/resources/qml/ui/Ripple.qml @@ -2,128 +2,18 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -import QtQuick +import QtQuick 2.15 +import im.nheko 1.0 Item { - id: ripple + id: wrapper + property color color - property color color: "#22000000" - property real maxRadius: Math.max(width, height) - readonly property real radiusAnimationRate: 0.05 - readonly property real radiusTailAnimationRate: 0.5 - readonly property real opacityAnimationDuration: 300 - property var rippleTarget: parent - - anchors.fill: parent - - PointHandler { - id: ph - - onGrabChanged: (_, point) => { - circle.centerX = point.position.x - circle.centerY = point.position.y - } - - target: Rectangle { - id: backgroundLayer - parent: rippleTarget - - anchors.fill: parent - color: "transparent" - clip: true - - Rectangle { - id: circle - - property real centerX - property real centerY - - x: centerX - radius - y: centerY - radius - - height: radius*2 - width: radius*2 - radius: 0 - color: ripple.color - - state: ph.active ? "ACTIVE" : "NORMAL" - states: [ - State { - name: "NORMAL" - }, - State { - name: "ACTIVE" - } - ] - transitions: [ - Transition { - from: "NORMAL" - to: "ACTIVE" - - SequentialAnimation { - //PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x } - //PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y } - PropertyAction { target: circle; property: "visible"; value: true } - PropertyAction { target: circle; property: "opacity"; value: 1 } - - NumberAnimation { - id: radius_animation - - target: circle - properties: "radius" - from: 0 - to: ripple.maxRadius - duration: ripple.maxRadius / ripple.radiusAnimationRate - - easing { - type: Easing.OutQuad - } - - } - - } - - }, - Transition { - from: "ACTIVE" - to: "NORMAL" - - SequentialAnimation { - ParallelAnimation { - NumberAnimation { - id: radius_tail_animation - - target: circle - properties: "radius" - to: ripple.maxRadius - duration: ripple.maxRadius / ripple.radiusTailAnimationRate - - easing { - type: Easing.Linear - } - - } - - NumberAnimation { - id: opacity_animation - - target: circle - properties: "opacity" - to: 0 - duration: ripple.opacityAnimationDuration - - easing { - type: Easing.InQuad - } - - } - - } - PropertyAction { target: circle; property: "visible"; value: false } - } - } - ] - } + Loader { + Component { + id: ripple + RippleImpl { color: color; parent: wrapper.parent; } } + sourceComponent: !Settings.reducedMotion ? ripple : undefined } } diff --git a/resources/qml/ui/RippleImpl.qml b/resources/qml/ui/RippleImpl.qml new file mode 100644 index 00000000..9091bda2 --- /dev/null +++ b/resources/qml/ui/RippleImpl.qml @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick 2.15 + +Item { + id: ripple + + property color color: "#22000000" + property real maxRadius: Math.max(width, height) + readonly property real radiusAnimationRate: 0.05 + readonly property real radiusTailAnimationRate: 0.5 + readonly property real opacityAnimationDuration: 300 + property var rippleTarget: parent + + anchors.fill: parent + + PointHandler { + id: ph + + onGrabChanged: (_, point) => { + circle.centerX = point.position.x + circle.centerY = point.position.y + } + + target: Rectangle { + id: backgroundLayer + parent: rippleTarget + + anchors.fill: parent + color: "transparent" + clip: true + + Rectangle { + id: circle + + property real centerX + property real centerY + + x: centerX - radius + y: centerY - radius + + height: radius*2 + width: radius*2 + radius: 0 + color: ripple.color + + state: ph.active ? "ACTIVE" : "NORMAL" + states: [ + State { + name: "NORMAL" + }, + State { + name: "ACTIVE" + } + ] + transitions: [ + Transition { + from: "NORMAL" + to: "ACTIVE" + + SequentialAnimation { + //PropertyAction { target: circle; property: "centerX"; value: ph.point.position.x } + //PropertyAction { target: circle; property: "centerY"; value: ph.point.position.y } + PropertyAction { target: circle; property: "visible"; value: true } + PropertyAction { target: circle; property: "opacity"; value: 1 } + + NumberAnimation { + id: radius_animation + + target: circle + properties: "radius" + from: 0 + to: ripple.maxRadius + duration: ripple.maxRadius / ripple.radiusAnimationRate + + easing { + type: Easing.OutQuad + } + + } + + } + + }, + Transition { + from: "ACTIVE" + to: "NORMAL" + + SequentialAnimation { + ParallelAnimation { + NumberAnimation { + id: radius_tail_animation + + target: circle + properties: "radius" + to: ripple.maxRadius + duration: ripple.maxRadius / ripple.radiusTailAnimationRate + + easing { + type: Easing.Linear + } + + } + + NumberAnimation { + id: opacity_animation + + target: circle + properties: "opacity" + to: 0 + duration: ripple.opacityAnimationDuration + + easing { + type: Easing.InQuad + } + + } + + } + PropertyAction { target: circle; property: "visible"; value: false } + } + } + ] + } + } + } +}