Watch
1
0
Fork
You've already forked souveraine
0

shell: refine AirPods opening card

This commit is contained in:
Fimeg 2026-08-13 21:02:31 -04:00
commit bfd48fcf8f

View file

@ -64,8 +64,8 @@ Scope {
id: card
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: Math.min(parent.width - 32, 368)
height: 316
width: Math.min(parent.width - 36, 356)
height: 292
opacity: scope.canShow ? 1 : 0
scale: scope.canShow ? 1 : 0.94
@ -74,8 +74,8 @@ Scope {
Rectangle {
anchors.fill: parent
radius: 36
color: "#e81a1c21"
radius: 32
color: "#eb15171c"
border.width: 1
border.color: "#3ffffffF"
}
@ -83,16 +83,16 @@ Scope {
Rectangle {
anchors.fill: parent
anchors.margins: 1
radius: 35
radius: 31
gradient: Gradient {
GradientStop { position: 0; color: "#f425282f" }
GradientStop { position: 1; color: "#f1101115" }
GradientStop { position: 0; color: "#f42b3039" }
GradientStop { position: 1; color: "#f10f1014" }
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 25
anchors.margins: 23
spacing: 0
RowLayout {
@ -100,7 +100,7 @@ Scope {
Text {
text: AccessoryPresentation.label
color: "#f7f7f8"
font.pixelSize: 21
font.pixelSize: 20
font.weight: Font.DemiBold
}
Item { Layout.fillWidth: true }
@ -114,8 +114,8 @@ Scope {
Item {
id: stage
Layout.fillWidth: true
Layout.preferredHeight: 178
Layout.topMargin: 7
Layout.preferredHeight: 157
Layout.topMargin: 3
property real open: 0
property int seenGeneration: -1
@ -133,78 +133,163 @@ Scope {
SequentialAnimation {
id: reveal
NumberAnimation { target: stage; property: "open"; to: 1; duration: 720; easing.type: Easing.OutCubic }
NumberAnimation { target: stage; property: "open"; to: 1; duration: 620; easing.type: Easing.OutQuart }
}
// A quiet pool of light beneath the case makes the
// device feel present without importing artwork.
// Keep the object light, not illustrated. The pool
// gives the white case a place to sit without turning
// the card into a product render.
Rectangle {
width: 238; height: 48; radius: height / 2
width: 218; height: 36; radius: height / 2
anchors.horizontalCenter: parent.horizontalCenter
y: 126
color: "#263a8dff"
opacity: 0.35 * stage.open
scale: 0.72 + 0.28 * stage.open
y: 115
color: "#2c70e8"
opacity: 0.20 * stage.open
scale: 0.76 + 0.24 * stage.open
Behavior on opacity { NumberAnimation { duration: 420 } }
}
Item {
id: caseArt
width: 206; height: 154
width: 190; height: 146
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
// Buds rise after the lid clears them.
// The old 3-D lid was a flat white slab in a real
// layer surface. This is deliberately a clear 2-D
// silhouette: lid lifts on its hinge, buds clear
// the cavity, then the front of the case settles.
Item {
id: leftBud
x: 39; y: 47 - 20 * stage.open
opacity: Math.max(0, (stage.open - 0.22) / 0.78)
scale: 0.80 + 0.20 * stage.open
Rectangle { width: 39; height: 66; radius: 19; color: "#f4f6f8" }
Rectangle { width: 14; height: 43; radius: 7; color: "#f4f6f8"; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 45 }
Rectangle { width: 12; height: 4; radius: 2; color: "#c9d0d8"; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 54 }
z: 2
width: 31; height: 89
x: 34; y: 39 - 16 * stage.open
opacity: Math.max(0, (stage.open - 0.12) / 0.88)
scale: 0.84 + 0.16 * stage.open
rotation: -5 * stage.open
transformOrigin: Item.Bottom
Rectangle {
width: 31; height: 43; radius: 16
color: "#f7f9fb"
border.width: 1; border.color: "#ffffff"
}
Rectangle {
width: 11; height: 47; radius: 6
x: 10; y: 31
color: "#f7f9fb"
}
Rectangle {
width: 7; height: 3; radius: 2
x: 12; y: 55
color: "#bfc8d1"
}
Rectangle {
width: 6; height: 6; radius: 3
x: 6; y: 17
color: "#d1d8e0"
}
}
Item {
id: rightBud
x: 128; y: 47 - 20 * stage.open
opacity: Math.max(0, (stage.open - 0.22) / 0.78)
scale: 0.80 + 0.20 * stage.open
Rectangle { width: 39; height: 66; radius: 19; color: "#f4f6f8" }
Rectangle { width: 14; height: 43; radius: 7; color: "#f4f6f8"; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 45 }
Rectangle { width: 12; height: 4; radius: 2; color: "#c9d0d8"; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 54 }
z: 2
width: 31; height: 89
x: 125; y: 39 - 16 * stage.open
opacity: Math.max(0, (stage.open - 0.12) / 0.88)
scale: 0.84 + 0.16 * stage.open
rotation: 5 * stage.open
transformOrigin: Item.Bottom
Rectangle {
width: 31; height: 43; radius: 16
color: "#f7f9fb"
border.width: 1; border.color: "#ffffff"
}
Rectangle {
width: 11; height: 47; radius: 6
x: 10; y: 31
color: "#f7f9fb"
}
Rectangle {
width: 7; height: 3; radius: 2
x: 12; y: 55
color: "#bfc8d1"
}
Rectangle {
width: 6; height: 6; radius: 3
x: 19; y: 17
color: "#d1d8e0"
}
}
// Case body. The lid rotates up from its hinge as
// the admitted event lands, then waits in that pose.
Rectangle {
id: caseBody
width: 174; height: 94; radius: 43
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
color: "#f2f4f6"
z: 4
width: 174; height: 68; radius: 34
x: 8; y: 78
gradient: Gradient {
GradientStop { position: 0; color: "#ffffff" }
GradientStop { position: 1; color: "#e9edf1" }
}
border.width: 1
border.color: "#ffffff"
// A small, shadowed mouth makes the buds read
// as nested in a case rather than pasted on it.
Rectangle {
width: 150; height: 29; radius: 15
x: 12; y: 5
color: "#ccd3db"
opacity: 0.14 + 0.52 * stage.open
}
Rectangle {
width: 146; height: 1; radius: 1
x: 14; y: 28
color: "#c8d0d8"
opacity: 0.55
}
Rectangle {
width: 7; height: 7; radius: 4
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
y: 42
color: "#58d979"
opacity: 0.45 + 0.55 * stage.open
}
}
Rectangle {
id: lid
width: 174; height: 69; radius: 40
anchors.horizontalCenter: parent.horizontalCenter
y: 58
color: "#fafbfd"
// It is the rear half of an open case: behind
// the buds, not a white bar painted across
// their faces.
z: 1
width: 166; height: 58 - 17 * stage.open; radius: height / 2
// Closed, the lid meets the front shell. As it
// opens, the hinge rises just enough to leave
// the buds a visible throat of air.
x: 12; y: 78 - 8 * stage.open - height
rotation: -4 * stage.open
transformOrigin: Item.Bottom
gradient: Gradient {
GradientStop { position: 0; color: "#ffffff" }
GradientStop { position: 1; color: "#edf1f5" }
}
border.width: 1
border.color: "#ffffff"
transform: Rotation {
origin.x: lid.width / 2
origin.y: lid.height
axis.x: 1; axis.y: 0; axis.z: 0
angle: -73 * stage.open
Rectangle {
width: 126; height: 1; radius: 1
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 2
color: "#d8dee5"
opacity: 0.62
}
Rectangle {
width: 136; height: 13; radius: 7
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 6
color: "#dbe1e7"
opacity: 0.50
}
}
}
@ -212,8 +297,8 @@ Scope {
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 8
spacing: 10
Layout.topMargin: 7
spacing: 8
ChargePill { Layout.fillWidth: true; label: "Left"; level: AccessoryPresentation.leftCharge }
ChargePill { Layout.fillWidth: true; label: "Case"; level: AccessoryPresentation.caseCharge }
ChargePill { Layout.fillWidth: true; label: "Right"; level: AccessoryPresentation.rightCharge }
@ -226,8 +311,8 @@ Scope {
component ChargePill: Rectangle {
required property string label
required property int level
implicitHeight: 45
radius: 15
implicitHeight: 42
radius: 14
color: "#1bffffff"
border.width: 1
border.color: "#24ffffff"