Spectral/imports/Spectral/Effect/RippleEffect.qml

240 lines
6.2 KiB
QML

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
import Spectral.Component 2.0
import Spectral.Setting 0.1
AutoMouseArea {
id: ripple
property color color: MSettings.darkTheme ? Qt.rgba(255, 255, 255, 0.16) : Qt.rgba(0, 0, 0, 0.08)
property bool circular: false
property bool centered: false
property bool focused
property color focusColor: "transparent"
property int focusWidth: width - 32
property Item control
clip: true
Connections {
target: control
onPressedChanged: {
if (!control.pressed)
__private.removeLastCircle()
}
}
onPressed: {
__private.createTapCircle(mouse.x, mouse.y)
if (control)
mouse.accepted = false
}
onReleased: __private.removeLastCircle()
onCanceled: __private.removeLastCircle()
QtObject {
id: __private
property int startRadius: 0
property int endRadius
property bool showFocus: true
property Item lastCircle
function createTapCircle(x, y) {
endRadius = centered ? width/2 : radius(x, y) + 5
showFocus = false
lastCircle = tapCircle.createObject(ripple, {
"circleX": centered ? width/2 : x,
"circleY": centered ? height/2 : y
})
}
function removeLastCircle() {
if (lastCircle)
lastCircle.removeCircle()
}
function radius(x, y) {
var dist1 = Math.max(dist(x, y, 0, 0), dist(x, y, width, height))
var dist2 = Math.max(dist(x, y, width, 0), dist(x, y, 0, height))
return Math.max(dist1, dist2)
}
function dist(x1, y1, x2, y2) {
var distX = x2 - x1
var distY = y2 - y1
return Math.sqrt(distX * distX + distY * distY)
}
}
Rectangle {
id: focusBackground
objectName: "focusBackground"
width: parent.width
height: parent.height
color: Qt.rgba(0,0,0,0.2)
opacity: __private.showFocus && focused ? 1 : 0
Behavior on opacity {
NumberAnimation { duration: 500; easing.type: Easing.InOutQuad }
}
}
Rectangle {
id: focusCircle
objectName: "focusRipple"
property bool focusedState
x: (parent.width - width)/2
y: (parent.height - height)/2
width: focused
? focusedState ? focusWidth
: Math.min(parent.width - 8, focusWidth + 12)
: parent.width/5
height: width
radius: width/2
opacity: __private.showFocus && focused ? 1 : 0
color: focusColor.a === 0 ? Qt.rgba(1,1,1,0.4) : focusColor
Behavior on opacity {
NumberAnimation { duration: 500; easing.type: Easing.InOutQuad }
}
Behavior on width {
NumberAnimation { duration: focusTimer.interval; }
}
Timer {
id: focusTimer
running: focused
repeat: true
interval: 800
onTriggered: focusCircle.focusedState = !focusCircle.focusedState
}
}
Component {
id: tapCircle
Item {
id: circleItem
objectName: "tapRipple"
property bool done
property real circleX
property real circleY
property bool closed
width: parent.width
height: parent.height
function removeCircle() {
done = true
if (fillSizeAnimation.running) {
fillOpacityAnimation.stop()
closeAnimation.start()
circleItem.destroy(500);
} else {
__private.showFocus = true
fadeAnimation.start();
circleItem.destroy(300);
}
}
Item {
id: circleParent
width: parent.width
height: parent.height
visible: !circular
Rectangle {
id: circleRectangle
x: circleItem.circleX - radius
y: circleItem.circleY - radius
width: radius * 2
height: radius * 2
opacity: 0
color: ripple.color
NumberAnimation {
id: fillSizeAnimation
running: true
target: circleRectangle; property: "radius"; duration: 500;
from: __private.startRadius; to: __private.endRadius;
easing.type: Easing.InOutQuad
onStopped: {
if (done)
__private.showFocus = true
}
}
NumberAnimation {
id: fillOpacityAnimation
running: true
target: circleRectangle; property: "opacity"; duration: 300;
from: 0; to: 1; easing.type: Easing.InOutQuad
}
NumberAnimation {
id: fadeAnimation
target: circleRectangle; property: "opacity"; duration: 300;
from: 1; to: 0; easing.type: Easing.InOutQuad
}
SequentialAnimation {
id: closeAnimation
NumberAnimation {
target: circleRectangle; property: "opacity"; duration: 250;
to: 1; easing.type: Easing.InOutQuad
}
NumberAnimation {
target: circleRectangle; property: "opacity"; duration: 250;
from: 1; to: 0; easing.type: Easing.InOutQuad
}
}
}
}
CircleMask {
anchors.fill: parent
source: circleParent
visible: circular
}
}
}
}