这篇讲三个改滚轮组件样式的方法,共同的主角是一个叫 displacement 的属性------它表示每个滚轮项离选中项有多远。抓住它,透明度、高亮条、立体感都是顺手的事。
- 透明度渐变 --- 边缘项半透明,滚到中间最清晰
- 高亮背景 --- 在选中位置自己画一个高亮条
- 3D 旋转 --- 绕 X 轴旋转加缩放,滚轮有了立体感
Demo 1:透明度渐变
滚轮转起来的时候,靠近边缘的数字淡下去,中间的选项最醒目。效果像被灯光聚焦一样。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Tumbler {
id: tumbler
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 200
Layout.preferredHeight: 200
model: 100
delegate: Text {
text: modelData
color: Tumbler.tumbler.currentIndex === index ? "red" : "black"
font.pixelSize: 20
font.bold: Tumbler.tumbler.currentIndex === index
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 0.4 + Math.max(0, 1 - Math.abs(Tumbler.displacement)) * 0.6
}
}
Text {
Layout.alignment: Qt.AlignHCenter
text: "当前选择: " + tumbler.currentIndex
font.pointSize: 13
color: "#1296FF"
font.bold: true
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
Tumbler.displacement 是每个滚轮项都有的属性:选中项是 0,往上往下依次是 ±1、±2......它时刻跟着滚动更新,所以是驱动各种滚动效果的天然信号。
透明度渐变靠这一行:
qml
opacity: 0.4 + Math.max(0, 1 - Math.abs(Tumbler.displacement)) * 0.6
拆开看就一句话:离选中项越远,透明度越低。1 - Math.abs(displacement) 在选中项上是 1(完全不透明),隔一项变 0.5,隔两项是 0,再用 Math.max(0, ...) 把负数掐掉。最外面乘 0.6 加 0.4,是给透明度留个底------滚出视野的项也不会完全消失,保持一点存在感。
选中项另外用 currentIndex === index 染成红色加粗,和透明度的逻辑是独立的:一个管颜色,一个管浓淡。
Demo 2:高亮背景
Tumbler 自带的选中指示是一条细细的线,这里换成一块半透明的绿色色块,盖在选中位置上,选中的效果一下子明显了。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Tumbler {
id: tumbler
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 200
Layout.preferredHeight: 200
model: ["S", "M", "L", "XL", "XXL"]
// 手动添加高亮矩形
Rectangle {
parent: tumbler.contentItem
width: tumbler.width
height: tumbler.height / tumbler.visibleItemCount
color: "#21be2b"
opacity: 0.3
radius: 5
y: Math.floor(tumbler.height / 2 - height / 2) // 固定在中间
}
}
Text {
Layout.alignment: Qt.AlignHCenter
text: "当前选择: " + tumbler.model[tumbler.currentIndex]
font.pointSize: 13
color: "#21be2b"
font.bold: true
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
这个高亮条不是 Tumbler 提供的现成样式,而是我们自己往它内部塞的一个 Rectangle。关键是塞的位置:parent: tumbler.contentItem------contentItem 是滚轮的内部内容区,选中高亮就画在它上面,会跟着滚轮一起走。
位置怎么算?滚轮总高度除以可视项数,就是每一项的高度:
qml
height: tumbler.height / tumbler.visibleItemCount
y: Math.floor(tumbler.height / 2 - height / 2)
第一条算出一项有多高,第二条把矩形挪到正中------height / 2 是滚轮的一半,再减去矩形自身一半,正好居中。滚轮的高度用 tumbler.height 而不是写死,改成什么尺寸都能自适应。
和上一篇文章的思路对比一下:displacement 是"跟着某项走",这个是"钉在中间不动"。做法不同,效果都是让当前选中项一眼可辨。
Demo 3:3D 旋转
把滚轮数字绕 X 轴转一个角度,越靠近边缘转得越厉害,配合缩放,看起来就像在旋转的圆筒上,有了立体的感觉。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
// 浅色背景容器
Rectangle {
anchors.fill: parent
color: "#F2F2F2"
radius: 8
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Tumbler {
id: tumbler
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 200
Layout.preferredHeight: 200
Rectangle {
anchors.fill: parent
color: "#FFFFFF"
radius: 5
z: -1
}
model: 50
delegate: Text {
id: delegateText
text: modelData
color: "#333"
font.pixelSize: 16 + (1 - Math.abs(delegateText.Tumbler.displacement)) * 10
font.bold: delegateText.Tumbler.tumbler.currentIndex === index
opacity: 0.5 + (1 - Math.abs(delegateText.Tumbler.displacement)) * 0.5
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: 100 // 确保文本居中
transform: [
Rotation {
origin.x: width / 2
origin.y: height / 2
axis { x: 1; y: 0; z: 0 }
angle: delegateText.Tumbler.displacement * 30
},
Scale {
origin.x: width / 2
origin.y: height / 2
xScale: 1.0 - Math.abs(delegateText.Tumbler.displacement) * 0.2
yScale: 1.0 - Math.abs(delegateText.Tumbler.displacement) * 0.2
}
]
}
}
Text {
Layout.alignment: Qt.AlignHCenter
text: "当前选择: " + tumbler.currentIndex
font.pointSize: 13
color: "#1296FF"
font.bold: true
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
立体感的来源是 transform 数组------一个项可以叠多个变换,这里放了两个:
qml
transform: [
Rotation { axis { x: 1; y: 0; z: 0 } angle: displacement * 30 },
Scale { xScale/yScale: 1 - |displacement| * 0.2 }
]
Rotation 绕 X 轴转:轴是水平的,所以数字像门板一样前后翻转。旋转角度跟着 displacement 走,选中项转 0 度正对屏幕,越靠边的项转得越偏。Scale 再补一刀:离得远的项缩小一点。旋转加缩放,圆筒滚动的错觉就出来了。
font.pixelSize 和 opacity 也用了同样的套路,边缘的字更小、更淡。三个 demo 看下来会发现,样式定制的思路是统一的:拿 displacement 当输入,透明度、角度、大小都是它的函数。
对比
| 样式 | 手法 | 适合场景 |
|---|---|---|
| 透明度渐变 | opacity 随 displacement 变化 |
数字多、想突出选中项的列表 |
| 高亮背景 | 在 contentItem 里画矩形 |
选项少、需要一眼看到选中位置 |
| 3D 旋转 | Rotation + Scale 变换 |
想要滚轮有立体质感 |
运行验证
- 用 Qt Creator 打开
qml_tumbler/CMakeLists.txt; - 按
Ctrl+R运行; - 左侧导航切到"透明度渐变""高亮背景""3D 旋转",滚动滚轮观察效果差异。
扩展复用方向
- 透明度渐变的公式可以套到字体大小上,做出"中间大两边小"的放大镜效果。
- 高亮条的颜色和圆角随手改,配合品牌色用。
- 3D 效果的旋转角度改大一点、缩放比例调小,会更有"景深",但别过头,太夸张反而看不清文字。
已验证环境:
- Qt 版本:Qt 6.8.2 / Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_tumbler