Files
cbh/ElaWidgetTools/ElaPromotionCard.cpp
2025-09-19 22:40:52 +08:00

260 lines
9.7 KiB
C++

#include "ElaPromotionCard.h"
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPropertyAnimation>
#include <QRadialGradient>
#include <QtMath>
#include "ElaPromotionCardPrivate.h"
#include "ElaTheme.h"
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, BorderRadius)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QPixmap, CardPixmap)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, CardTitle)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, PromotionTitle)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, Title)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QString, SubTitle)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, CardTitleColor)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, PromotionTitleColor)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, PromotionTitleBaseColor)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, TitleColor)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, QColor, SubTitleColor)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, CardTitlePixelSize)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, PromotionTitlePixelSize)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, TitlePixelSize)
Q_PROPERTY_CREATE_Q_CPP(ElaPromotionCard, int, SubTitlePixelSize)
ElaPromotionCard::ElaPromotionCard(QWidget* parent)
: QWidget{parent}, d_ptr(new ElaPromotionCardPrivate())
{
Q_D(ElaPromotionCard);
d->q_ptr = this;
d->_pBorderRadius = 5;
d->_pPressRadius = 0;
d->_pHoverOpacity = 0;
d->_pHorizontalCardPixmapRatio = 1;
d->_pVerticalCardPixmapRatio = 1;
d->_pCardTitle = "";
d->_pPromotionTitle = "";
d->_pTitle = "";
d->_pSubTitle = "";
d->_pCardTitleColor = Qt::white;
d->_pPromotionTitleColor = Qt::white;
d->_pTitleColor = Qt::white;
d->_pSubTitleColor = Qt::white;
d->_pPromotionTitleBaseColor = QColor(0, 0, 0, 120);
d->_pCardTitlePixelSize = 22;
d->_pPromotionTitlePixelSize = 12;
d->_pTitlePixelSize = 25;
d->_pSubTitlePixelSize = 16;
setMouseTracking(true);
setObjectName("ElaPromotionCard");
setStyleSheet("#ElaPromotionCard{background-color:transparent;}");
d->_hoverGradient = new QRadialGradient();
d->_hoverGradient->setRadius(170);
d->_hoverGradient->setColorAt(0, QColor(0xFF, 0xFF, 0xFF, 40));
d->_hoverGradient->setColorAt(1, QColor(0xFF, 0xFF, 0xFF, 0));
d->_pressGradient = new QRadialGradient();
d->_pressGradient->setRadius(170);
d->_pressGradient->setColorAt(0, QColor(0xFF, 0xFF, 0xFF, 0));
d->_pressGradient->setColorAt(1, QColor(0xFF, 0xFF, 0xFF, 40));
}
ElaPromotionCard::~ElaPromotionCard()
{
Q_D(const ElaPromotionCard);
delete d->_hoverGradient;
delete d->_pressGradient;
}
void ElaPromotionCard::setHorizontalCardPixmapRatio(qreal pixmapRatio)
{
Q_D(ElaPromotionCard);
if (pixmapRatio > 0 && pixmapRatio <= 1)
{
Q_EMIT pHorizontalCardPixmapRatioChanged();
d->_pHorizontalCardPixmapRatio = pixmapRatio;
}
}
qreal ElaPromotionCard::getHorizontalCardPixmapRatio() const
{
Q_D(const ElaPromotionCard);
return d->_pHorizontalCardPixmapRatio;
}
void ElaPromotionCard::setVerticalCardPixmapRatio(qreal pixmapRatio)
{
Q_D(ElaPromotionCard);
if (pixmapRatio > 0 && pixmapRatio <= 1)
{
Q_EMIT pVerticalCardPixmapRatioChanged();
d->_pVerticalCardPixmapRatio = pixmapRatio;
}
}
qreal ElaPromotionCard::getVerticalCardPixmapRatio() const
{
Q_D(const ElaPromotionCard);
return d->_pVerticalCardPixmapRatio;
}
bool ElaPromotionCard::event(QEvent* event)
{
Q_D(ElaPromotionCard);
switch (event->type())
{
case QEvent::MouseButtonPress:
{
QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
QPropertyAnimation* opacityAnimation = new QPropertyAnimation(d, "pPressOpacity");
connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) {
update();
});
connect(opacityAnimation, &QPropertyAnimation::finished, this, [=]() {
d->_isPressAnimationFinished = true;
});
opacityAnimation->setDuration(300);
opacityAnimation->setEasingCurve(QEasingCurve::InQuad);
opacityAnimation->setStartValue(1);
opacityAnimation->setEndValue(0);
opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QPropertyAnimation* pressAnimation = new QPropertyAnimation(d, "pPressRadius");
connect(pressAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) {
d->_pressGradient->setRadius(value.toReal());
});
pressAnimation->setDuration(300);
pressAnimation->setEasingCurve(QEasingCurve::InQuad);
pressAnimation->setStartValue(30);
pressAnimation->setEndValue(d->_getLongestDistance(mouseEvent->pos()) * 1.8);
pressAnimation->start(QAbstractAnimation::DeleteWhenStopped);
d->_isPressAnimationFinished = false;
d->_pressGradient->setFocalPoint(mouseEvent->pos());
d->_pressGradient->setCenter(mouseEvent->pos());
//点击后隐藏Hover效果
d->_startHoverOpacityAnimation(false);
break;
}
case QEvent::MouseButtonRelease:
{
Q_EMIT promotionCardClicked();
break;
}
case QEvent::MouseMove:
{
QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (d->_pHoverOpacity < 1 && d->_isPressAnimationFinished)
{
d->_startHoverOpacityAnimation(true);
}
if (d->_isPressAnimationFinished)
{
d->_hoverGradient->setCenter(mouseEvent->pos());
d->_hoverGradient->setFocalPoint(mouseEvent->pos());
}
update();
break;
}
case QEvent::Enter:
{
d->_startHoverOpacityAnimation(true);
break;
}
case QEvent::Leave:
{
d->_startHoverOpacityAnimation(false);
break;
}
default:
{
break;
}
}
return QWidget::event(event);
}
void ElaPromotionCard::paintEvent(QPaintEvent* event)
{
Q_D(ElaPromotionCard);
QPainter painter(this);
painter.save();
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
painter.setPen(Qt::NoPen);
//阴影绘制
eTheme->drawEffectShadow(&painter, rect(), d->_shadowBorderWidth, d->_pBorderRadius);
QRect foregroundRect(d->_shadowBorderWidth, d->_shadowBorderWidth, width() - 2 * d->_shadowBorderWidth, height() - 2 * d->_shadowBorderWidth);
QPainterPath path;
path.addRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius);
painter.setClipPath(path);
//背景图片绘制
if (!d->_pCardPixmap.isNull())
{
//源区域计算
QRect pixSourceRect = d->_pCardPixmap.rect();
qreal horizontalOffset = d->_pCardPixmap.width() * (1 - d->_pHorizontalCardPixmapRatio) / 2;
qreal verticalOffset = d->_pCardPixmap.height() * (1 - d->_pVerticalCardPixmapRatio) / 2;
pixSourceRect.adjust(horizontalOffset, verticalOffset, -horizontalOffset, -verticalOffset);
painter.drawPixmap(foregroundRect, d->_pCardPixmap, pixSourceRect);
}
//文字绘制
painter.save();
QFont font = painter.font();
//卡片标题
font.setWeight(QFont::Bold);
font.setPixelSize(d->_pCardTitlePixelSize);
painter.setFont(font);
painter.setPen(d->_pCardTitleColor);
painter.drawText(QRect(25, 25, foregroundRect.width() - 25, foregroundRect.height()), Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, d->_pCardTitle);
//标题
font.setWeight(QFont::Bold);
font.setPixelSize(d->_pTitlePixelSize);
painter.setFont(font);
painter.setPen(d->_pTitleColor);
int titleTextHeight = painter.fontMetrics().height();
QRect titleRect(25, (height() - titleTextHeight) / 2, foregroundRect.width() - 25, titleTextHeight);
painter.drawText(titleRect, Qt::AlignLeft | Qt::AlignBottom | Qt::TextSingleLine, d->_pTitle);
//推广标题
if (!d->_pPromotionTitle.isEmpty())
{
font.setWeight(QFont::Normal);
font.setPixelSize(d->_pPromotionTitlePixelSize);
painter.setFont(font);
int promotionTitleTextWidth = painter.fontMetrics().horizontalAdvance(d->_pPromotionTitle);
int promotionTitleTextHeight = painter.fontMetrics().height();
QRect promotionTitleTextRect(32, titleRect.top() - promotionTitleTextHeight - 5, foregroundRect.width() / 2 - 25, promotionTitleTextHeight);
//背景绘制
painter.setPen(Qt::NoPen);
painter.setBrush(d->_pPromotionTitleBaseColor);
painter.drawRoundedRect(QRect(25, promotionTitleTextRect.top() - 2, promotionTitleTextWidth + 14, promotionTitleTextHeight + 4), 8, 8);
//文字绘制
painter.setPen(d->_pPromotionTitleColor);
painter.drawText(promotionTitleTextRect, Qt::AlignLeft | Qt::AlignBottom | Qt::TextSingleLine, d->_pPromotionTitle);
}
//副标题
font.setWeight(QFont::Medium);
font.setPixelSize(d->_pSubTitlePixelSize);
painter.setFont(font);
painter.setPen(d->_pSubTitleColor);
painter.drawText(QRect(25, titleRect.bottom(), foregroundRect.width() / 2 - 25, height() / 2), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, d->_pSubTitle);
painter.restore();
//效果阴影绘制
if (d->_isPressAnimationFinished)
{
//覆盖阴影绘制
painter.setOpacity(d->_pHoverOpacity);
painter.setBrush(*d->_hoverGradient);
painter.drawEllipse(d->_hoverGradient->center(), d->_hoverGradient->radius(), d->_hoverGradient->radius());
}
else
{
//点击阴影绘制
painter.setOpacity(d->_pPressOpacity);
painter.setBrush(*d->_pressGradient);
painter.drawEllipse(d->_pressGradient->center(), d->_pPressRadius, d->_pPressRadius / 1.1);
}
painter.restore();
}