feat: swing the pill's padlock open when it unlocks
This commit is contained in:
1 parent
2ab4122c05
commit
aeb9aa89d5
3 files changed
+70
-35
No files matched your search
@@ -231,6 +231,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: 16
|
||||
open: root.shownPhase === "success"
|
||||
pace: root.pace
|
||||
color: root.shownPhase === "failure" || root.shownPhase === "lockout" ? Theme.failure : Theme.textPrimary
|
||||
}
|
||||
|
||||
|
||||
@@ -411,6 +411,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width * 0.42
|
||||
height: width
|
||||
pace: root.pace
|
||||
color: root.color
|
||||
opacity: root.mode === "lockout" ? 1 : 0
|
||||
scale: root.mode === "lockout" ? 1 : 0.7
|
||||
|
||||
+68
-35
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// A padlock whose shackle lifts and swings open.
|
||||
// A padlock that opens the way a real one does: the shackle jumps up, its
|
||||
// left leg coming out of the body, and swings round its right leg to the
|
||||
// other side.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
@@ -10,9 +12,64 @@ Item {
|
||||
|
||||
property color color: Theme.textPrimary
|
||||
property bool open: false
|
||||
property real pace: 1
|
||||
|
||||
readonly property real u: width / 100
|
||||
|
||||
// 0 shut, 1 open. Everything below follows from it. Only opening is
|
||||
// animated: it is shut again for the next scan, and the screen was
|
||||
// locked all along, so showing it lock would make no sense.
|
||||
property real openness: 0
|
||||
onOpenChanged: {
|
||||
if (open) {
|
||||
opening.restart()
|
||||
} else {
|
||||
opening.stop()
|
||||
openness = 0
|
||||
}
|
||||
}
|
||||
NumberAnimation {
|
||||
id: opening
|
||||
target: root
|
||||
property: "openness"
|
||||
to: 1
|
||||
duration: 560 * root.pace
|
||||
}
|
||||
|
||||
function smooth(from, to, p) {
|
||||
const x = Math.max(0, Math.min(1, (p - from) / (to - from)))
|
||||
return x * x * (3 - 2 * x)
|
||||
}
|
||||
// Up first, then round, a little past the other side and back.
|
||||
readonly property real lift: 12 * smooth(0, 0.3, openness)
|
||||
readonly property real swing: {
|
||||
const p = Math.max(0, Math.min(1, (openness - 0.22) / 0.78))
|
||||
const s = 1.4
|
||||
return 180 * (1 + (s + 1) * Math.pow(p - 1, 3) + s * Math.pow(p - 1, 2))
|
||||
}
|
||||
// A small beat as it comes open.
|
||||
scale: 1 + 0.14 * Math.sin(Math.PI * smooth(0.55, 1, openness))
|
||||
|
||||
// The shackle as points: up the left leg, over the top, down the right
|
||||
// leg, which stays in the body. Swung about that leg, everything else
|
||||
// narrows towards it and comes out on the other side.
|
||||
function shackle() {
|
||||
const right = 68.5, radius = 18.5
|
||||
const top = 28 - lift
|
||||
const narrow = Math.cos(swing * Math.PI / 180)
|
||||
const points = []
|
||||
const add = (x, y) => points.push(Qt.point((right + (x - right) * narrow) * u, y * u))
|
||||
add(31.5, 56 - 18 * smooth(0, 0.3, openness))
|
||||
add(31.5, top)
|
||||
for (let i = 1; i < 24; ++i) {
|
||||
const a = Math.PI + Math.PI * i / 24
|
||||
add(50 + radius * Math.cos(a), top + radius * Math.sin(a))
|
||||
}
|
||||
add(right, top)
|
||||
add(right, 56)
|
||||
return points
|
||||
}
|
||||
|
||||
// Body
|
||||
Rectangle {
|
||||
x: 14 * root.u
|
||||
@@ -23,41 +80,17 @@ Item {
|
||||
color: root.color
|
||||
}
|
||||
|
||||
// Shackle: a U standing on the body. Opening lifts it and swings it about
|
||||
// its right leg.
|
||||
Item {
|
||||
id: shackle
|
||||
x: 26 * root.u
|
||||
y: 4 * root.u
|
||||
width: 48 * root.u
|
||||
height: 52 * root.u
|
||||
transformOrigin: Item.BottomRight
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
property real lift: root.open ? 7 * root.u : 0
|
||||
Behavior on lift { NumberAnimation { duration: 260; easing.type: Easing.OutBack } }
|
||||
rotation: root.open ? -28 : 0
|
||||
Behavior on rotation { NumberAnimation { duration: 320; easing.type: Easing.OutBack } }
|
||||
transform: Translate { y: -shackle.lift }
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
ShapePath {
|
||||
strokeColor: root.color
|
||||
strokeWidth: 11 * root.u
|
||||
fillColor: "transparent"
|
||||
capStyle: ShapePath.FlatCap
|
||||
startX: 5.5 * root.u
|
||||
startY: shackle.height
|
||||
PathLine { x: 5.5 * root.u; y: 24 * root.u }
|
||||
PathArc {
|
||||
x: shackle.width - 5.5 * root.u
|
||||
y: 24 * root.u
|
||||
radiusX: (shackle.width - 11 * root.u) / 2
|
||||
radiusY: radiusX
|
||||
}
|
||||
PathLine { x: shackle.width - 5.5 * root.u; y: shackle.height }
|
||||
}
|
||||
ShapePath {
|
||||
strokeColor: root.color
|
||||
strokeWidth: 11 * root.u
|
||||
fillColor: "transparent"
|
||||
capStyle: ShapePath.RoundCap
|
||||
joinStyle: ShapePath.RoundJoin
|
||||
PathPolyline { path: root.shackle() }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user