添砖加瓦
细想已经实现了一个控件的各种状态(聚焦、鼠标悬浮、点击、使能、失能等)中的样式绘制和控制,已经基本上完成了定制的实现了,剩下就是能够为这个控件加点什么,比如说,加个水波纹动画啥的。
看看源码中的动画实现,那才叫真正的框架啊啊啊,真正的框架是看不懂的额。
所以说,只能尽量理解源码中的方法,加上查阅一下资料,再一行一行慢慢验证实验看下了。
这次我们就不追求别人的框架了,先用最浅显的代码弄好效果再说吧qaq。
动画
通过查看相关资料,说白了qt动画最必要的是在一段短时间内生成一堆连续的间隔较小的数,如透明度,当透明度为1时就是不透明,为0就是全透明,在0 - 1之间生成间隔不大的很多个数,再应用在控件上,就有了渐渐不透明或者渐渐透明的动画效果~
这次的目标,水波纹包含着两个数,一个是画水波纹不断扩大的圆型,另一个就是让水波纹逐渐消失透明度。
在那些绘制函数里,有控件的画笔,直接用这只笔基于这个控件再画一层东西即可。
而数据产生,qt有类QParallelAnimationGroup、QPropertyAnimation帮我们解决,QPropertyAnimation用法通常为绑定某控件的属性,设置开始数值和结束数和时间,产生一堆两数值间的一堆数据应用在这个控件的属性上,对这个属性进行改动。
而目标是再画一层水波纹,不方便直接修改控件的属性,所以新建一个数据类保存这些数据,让QPropertyAnimation绑定的是这个类的属性即可。
动手
学习下源码,在代码目录下新建一个animation的类,里面存放所有关于动画的实现文件。
我们目标很明确,随便弄个类,里面有两个变量方便绑定即可。
新建类绑定变量
新建一个类,继承QParallelAnimationGroup,类里面加入方便绑属性
Q_PROPERTY(qreal radius WRITE setRadius READ radius)
Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity)
新建属性:
QPropertyAnimation *const m_radiusAnimation;
QPropertyAnimation *const m_opacityAnimation;
QWidget *const m_target;
qreal m_radius;
qreal m_opacity;
其中QPropertyAnimation就是能产生一堆连续数据的类了,target用来绑定控件。
绑定的代码如下:
QPropertyAnimation *m_opacityAnimation = new QPropertyAnimation;
m_opacityAnimation->setTargetObject(this);
m_opacityAnimation->setPropertyName(“opacity”);
m_opacityAnimation->setEasingCurve(easing);
m_opacityAnimation->setDuration(duration);
addAnimation(animation);
首先把setTargetObject,目标为这个类,setPropertyName也就绑定了这个类的opacity属性,也是刚刚新建的Q_PROPERTY里面。再添加开始数值,结束数值,时间,就可以等待它的start了:
m_opacityAnimation->setStartValue(1);
m_opacityAnimation->setEndValue(0);
m_opacityAnimation->setDuration(duration);
由于是改动了Q_PROPERTY里的数值,自定义一下设置这个数值的函数:
void MaterialRipple::setOpacity(qreal opacity)
{
if (qFuzzyCompare(m_opacity,opacity)) {
return;
}
m_opacity = opacity;
m_target->update();
}
这个target就是我们待绘制的button,调用它的update时它会进行一次绘制,相当于通知了这个button要进行绘制了,我的opacity更新了。
radius也是一模一样的,改一下数值即可;
button触发
就是点击的时候触发了这个水波纹动画,在mystyle中是removeEventFilter了button和重新installEventFilter了一遍的,所以写eventfilter时加入:
{% btn 'https://nightcivet.site/qstyle-3/',qstyle 一步一个jio印(3)—— 添加动画 水波纹效果,far fa-hand-point-right,outline blue larger %}bool MyStyle::eventFilter(QObject *object, QEvent *event)
{
if (QPushButton *button = qobject_cast<QPushButton *>(object)) {
switch (event->type()) {
case QEvent::MouseButtonPress:
{
xxx...
}
}
}
return QCommonStyle::eventFilter(object, event);
}
这里的内容xxx即可上刚刚新建的类,同时可以把button参数传进去了!调用动画类的start(),就开始了数据产生!
画!
直接在drawPanelButtonCommandPrimitive函数最后加入:
if (ripple && enabled){
const QPointF center = ripple->center();
painter->setOpacity(ripple->opacity());
QBrush brush;
brush.setColor(palette.color(QPalette::Highlight));
brush.setStyle(Qt::SolidPattern);
painter->setBrush(brush);
painter->drawEllipse(center, ripple->radius(), ripple->radius());
}
这个ripple便是新建的数据类,表示波纹嘛,和之前说的一样,设置透明度,画圆即可。
效果!
故意调慢了一点时间,颜色也随便上了,就是这样的效果。
bug还是挺多的,比如连续点击时会出问题,两个button快速切换点击也是,其实就是没有处理动画还没完成的情况,包括控件多的时候的情况;
就是缺乏对这种数据的管理,所以源码框架就是做这些事情,管理控件与动画的绑定,管理动画开始结束的控制。