QML Tumbler 样式定制:渐变、高亮与 3D 滚轮效果

这篇讲三个改滚轮组件样式的方法,共同的主角是一个叫 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.pixelSizeopacity 也用了同样的套路,边缘的字更小、更淡。三个 demo 看下来会发现,样式定制的思路是统一的:拿 displacement 当输入,透明度、角度、大小都是它的函数。

对比

样式 手法 适合场景
透明度渐变 opacitydisplacement 变化 数字多、想突出选中项的列表
高亮背景 contentItem 里画矩形 选项少、需要一眼看到选中位置
3D 旋转 Rotation + Scale 变换 想要滚轮有立体质感

运行验证

  1. 用 Qt Creator 打开 qml_tumbler/CMakeLists.txt
  2. Ctrl+R 运行;
  3. 左侧导航切到"透明度渐变""高亮背景""3D 旋转",滚动滚轮观察效果差异。

扩展复用方向

  • 透明度渐变的公式可以套到字体大小上,做出"中间大两边小"的放大镜效果。
  • 高亮条的颜色和圆角随手改,配合品牌色用。
  • 3D 效果的旋转角度改大一点、缩放比例调小,会更有"景深",但别过头,太夸张反而看不清文字。

已验证环境

相关推荐
隐积13 小时前
3.1.7.Qt容器大家族(3):QVariant——万能盒子
开发语言·qt
熬夜苦读学习1 天前
Qt-- 系统相关
开发语言·qt
Quz1 天前
QML Tumbler 基础用法:多滚轮选择与自定义外观
qt
Quz1 天前
QML SpinBox 动画效果:缩放反馈与数字滚动
qt
小灰灰搞电子2 天前
Qt 实现魔法导航菜单源码分享
开发语言·qt
Quz2 天前
QML SpinBox 基础与步长控制:可编辑输入与自定义步长
qt
隐积2 天前
Qt魔法之旅 · 全系列课程导航
qt
海清河晏1112 天前
Qt 实战:信号与槽+事件系统
开发语言·c++·qt
郝学胜-神的一滴2 天前
Qt 高级编程 037:QSS按钮样式封神指南
开发语言·c++·qt·软件工程·用户界面