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; } } }
最终效果如下: