QML Image Comparison Slider



  • Hi guys. I'm trying to create an image comparison slider item in QML, something similar to Juxtapose.

    Its going pretty well, and I have a working QML item, however I have a few issues that I haven't been able to resolve yet and I was wondering if anyone had any ideas to solve them.

    When I set the background of the two images to be a solid color, everything works fine:

    0_1498574031243_Screenshot_20170627_163220.png

    However, when I set the background of the images to be transparent, a problem arises

    0_1498574061838_Screenshot_20170627_163248.png

    As you can see, the right image now bleeds over to the left side.

    From the code, it is relatively apparent why this happens, since the right side image just spans the entire width, and the left image is pasted on top of it. However, I haven't been able to thing of a solution that will work. If I anchor the right hand side image to the divider, similar to what I did for the left hand side image, then the image will move when I move the slider, which is undesired.

    Here is the code:

    ImageComparison.qml

    import QtQuick 2.0
    
    Item {
        id: root
        property var divColor: "white"
        property var bgColor: "transparent"
        property var leftSource: "http://images.clipartpanda.com/rocket-clipart-nicubunu_Toy_rocket.png"
        property var rightSource: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/SNice.svg/1200px-SNice.svg.png"
    
        // When the size of the item is changed, we want to keep the divider at the same
        // relative position. If the relative position is undefined, we default to 0.5
        onWidthChanged: {
            div.x = div.relX ? width*div.relX : width*0.5
        }
    
        // Allows you to click anywhere on the pictures to move the divider
        MouseArea {
            anchors.fill: parent
            cursorShape: Qt.PointingHandCursor
            onClicked: div.x = mouseX
        }
    
        // Right image. It is at the bottom, and fills the entire item
        Rectangle {
            anchors.fill: parent
            color: bgColor
    
            Image {
                source: rightSource
                anchors.fill: parent
            }
        }
    
        // Left image. It is overlayed on the right image, and fills from the left to the divider
        Rectangle {
            anchors {
                left: parent.left
                right: div.left
                top: parent.top
                bottom: parent.bottom
            }
            color: bgColor
            clip: true
            // The actual image is actually just as large as the containing item
            Image {
                width: root.width
                height: root.height
                source: leftSource
            }
        }
    
        // The divider
        Rectangle {
            id: div
            property double relX: x/root.width
            x: root.width/2
            width: 5
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            color: divColor
            border.color: "black"
            border.width: 1
    
            MouseArea {
                id: dragArea
                anchors {
                    top: parent.top
                    bottom: parent.bottom
                    right: rightArrow.right
                    left: leftArrow.left
                }
    
                drag.target: parent
                cursorShape: Qt.SizeHorCursor
                hoverEnabled: true
            }
    
            Drag.active: dragArea.drag.active
            // Keep divider from going outside the limits
            onXChanged: {
                if (x > parent.width - width) x = parent.width - width
                if (x < 0) x = 0
            }
    
            // Right arrow
            Canvas {
                id: rightArrow
                anchors {
                    left: parent.right
                    verticalCenter: parent.verticalCenter
                    leftMargin: dragArea.containsMouse ? 5 : 2
                }
                width: 20
                height: 20
                opacity: 0.5
                onPaint: {
                    var ctx = getContext("2d");
                    ctx.fillStyle = divColor;
                    ctx.strokeStyle = "black";
                    ctx.beginPath();
                    ctx.moveTo(0,0);
                    ctx.lineTo(width,height/2);
                    ctx.lineTo(0,height);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                }
                Behavior on anchors.leftMargin { PropertyAnimation{ duration: 150} }
            }
    
            // Left arrow
            Canvas {
                id: leftArrow
                anchors {
                    right: parent.left
                    verticalCenter: parent.verticalCenter
                    rightMargin: dragArea.containsMouse ? 5 : 2
                }
                width: 20
                height: 20
                opacity: 0.5
                onPaint: {
                    var ctx = getContext("2d");
                    ctx.fillStyle = divColor
                    ctx.strokeStyle = "black";
                    ctx.beginPath();
                    ctx.moveTo(width,0);
                    ctx.lineTo(0,height/2);
                    ctx.lineTo(width,height);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                }
                Behavior on anchors.rightMargin { PropertyAnimation{ duration: 150} }
            }
        }
    
    }
    

    And this is a simple QML file which uses the ImageComparison item

    main.qml (in same directory as ImageComparision.qml)

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import "." as CustomControls
    
    ApplicationWindow {
        id: window
        visible: true
        width: 500
        height: 500
    
        Rectangle {
            color: "red"
            anchors.fill: parent
    
            CustomControls.ImageComparison {
                anchors.centerIn: parent
                width: window.width-100
                height: window.height-100
            }
        }
    }
    
    

    If anyone has any ideas on how to solve this issue, or any other general improvements to the code, please tell me :)



  • Here is a nice animation showing how it looks :)

    alt text


  • Moderators

    @stcorp
    i would simply use a opacity mask on both images.



  • @raven-worx Yeah, that would probably work. I forgot those existed. Thanks for thinking along


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.