Simulate Android CircularReveal with Qml



  • Hi.
    How can I create CircularReveal [1] in Qml?
    I tried using both OpacityMask and ShaderEffect, but with no luck.
    Using CircularReveal2, transition is not circle but an ellipse, because width and height of Item are not equal.
    Using CircularReveal3, OpacityMask is anchored in Item and quality of item is improved over time.

    Thanks

    [1] : https://github.com/ozodrukh/CircularReveal/

    main.qml

    import QtQuick 2.7
    import QtQuick.Window 2.2
    import QtQuick.Controls 1.4
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Rectangle {
            anchors.fill: parent
            color: "#55a"
        }
    
        CircularReveal2 {
            id: cr2
            width: 500
            height: 100
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.bottom: parent.verticalCenter
    
            Button {
                text: "Click me"
                anchors.fill: parent
            }
        }
    
        CircularReveal3 {
            id: cr3
            width: 500
            height: 100
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.verticalCenter
    
            Button {
                text: "Click me"
                anchors.fill: parent
            }
        }
    }
    

    CircularReveal2.qml

    import QtQuick 2.0
    
    Item {
        id: root
    
        function hypot(x, y) {
            return Math.sqrt(x*x + y*y)
        }
    
        function startAnimation() {
            // TODO start reveal animation
        }
    
        function resetAnimation() {
            // TODO reverse reveal animation
        }
    
        layer.enabled: true
        layer.effect: CircularShader { }
    }
    

    CircularReveal3.qml

    import QtQuick 2.0
    import QtQuick.Controls 1.4
    import QtGraphicalEffects 1.0
    
    Item {
        id: root
    
        Component.onCompleted: {
            mask.width = mask.height = hypot(root.width, root.height)
        }
    
        function hypot(x, y) {
            return Math.sqrt(x*x + y*y)
        }
    
        function startAnimation() {
            // TODO start reveal animation
        }
    
        function resetAnimation() {
            // TODO reverse reveal animation
        }
        Rectangle {
            id: mask
            visible: false
            x: (root.width - mask.width) / 2
            y: (root.height - mask.height) / 2
            radius: width
    
            Behavior on width {
                NumberAnimation { duration: 5000 }
            }
            Behavior on height {
                NumberAnimation { duration: 5000 }
            }
        }
    
    
        layer.enabled: true
        layer.effect: OpacityMask {
            maskSource: mask
        }
    }
    

    CircularShader.qml

    import QtQuick 2.0
    
    ShaderEffect {
        id: shader
        
        property variant source
        property real centerX: 0.5 // [0,1]
        property real centerY: 0.5 // [0,1]
        
        property bool forward: true
        property real smoothness: 0.1
        property real progress: 0.0
    
        Component.onCompleted: doForward()
        
        function doForward() {
            forwardAnimation.start()
        }
        
        function doBackward() {
            backwardAnimation.start()
        }
    
        function hypot(x, y) {
            return Math.sqrt(x*x + y*y)
        }
        
        NumberAnimation {
            id: forwardAnimation
            target: shader
            property: "progress"
            duration: 5000
            from: 0
            to: 1
    //        running: false
        }
        
        NumberAnimation {
            id: backwardAnimation
            target: shader
            property: "progress"
            duration: 5000
            from: 1
            to: 0
    //        running: false
        }
        
        fragmentShader:
            "
    varying highp vec2 qt_TexCoord0;
    uniform sampler2D source;
    uniform lowp float qt_Opacity;
    
    uniform highp float width;
    uniform highp float height;
    uniform highp float centerX;
    uniform highp float centerY;
    uniform float smoothness;
    uniform bool forward;
    uniform float progress;
    
    const float SQRT_2 = 1.414213562373;
    
    //float hypot(float x, float y) {
    //    return sqrt(x*x + y*y);
    //}
    
    void main() {
        vec2 center = vec2(centerX, centerY);
    
        float x = forward ? progress : 1.-progress;
        gl_FragColor = distance(qt_TexCoord0, center) < progress ?
                            texture2D(source, qt_TexCoord0) :
                            vec4(0, 0, 0, 0);
    
    //    float x = forward ? progress : 1.-progress;
    //    float m = smoothstep(-smoothness, 0.0, SQRT_2*distance(center, qt_TexCoord0) - x*(1.+smoothness));
    //    gl_FragColor = mix(vec4(0, 0, 0, 0), texture2D(source, qt_TexCoord0), forward ? 1.-m : m) * qt_Opacity;
    }
                "
    }
    

Log in to reply
 

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