qml+shader实现类似steam游戏库中界面卡片的选中特效

最近使用Qt在做一个项目中涉及到大量卡片列表的需求,为了让界面展示更加丰富,打算实现类似steam中鼠标滑过时的效果:

经过分析可知,当鼠标滑过时动效分为两个部分:

1)  图片的翻转动画:使用PropertyAnimation驱动卡片沿指定的轴翻转

2   图片上的光影动画:使用PropertyAnimation驱动指定变量发生变化,该变量传入到着色器中,控制光带移动

好在Qml同时支持动画和着色器,实现代码如下

import QtQuick 2.0
Item {
    id: shadercard
    property string mypicpath: "qrc:/images3/images3/test.png"  //图片路径
    property real    vpos:-0.8                                 //图片光影位置

    ShaderEffect  {
        id:shadereffect
        anchors.fill: parent

        //将光影位置传到着色器中
        property real       iposStartX: vpos

        //将图片作为通道传到着色器中
        property variant iChannel0: Image {
            source: mypicpath
        }

        //顶点着色器,没做什么
        vertexShader: "
        uniform highp mat4 qt_Matrix;
        attribute highp vec4 qt_Vertex;
        attribute highp vec2 qt_MultiTexCoord0;
        varying highp vec2 coord;
        void main() {
            coord = qt_MultiTexCoord0;
            gl_Position = qt_Matrix * qt_Vertex;
        }"


        fragmentShader:"

        uniform float  iposStartX;         //光影位置
        uniform sampler2D   iChannel0;  //图片数据
        varying highp vec2 coord;

        #define ANGLE 60.0   //光影斜着的角度


        float thresholdY = 0.5;  //控制光影的宽带
        float blur = 0.065;      //控制光带边缘的模糊程度
        float alphaFactor = 0.8; //控制光带的明暗程度

        float colFactor(vec2 uv, float thresholdX, float blur){
            float tempY = uv.x * cos(radians(ANGLE)) + iposStartX; //根据x的位置,以Angle的角度计算y的位置
            float intervalY = uv.y - tempY;                        //将增加的Y值加上
            return smoothstep(thresholdY, thresholdY - blur, abs(intervalY)) * alphaFactor; //根据intervalY的位置进行smoothstep,在画出光带
        }
        void main(void)
        {
           vec4 back = texture2D(iChannel0, coord);
           float factor = colFactor(coord, thresholdY, blur);
           vec3 col =  back.xyz +  vec3(0.2) *factor;
           gl_FragColor = vec4(col,1.0);
        }
    "
    }

    //控制卡牌翻转
    transform: Rotation{
        id:animationrotate
        origin.x:testimnage.width/2.0
        origin.y:-testimnage.height/2.0
        axis{x:1;y:0;z:0}
        angle: 0

    }

    //两个动画,分别控制翻转角度和光带位置
    PropertyAnimation{
        id:animation
        target:animationrotate
        easing.type:Easing.OutQuad
        properties: "angle"
        duration: 500
    }
    PropertyAnimation{
        id:animation2
        easing.type:Easing.OutQuad
        target:shadercard
        properties: "vpos"
        duration: 500
    }

    //鼠标事件
    MouseArea{
        anchors.fill: parent
        hoverEnabled: true

        onEntered: {

            if(animation.running)
            {
                animation.running = false
                animation2.running = false
            }
            animation.to=3
            animation2.to = -0.3
            animation.running=true;
            animation2.running=true;


        }
        onExited: {
            if(animation.running)
            {
                animation.running = false
                animation2.running = false
            }
            animation.to=0
            animation2.to = -0.8
            animation.running=true;
            animation2.running=true;

        }
    }
}

 

最终效果如下:

 

posted @ 2022-03-19 15:43  小手一拿  阅读(369)  评论(0编辑  收藏  举报