Two things made the eyes jump. The body bobbed two units per step and the head rode along with it, and each eye ran its own random blink timer, so the cat winked one eye at a time at intervals of its own choosing. The bob is now a property on the cat rather than an animation owning torso.y, the amplitude is softer, and the head translates against it by 75 percent, the way a walking cat keeps its head level. Measured on a 240 px cat, the eye's vertical travel per step went from 4.0 px to 0.8 px. Both eyes now share one eyelid, driven by a single timer and a single animation. That also removes the Behavior that was fighting the blink animation for the same property, and the mid-blink flicker to the drawn closed-eye arch, which now only appears when the eyes are really shut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
645 lines
27 KiB
QML
645 lines
27 KiB
QML
pragma ComponentBehavior: Bound
|
|
import QtQuick
|
|
import QtQuick.Shapes
|
|
|
|
// A chibi cat drawn from primitives and vector paths.
|
|
// Design space is 100 x 90 units, ground line at y = 84, cat faces right.
|
|
Item {
|
|
id: cat
|
|
|
|
property color coat: "#f4b183"
|
|
property bool facingRight: true
|
|
property string mood: "walking" // walking running sitting grooming stretching sleeping loving alert
|
|
property bool lifted: false
|
|
property bool petted: false
|
|
property point look: Qt.point(0, 0)
|
|
property bool showCodes: true
|
|
property real sipDip: 0
|
|
|
|
// Vertical travel of the body during a step. The head cancels most of it
|
|
// again, because a walking cat keeps its head almost level and the eyes
|
|
// are what the eye follows.
|
|
property real bob: 0
|
|
|
|
// The cat announces what it is doing in the machine's own language.
|
|
readonly property string moodCode: mood === "running" || mood === "chasing" ? "G00"
|
|
: mood === "walking" ? "G01"
|
|
: mood === "sitting" ? "M00"
|
|
: mood === "grooming" ? "M08"
|
|
: mood === "stretching" ? "G28"
|
|
: mood === "sleeping" ? "M30"
|
|
: mood === "drinking" ? "M01"
|
|
: ""
|
|
|
|
readonly property bool dark: coat.hslLightness < 0.45
|
|
readonly property color coatShade: Qt.darker(coat, 1.12)
|
|
readonly property color coatLight: Qt.lighter(coat, dark ? 1.35 : 1.16)
|
|
readonly property color ink: dark ? Qt.lighter(coat, 1.9) : Qt.darker(coat, 1.7)
|
|
readonly property color eyeColor: dark ? "#f7d67c" : "#2f2a36"
|
|
readonly property color pink: "#f2929f"
|
|
readonly property color blush: "#f77d95"
|
|
|
|
readonly property bool walking: mood === "walking"
|
|
readonly property bool running: mood === "running" || mood === "chasing"
|
|
readonly property bool moving: (walking || running) && !lifted
|
|
readonly property bool sleeping: mood === "sleeping"
|
|
readonly property bool happy: mood === "loving" || mood === "grooming" || mood === "stretching"
|
|
|| mood === "drinking" || petted
|
|
// The cat is drawn in a 100 x 90 box. The window box is larger so whiskers,
|
|
// tail and the glyphs above the head stay inside it in every pose. The slack
|
|
// has to be symmetric: facing left mirrors the art about the box centre.
|
|
// Widest reach is the right whiskers while stretching, art x 109.
|
|
readonly property real designWidth: 120
|
|
readonly property real designHeight: 106
|
|
readonly property real artX: 7
|
|
readonly property real artY: 14
|
|
readonly property real unit: width / designWidth
|
|
|
|
// tail pose (design units); swish is added on top by the idle animation
|
|
property real tailC1X: 14
|
|
property real tailC1Y: 66
|
|
property real tailC2X: 8
|
|
property real tailC2Y: 46
|
|
property real tailX: 13
|
|
property real tailY: 34
|
|
property real tailSwish: 0
|
|
Behavior on tailC1X { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailC1Y { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailC2X { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailC2Y { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailX { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailY { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
|
|
implicitWidth: designWidth
|
|
implicitHeight: designHeight
|
|
|
|
transform: Scale {
|
|
origin.x: cat.width / 2
|
|
xScale: cat.facingRight ? 1 : -1
|
|
}
|
|
|
|
Item {
|
|
id: art
|
|
x: cat.artX * cat.unit
|
|
y: cat.artY * cat.unit
|
|
width: 100
|
|
height: 90
|
|
transformOrigin: Item.TopLeft
|
|
scale: cat.unit
|
|
|
|
// ground shadow
|
|
Rectangle {
|
|
id: shadow
|
|
x: 24; y: 79
|
|
width: 56; height: 8
|
|
radius: 4
|
|
color: "#000000"
|
|
opacity: 0.16
|
|
antialiasing: true
|
|
scale: 1 - torso.y * -0.02
|
|
}
|
|
|
|
Item {
|
|
id: torso
|
|
width: 100
|
|
height: 90
|
|
y: cat.bob
|
|
|
|
// tail
|
|
Shape {
|
|
id: tailShape
|
|
z: -1
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
id: tail
|
|
strokeColor: cat.coatShade
|
|
strokeWidth: 7
|
|
capStyle: ShapePath.RoundCap
|
|
fillColor: "transparent"
|
|
startX: 30; startY: 60
|
|
PathCubic {
|
|
control1X: cat.tailC1X; control1Y: cat.tailC1Y
|
|
control2X: cat.tailC2X + cat.tailSwish * 0.5; control2Y: cat.tailC2Y
|
|
x: cat.tailX + cat.tailSwish; y: cat.tailY
|
|
}
|
|
}
|
|
|
|
SequentialAnimation {
|
|
running: !cat.sleeping && !cat.lifted
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { target: cat; property: "tailSwish"; to: 6; duration: 900; easing.type: Easing.InOutSine }
|
|
NumberAnimation { target: cat; property: "tailSwish"; to: -4; duration: 900; easing.type: Easing.InOutSine }
|
|
}
|
|
}
|
|
|
|
// legs — the far pair sits behind the body, the near pair in front
|
|
Repeater {
|
|
id: legs
|
|
model: 4
|
|
Rectangle {
|
|
id: leg
|
|
required property int index
|
|
readonly property bool front: index >= 2
|
|
readonly property bool far: index % 2 === 0
|
|
readonly property real swing: cat.running ? 38 : 24
|
|
readonly property real restRotation: front ? (cat.mood === "stretching" ? (far ? 48 : 40)
|
|
: cat.lifted ? (far ? -12 : -18) : 0)
|
|
: (cat.mood === "stretching" ? -6 : 0)
|
|
z: far ? 0 : 2
|
|
x: front ? (cat.seated && !cat.lifted ? 60 : 56) + (far ? 0 : 8)
|
|
: (cat.seated && !cat.lifted ? 36 : 30) + (far ? 0 : 8)
|
|
y: cat.sleeping && !cat.lifted ? 74
|
|
: (front ? (cat.seated && !cat.lifted ? 60 : 63)
|
|
: (cat.seated && !cat.lifted ? 72 : 63))
|
|
width: 10
|
|
height: cat.sleeping && !cat.lifted ? 8
|
|
: (front ? (cat.seated && !cat.lifted ? 24 : 20)
|
|
: (cat.seated && !cat.lifted ? 12 : 20))
|
|
radius: 5
|
|
color: far ? cat.coatShade : cat.coat
|
|
antialiasing: true
|
|
transformOrigin: Item.Top
|
|
|
|
Behavior on x { NumberAnimation { duration: 400; easing.type: Easing.InOutQuad } }
|
|
Behavior on y { NumberAnimation { duration: 400; easing.type: Easing.InOutQuad } }
|
|
Behavior on height { NumberAnimation { duration: 400; easing.type: Easing.InOutQuad } }
|
|
|
|
// paw
|
|
Rectangle {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
width: parent.width
|
|
height: 6.5
|
|
radius: 3.25
|
|
color: cat.coatLight
|
|
antialiasing: true
|
|
|
|
Rectangle { // toe line
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 1.2
|
|
width: 4.5
|
|
height: 1
|
|
radius: 0.5
|
|
color: cat.coatShade
|
|
opacity: 0.7
|
|
visible: !leg.far && !cat.sleeping
|
|
}
|
|
}
|
|
|
|
// While walking the gait animation owns rotation; otherwise the pose does.
|
|
states: State {
|
|
name: "resting"
|
|
when: !cat.moving
|
|
PropertyChanges { leg.rotation: leg.restRotation }
|
|
}
|
|
transitions: Transition {
|
|
NumberAnimation { property: "rotation"; duration: 400; easing.type: Easing.OutBack }
|
|
}
|
|
|
|
SequentialAnimation on rotation {
|
|
running: cat.moving
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { to: leg.far ? leg.swing : -leg.swing; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: leg.far ? -leg.swing : leg.swing; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
}
|
|
}
|
|
}
|
|
|
|
// body
|
|
Rectangle {
|
|
id: body
|
|
z: 1
|
|
x: 24; y: 46
|
|
width: 50; height: 32
|
|
radius: 16
|
|
color: cat.coat
|
|
antialiasing: true
|
|
|
|
Behavior on x { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on y { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on width { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on height { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on rotation { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
|
|
Rectangle { // belly
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 3
|
|
width: parent.width * 0.5
|
|
height: parent.height * 0.42
|
|
radius: height / 2
|
|
color: cat.coatLight
|
|
antialiasing: true
|
|
}
|
|
}
|
|
|
|
// head
|
|
Item {
|
|
id: head
|
|
z: 3
|
|
x: 46; y: 12
|
|
width: 50; height: 44
|
|
transformOrigin: Item.Bottom
|
|
transform: Translate { y: -cat.bob * 0.75 }
|
|
|
|
Behavior on x { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on y { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on rotation { NumberAnimation { duration: 450; easing.type: Easing.InOutBack } }
|
|
|
|
// ears
|
|
Shape {
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath { // left ear
|
|
fillColor: cat.coat
|
|
strokeColor: "transparent"
|
|
startX: 6; startY: 14
|
|
PathQuad { controlX: 2; controlY: -4; x: 8; y: -4 }
|
|
PathQuad { controlX: 14; controlY: -2; x: 24; y: 5 }
|
|
PathLine { x: 6; y: 14 }
|
|
}
|
|
ShapePath { // left inner
|
|
fillColor: cat.pink
|
|
strokeColor: "transparent"
|
|
startX: 9; startY: 11
|
|
PathQuad { controlX: 6; controlY: 2; x: 9; y: 1 }
|
|
PathQuad { controlX: 14; controlY: 3; x: 19; y: 6 }
|
|
PathLine { x: 9; y: 11 }
|
|
}
|
|
ShapePath { // right ear
|
|
fillColor: cat.coat
|
|
strokeColor: "transparent"
|
|
startX: 44; startY: 14
|
|
PathQuad { controlX: 48; controlY: -4; x: 42; y: -4 }
|
|
PathQuad { controlX: 36; controlY: -2; x: 26; y: 5 }
|
|
PathLine { x: 44; y: 14 }
|
|
}
|
|
ShapePath { // right inner
|
|
fillColor: cat.pink
|
|
strokeColor: "transparent"
|
|
startX: 41; startY: 11
|
|
PathQuad { controlX: 44; controlY: 2; x: 41; y: 1 }
|
|
PathQuad { controlX: 36; controlY: 3; x: 31; y: 6 }
|
|
PathLine { x: 41; y: 11 }
|
|
}
|
|
}
|
|
|
|
Rectangle { // face
|
|
x: 0; y: 4
|
|
width: 50; height: 40
|
|
radius: 21
|
|
color: cat.coat
|
|
antialiasing: true
|
|
}
|
|
|
|
// cheeks
|
|
Repeater {
|
|
model: 2
|
|
Rectangle {
|
|
required property int index
|
|
x: index === 0 ? 5 : 35
|
|
y: 30
|
|
width: 10; height: 5.5
|
|
radius: 3
|
|
color: cat.blush
|
|
opacity: cat.happy ? 0.75 : 0.4
|
|
antialiasing: true
|
|
Behavior on opacity { NumberAnimation { duration: 300 } }
|
|
}
|
|
}
|
|
|
|
// eyes
|
|
Repeater {
|
|
model: 2
|
|
Item {
|
|
id: eye
|
|
required property int index
|
|
x: (index === 0 ? 12 : 29) + cat.look.x * 2.2
|
|
y: 17 + cat.look.y * 1.6
|
|
width: 9.5; height: 12
|
|
|
|
Rectangle {
|
|
id: eyeBall
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: eye.width * (cat.mood === "alert" || cat.mood === "batting" ? 1.15 : 1)
|
|
height: eye.height * cat.eyeOpen * (cat.mood === "alert" || cat.mood === "batting" ? 1.15 : 1)
|
|
radius: width / 2
|
|
color: cat.eyeColor
|
|
visible: !cat.eyesShut && cat.eyeOpen > 0.02
|
|
antialiasing: true
|
|
|
|
Rectangle { // highlight
|
|
x: parent.width * 0.5; y: parent.height * 0.14
|
|
width: 3.4; height: 3.4 * cat.eyeOpen
|
|
radius: 1.7
|
|
color: "white"
|
|
antialiasing: true
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.22; y: parent.height * 0.6
|
|
width: 1.6; height: 1.6 * cat.eyeOpen
|
|
radius: 0.8
|
|
color: "white"
|
|
antialiasing: true
|
|
}
|
|
}
|
|
|
|
Shape { // closed eye: happy arch or sleepy curve
|
|
anchors.fill: parent
|
|
visible: cat.eyesShut
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
strokeColor: cat.ink
|
|
strokeWidth: 1.8
|
|
capStyle: ShapePath.RoundCap
|
|
fillColor: "transparent"
|
|
startX: 0.5; startY: 7
|
|
PathQuad {
|
|
controlX: 4.75
|
|
controlY: cat.happy ? 2 : 10
|
|
x: 9; y: 7
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// nose
|
|
Rectangle {
|
|
x: 22.5; y: 28
|
|
width: 5; height: 3.6
|
|
radius: 1.8
|
|
color: cat.pink
|
|
antialiasing: true
|
|
}
|
|
|
|
// mouth "ω"
|
|
Shape {
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
strokeColor: cat.ink
|
|
strokeWidth: 1.1
|
|
capStyle: ShapePath.RoundCap
|
|
fillColor: "transparent"
|
|
startX: 20.5; startY: 32
|
|
PathQuad { controlX: 22.5; controlY: 35.2; x: 25; y: 32.4 }
|
|
PathQuad { controlX: 27.5; controlY: 35.2; x: 29.5; y: 32 }
|
|
}
|
|
}
|
|
|
|
// whiskers
|
|
Repeater {
|
|
model: 4
|
|
Rectangle {
|
|
required property int index
|
|
readonly property bool leftSide: index < 2
|
|
x: leftSide ? -7 : 45
|
|
y: 25 + (index % 2) * 4.5
|
|
width: 12
|
|
height: 1
|
|
radius: 0.5
|
|
color: cat.ink
|
|
opacity: 0.55
|
|
rotation: (leftSide ? 1 : -1) * ((index % 2) === 0 ? 7 : -7)
|
|
antialiasing: true
|
|
}
|
|
}
|
|
|
|
// raised paw while grooming
|
|
Rectangle {
|
|
x: 8; y: 26
|
|
width: 10; height: 16
|
|
radius: 5
|
|
color: cat.coat
|
|
antialiasing: true
|
|
visible: cat.mood === "grooming"
|
|
transformOrigin: Item.Bottom
|
|
SequentialAnimation on rotation {
|
|
running: cat.mood === "grooming"
|
|
loops: Animation.Infinite
|
|
NumberAnimation { to: 14; duration: 260; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: -6; duration: 260; easing.type: Easing.InOutSine }
|
|
}
|
|
Rectangle {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
y: 0
|
|
width: parent.width; height: 6
|
|
radius: 3
|
|
color: cat.coatLight
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// purr wiggle while petted / loving
|
|
SequentialAnimation on rotation {
|
|
running: cat.petted || cat.mood === "loving"
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { to: 2.5; duration: 180; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: -2.5; duration: 180; easing.type: Easing.InOutSine }
|
|
}
|
|
}
|
|
|
|
// Machine code tag, flashed in the headroom whenever the mood changes
|
|
Item {
|
|
id: codeTag
|
|
x: 16; y: -12
|
|
width: codeBg.width
|
|
height: codeBg.height
|
|
opacity: 0
|
|
visible: cat.showCodes && opacity > 0.01
|
|
transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: codeTag.width / 2 }
|
|
z: 10
|
|
Accessible.ignored: true
|
|
|
|
Rectangle {
|
|
id: codeBg
|
|
width: codeText.implicitWidth + 8
|
|
height: codeText.implicitHeight + 4
|
|
radius: 3
|
|
color: "#242833"
|
|
opacity: 0.92
|
|
antialiasing: true
|
|
|
|
Text {
|
|
id: codeText
|
|
anchors.centerIn: parent
|
|
text: cat.moodCode
|
|
color: "#8fd6ff"
|
|
font.pixelSize: 10
|
|
font.bold: true
|
|
font.family: "Menlo"
|
|
}
|
|
}
|
|
|
|
SequentialAnimation {
|
|
id: codeFlash
|
|
NumberAnimation { target: codeTag; property: "opacity"; to: 1; duration: 160 }
|
|
PauseAnimation { duration: 1500 }
|
|
NumberAnimation { target: codeTag; property: "opacity"; to: 0; duration: 500 }
|
|
}
|
|
}
|
|
|
|
// speech bubble: "!" / "z" / "♥"
|
|
Text {
|
|
id: bubble
|
|
readonly property string glyph: cat.mood === "alert" || cat.mood === "batting" ? "!"
|
|
: cat.sleeping ? "z"
|
|
: (cat.mood === "loving" || cat.petted) ? "♥" : ""
|
|
x: 82; y: 4
|
|
text: glyph
|
|
visible: glyph.length > 0
|
|
font.pixelSize: cat.mood === "alert" || cat.mood === "batting" ? 22 : 16
|
|
font.bold: true
|
|
font.family: "Helvetica Neue"
|
|
color: cat.mood === "alert" || cat.mood === "batting" ? "#f2a33c"
|
|
: (glyph === "♥" ? "#ff5b7f" : cat.ink)
|
|
transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: bubble.width / 2 }
|
|
Accessible.ignored: true
|
|
|
|
SequentialAnimation on y {
|
|
running: bubble.visible
|
|
loops: Animation.Infinite
|
|
NumberAnimation { from: 10; to: -8; duration: cat.sleeping ? 1800 : 900; easing.type: Easing.OutQuad }
|
|
}
|
|
SequentialAnimation on opacity {
|
|
running: bubble.visible
|
|
loops: Animation.Infinite
|
|
NumberAnimation { from: 0; to: 1; duration: cat.sleeping ? 500 : 250 }
|
|
NumberAnimation { to: 0; duration: cat.sleeping ? 1300 : 650 }
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
onMoodCodeChanged: if (showCodes && moodCode.length > 0) codeFlash.restart()
|
|
|
|
SequentialAnimation {
|
|
running: cat.moving
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { target: cat; property: "bob"; to: cat.running ? -3 : -1.6; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
NumberAnimation { target: cat; property: "bob"; to: 0; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
}
|
|
|
|
// One pair of eyelids for both eyes. Two independent blink timers made the
|
|
// cat wink at random, which read as the eyes jittering.
|
|
readonly property bool eyesShut: sleeping || happy
|
|
property real eyeOpen: 1
|
|
|
|
onEyesShutChanged: {
|
|
blinkSeq.stop()
|
|
lidAnim.to = eyesShut ? 0 : 1
|
|
lidAnim.restart()
|
|
}
|
|
|
|
NumberAnimation {
|
|
id: lidAnim
|
|
target: cat
|
|
property: "eyeOpen"
|
|
duration: 140
|
|
easing.type: Easing.InOutQuad
|
|
}
|
|
|
|
SequentialAnimation {
|
|
id: blinkSeq
|
|
NumberAnimation { target: cat; property: "eyeOpen"; to: 0.06; duration: 90; easing.type: Easing.InQuad }
|
|
NumberAnimation { target: cat; property: "eyeOpen"; to: 1; duration: 140; easing.type: Easing.OutQuad }
|
|
}
|
|
|
|
Timer {
|
|
id: blinkTimer
|
|
interval: 3600
|
|
running: !cat.eyesShut && !cat.lifted
|
|
repeat: true
|
|
onTriggered: { interval = 3200 + Math.random() * 4200; blinkSeq.start() }
|
|
Component.onCompleted: interval = 2000 + Math.random() * 4000
|
|
}
|
|
|
|
// Head dips into the mug, holds, comes back up.
|
|
SequentialAnimation {
|
|
running: cat.mood === "drinking" && !cat.lifted
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { target: cat; property: "sipDip"; to: 13; duration: 700; easing.type: Easing.InOutQuad }
|
|
PauseAnimation { duration: 900 }
|
|
NumberAnimation { target: cat; property: "sipDip"; to: 0; duration: 600; easing.type: Easing.InOutQuad }
|
|
PauseAnimation { duration: 1400 }
|
|
}
|
|
|
|
states: [
|
|
State {
|
|
name: "running"
|
|
when: cat.running && !cat.lifted
|
|
PropertyChanges { body.rotation: -5; head.y: 15; head.x: 48 }
|
|
PropertyChanges { cat.tailC1X: 8; cat.tailC1Y: 60; cat.tailC2X: 4; cat.tailC2Y: 56; cat.tailX: 0; cat.tailY: 52 }
|
|
},
|
|
State {
|
|
name: "sitting"
|
|
when: (cat.mood === "sitting" || cat.mood === "loving" || cat.mood === "alert") && !cat.lifted
|
|
PropertyChanges { body.x: 34; body.y: 40; body.width: 40; body.height: 40 }
|
|
PropertyChanges { head.y: 8; head.x: 40 }
|
|
PropertyChanges { cat.tailC1X: 26; cat.tailC1Y: 84; cat.tailC2X: 40; cat.tailC2Y: 88; cat.tailX: 52; cat.tailY: 80 }
|
|
},
|
|
State {
|
|
name: "grooming"
|
|
when: cat.mood === "grooming" && !cat.lifted
|
|
PropertyChanges { body.x: 34; body.y: 40; body.width: 40; body.height: 40 }
|
|
PropertyChanges { head.y: 10; head.x: 38; head.rotation: -10 }
|
|
PropertyChanges { cat.tailC1X: 26; cat.tailC1Y: 84; cat.tailC2X: 40; cat.tailC2Y: 88; cat.tailX: 52; cat.tailY: 80 }
|
|
},
|
|
State {
|
|
name: "stretching"
|
|
when: cat.mood === "stretching" && !cat.lifted
|
|
PropertyChanges { body.x: 16; body.y: 50; body.width: 64; body.height: 26; body.rotation: -8 }
|
|
PropertyChanges { head.y: 32; head.x: 52; head.rotation: 7 }
|
|
PropertyChanges { cat.tailC1X: 10; cat.tailC1Y: 48; cat.tailC2X: 6; cat.tailC2Y: 30; cat.tailX: 14; cat.tailY: 22 }
|
|
},
|
|
State {
|
|
name: "sleeping"
|
|
when: cat.sleeping && !cat.lifted
|
|
PropertyChanges { body.x: 22; body.y: 54; body.width: 56; body.height: 26 }
|
|
PropertyChanges { head.y: 24; head.x: 44; head.rotation: 4 }
|
|
PropertyChanges { cat.tailC1X: 24; cat.tailC1Y: 86; cat.tailC2X: 60; cat.tailC2Y: 90; cat.tailX: 76; cat.tailY: 78 }
|
|
},
|
|
State {
|
|
name: "drinking"
|
|
when: cat.mood === "drinking" && !cat.lifted
|
|
PropertyChanges { body.x: 32; body.y: 42; body.width: 42; body.height: 38 }
|
|
PropertyChanges { head.x: 50; head.y: 20 + cat.sipDip; head.rotation: 16 }
|
|
PropertyChanges { cat.tailC1X: 26; cat.tailC1Y: 84; cat.tailC2X: 40; cat.tailC2Y: 88; cat.tailX: 52; cat.tailY: 80 }
|
|
},
|
|
State {
|
|
name: "batting"
|
|
when: cat.mood === "batting" && !cat.lifted
|
|
PropertyChanges { body.rotation: -10; body.x: 26 }
|
|
PropertyChanges { head.y: 10; head.x: 50 }
|
|
},
|
|
State {
|
|
name: "lifted"
|
|
when: cat.lifted
|
|
PropertyChanges { torso.rotation: 8; torso.y: -6 }
|
|
PropertyChanges { body.x: 28; body.y: 40; body.width: 42; body.height: 40 }
|
|
PropertyChanges { head.y: 8; head.x: 40 }
|
|
PropertyChanges { cat.tailC1X: 22; cat.tailC1Y: 74; cat.tailC2X: 18; cat.tailC2Y: 86; cat.tailX: 24; cat.tailY: 92 }
|
|
PropertyChanges { shadow.opacity: 0.06; shadow.scale: 0.7 }
|
|
}
|
|
]
|
|
|
|
transitions: Transition {
|
|
NumberAnimation { target: torso; properties: "rotation,y"; duration: 260; easing.type: Easing.OutBack }
|
|
NumberAnimation { target: shadow; properties: "opacity,scale"; duration: 260 }
|
|
}
|
|
|
|
readonly property bool seated: mood === "sitting" || mood === "loving" || mood === "alert"
|
|
|| mood === "grooming" || mood === "drinking"
|
|
}
|