Draw transparent hole or something like that



  • Hi. I've try out to make a user tutorial like in popular Android-based library https://github.com/amlcurran/ShowcaseView. But how can draw a transparent hole? I need it to accent user for a view.
    P.S.: I've stop on using FocusScope, but I can't make a hole with OpacityMask.
    Example tutorial


  • Moderators

    @alejandro_pnz said in Draw transparent hole or something like that:

    P.S.: I've stop on using FocusScope, but I can't make a hole with OpacityMask.

    show some code pls.
    There are multiple ways to achieve that. For example using ShaderEffect or layering with OpacityMask.



  • I'd just use an OpacityMask with your overlay as the source, a circle as the maskSource, and set invert to true.
    It will effectively make a circle hole in your overlay.

    EDIT : I did a quick proof of concept but then I had some fun overcomplicating it a bit with silly animations (preview here : http://www.gfycat.com/DevotedTangibleBobwhite) :

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtGraphicalEffects 1.0
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Label {
            text: "Dimmed text in background"
            anchors.horizontalCenter: parent.horizontalCenter
            font.pixelSize: 18
        }
    
        Button {
            id: button
            text: "Showcase me"
            property real circleRadius: Math.min(parent.width/3, parent.height/3)
            x: parent.width/2 - width/2 + circleRadius * Math.cos(angleInRadians)
            y: parent.height/2 - height/2 + circleRadius * Math.sin(angleInRadians)
            property real angle: 0
            property real angleInRadians: angle * Math.PI / 180
            property bool showcase: false
            RotationAnimation on angle{
                id: rotationAnimation
                running: button.showcase
                from: 0
                to: 360
                loops: Animation.Infinite
                duration: 4000
            }
            onClicked: showcase = !showcase
        }
        Item {
            id: mask
            visible: false
            anchors.fill: parent
            Item {
                id: buttonGeometry
                x: button.x
                y: button.y
                width: button.width
                height: button.height
            }
    
            Repeater {
                id: repeater
                property real startDiameter: Math.max(button.width, button.height) + 50
                property real diameterStep: 50
                property real opacityStep: 0.2
                model: 2
                Rectangle {
                    anchors.centerIn: buttonGeometry
                    property real diameter: repeater.startDiameter + repeater.diameterStep * index
                    width: diameter
                    height: diameter
                    radius: diameter/2
                    opacity: 1 - repeater.opacityStep * index
                }
            }
        }
    
        Rectangle {
            id: overlayRectangle
            visible: false
            anchors.fill: parent
            color: "#888"
        }
    
        OpacityMask {
            visible: opacity
            enabled: visible
            anchors.fill: parent
            maskSource: mask
            source: overlayRectangle
            invert: true
            opacity: button.showcase ? 0.8 : 0
            Behavior on opacity {
                NumberAnimation { easing.type: Easing.InOutCubic }
            }
            Label {
                anchors.centerIn: parent
                text: "Showcase Sample"
                font.pixelSize: 30
                color: "blue"
            }
            Button {
                anchors {
                    bottom: parent.bottom
                    left: parent.left
                    margins: 16
                }
                text: "Close"
                onClicked: button.showcase = false
            }
        }
    }
    
    


  • @GrecKo Thanks, this one is pretty good. But what about backward compatibility? I mean that "invert" property for OpacityMask available only from Qt 5.7. Any suggestions?
    P.S.: OpacityMask in docs
    P.P.S.: thanks all! I've search answer for the question described above here



  • For someone which interest on same problem:

    import QtQuick 2.6
    
    Item {
        anchors.fill: parent
        Image {
            anchors.fill: parent
            source: "http://i.imgur.com/R3yMj0y.jpg"
            fillMode: Image.PreserveAspectCrop
            focus: true
            Keys.onRightPressed: _mask.maskX += 100
            Keys.onLeftPressed: _mask.maskX -= 100
            Keys.onUpPressed: _mask.maskY -= 100
            Keys.onDownPressed: _mask.maskY += 100
            MouseArea {
                anchors.fill: parent
                hoverEnabled: true
                onPositionChanged: {
                    _mask.maskX = mouseX;
                    _mask.maskY = mouseY;
                }
            }
        }
    
        Rectangle {
            id: _bk
            anchors.fill: parent
            color: "#33000000"
            visible: false
            layer.enabled: true
            layer.smooth: true
        }
    
        Rectangle {
            id: _mask
            anchors.fill: parent
            color: "transparent"
            visible: true
            property int maskX: 0
            property int maskY: 0
            Rectangle {
                id: circle
                width: 100; height: 100
                x: _mask.maskX-50; y: _mask.maskY-50
                radius: 50
                color: "#000"
                Behavior on x { NumberAnimation { duration: 400; easing.type: Easing.OutBack; easing.overshoot: 1.4 } }
                Behavior on y { NumberAnimation { duration: 400; easing.type: Easing.OutBack; easing.overshoot: 1.4 } }
            }
            layer.enabled: true
            layer.samplerName: "maskSource"
            layer.effect: ShaderEffect {
                property variant source: _bk
                fragmentShader: "
                    varying highp vec2 qt_TexCoord0;
                    uniform highp float qt_Opacity;
                    uniform lowp sampler2D source;
                    uniform lowp sampler2D maskSource;
                    void main(void) {
                        gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
                    }
                "
            }
        }
        Rectangle {
            id: _mask2
            anchors.fill: parent
            color: "transparent"
            visible: true
            Rectangle {
                id: circle2
                width: 150; height: 150
                x: _mask.maskX-75; y: _mask.maskY-75
                radius: 75
                color: "#000"
                Behavior on x { NumberAnimation { duration: 550; easing.type: Easing.OutBack; easing.overshoot: 2.4 } }
                Behavior on y { NumberAnimation { duration: 550; easing.type: Easing.OutBack; easing.overshoot: 2.4 } }
            }
            layer.enabled: true
            layer.samplerName: "maskSource"
            layer.effect: ShaderEffect {
                property variant source: _bk
                fragmentShader: "
                    varying highp vec2 qt_TexCoord0;
                    uniform highp float qt_Opacity;
                    uniform lowp sampler2D source;
                    uniform lowp sampler2D maskSource;
                    void main(void) {
                        gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
                    }
                "
            }
        }
        Rectangle {
            id: _mask3
            anchors.fill: parent
            color: "transparent"
            visible: true
            Rectangle {
                id: circle3
                width: 220; height: 220
                x: _mask.maskX-110; y: _mask.maskY-110
                radius: 110
                color: "#000"
                Behavior on x { NumberAnimation { duration: 650; easing.type: Easing.OutBack; easing.overshoot: 3.0 } }
                Behavior on y { NumberAnimation { duration: 650; easing.type: Easing.OutBack; easing.overshoot: 3.0 } }
            }
            layer.enabled: true
            layer.samplerName: "maskSource"
            layer.effect: ShaderEffect {
                property variant source: _bk
                fragmentShader: "
                    varying highp vec2 qt_TexCoord0;
                    uniform highp float qt_Opacity;
                    uniform lowp sampler2D source;
                    uniform lowp sampler2D maskSource;
                    void main(void) {
                        gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity;
                    }
                "
            }
        }
    }
    

    and result:
    Masked interactive guide

    I make it a little nice and interactive but the key is (1.0-texture2D(maskSource, qt_TexCoord0.st).a) which makes opacity mask reversed.


Log in to reply
 

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