How to make slider with image
Unsolved
QML and Qt Quick
-
@linhnobi here a very basic example of @johngod suggestion:
ApplicationWindow { width: 640 height: 640 visible: true title: "Simple Audio Slider Example" ColumnLayout { width: parent.width Slider { id: audioSlider value: audioTrack.progressValue Layout.preferredHeight: 100 Layout.fillWidth: true Text { id: progressTime anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.margins: 10 color: "white" text: formatInterval(audioTrack.position) } Text { id: totalTrackDuration anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.margins: 10 color: "white" text: formatInterval(audioTrack.duration) } onValueChanged: Qt.callLater(function () { audioTrack.seek(value * audioTrack.duration) }) Audio { id: audioTrack autoPlay: true property double progressValue: position / duration source: "https://upload.wikimedia.org/wikipedia/commons/6/63/Sagetyrtle_-_citystreet3_%28cc0%29_%28freesound%29.mp3" } background: Rectangle { // optional change Rectangle to Image id: backgroundImage width: audioSlider.width height: audioSlider.height ColorOverlay { id: greyOverlay anchors.fill: backgroundImage source: backgroundImage color: "#80000000" } Rectangle { width: audioSlider.visualPosition * parent.width height: parent.height color: "orange" opacity: 0.3 } } handle: Item { visible: false } } RowLayout { Button { text: "Play" onClicked: audioTrack.play() } Button { text: "Pause" onClicked: audioTrack.pause() } } } function formatInterval(interval) { let seconds = Math.floor(interval / 1000) let hours = Math.floor(seconds / 3600) let minutes = Math.floor((seconds % 3600) / 60) seconds = seconds % 60 const formattedHours = hours.toString().padStart(2, '0') const formattedMinutes = minutes.toString().padStart(2, '0') const formattedSeconds = seconds.toString().padStart(2, '0') return hours > 0 ? `${formattedHours}:${formattedMinutes}:${formattedSeconds}` : `${formattedMinutes}:${formattedSeconds}` } }
-
@linhnobi the easiest solution would be to add a grey rectangle with transparency over the not played track part (replacing the orange rectangle of already played audio). But this approach would lead to a large grey area above the entire slider and colors still being visible.
the better solution is to use a ShaderEffect, which grey scales the unplayed area based on the audio position.
Here the updated example:ApplicationWindow { width: 640 height: 640 visible: true title: "Simple Audio Slider Example" ColumnLayout { width: parent.width Slider { id: audioSlider value: audioTrack.progressValue Layout.preferredHeight: 100 Layout.fillWidth: true Rectangle { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.margins: 10 color: "grey" opacity: 0.8 radius: 2 width: childrenRect.width height: childrenRect.height Text { id: progressTime padding: 5 color: "white" text: formatInterval(audioTrack.position) } } Rectangle { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.margins: 10 color: "grey" opacity: 0.8 radius: 2 width: childrenRect.width height: childrenRect.height Text { id: totalTrackDuration padding: 5 color: "white" text: formatInterval(audioTrack.duration) } } onValueChanged: Qt.callLater(function () { audioTrack.seek(value * audioTrack.duration) }) Audio { id: audioTrack autoPlay: true property double progressValue: position / duration source: "https://upload.wikimedia.org/wikipedia/commons/6/63/Sagetyrtle_-_citystreet3_%28cc0%29_%28freesound%29.mp3" } background: Image { id: backgroundImage width: audioSlider.width height: audioSlider.height fillMode: Image.PreserveAspectCrop source: "https://ddgobkiprc33d.cloudfront.net/623e4d4f-c295-4b8c-940c-cf050a46cec4.png" ShaderEffect { anchors.fill: parent property variant source: backgroundImage property real breakpoint: audioSlider.visualPosition fragmentShader: " uniform sampler2D source; uniform highp float breakpoint; varying highp vec2 qt_TexCoord0; void main() { highp vec4 p = texture2D(source, qt_TexCoord0); if (qt_TexCoord0.x > breakpoint) { highp float grey = dot(p.xyz, vec3(0.299, 0.587, 0.114)); p = vec4(vec3(grey), p.w); } gl_FragColor = p; }" } } handle: Item { visible: false } } RowLayout { Button { text: "Play" onClicked: audioTrack.play() } Button { text: "Pause" onClicked: audioTrack.pause() } } } function formatInterval(interval) { let seconds = Math.floor(interval / 1000) let hours = Math.floor(seconds / 3600) let minutes = Math.floor((seconds % 3600) / 60) seconds = seconds % 60 const formattedHours = hours.toString().padStart(2, '0') const formattedMinutes = minutes.toString().padStart(2, '0') const formattedSeconds = seconds.toString().padStart(2, '0') return hours > 0 ? `${formattedHours}:${formattedMinutes}:${formattedSeconds}` : `${formattedMinutes}:${formattedSeconds}` } }