Qt自绘制动态字体,模糊的,毛茸茸的fuzzy。

cpp 复制代码
#ifndef FUZZYTEXTWIDGET_H
#define FUZZYTEXTWIDGET_H

#include <QWidget>
#include <QTimer>
#include <QColor>
#include <QFont>

class FuzzyTextWidget : public QWidget
{
    Q_OBJECT

public:
    explicit FuzzyTextWidget(QWidget *parent = nullptr);
    ~FuzzyTextWidget();

    void setText(const QString &text);
    void setFontSize(const QString &fontSize);
    void setFontWeight(QFont::Weight fontWeight);
    void setFontFamily(const QString &fontFamily);
    void setColor(const QColor &color);
    void setEnableHover(bool enable);
    void setBaseIntensity(double intensity);
    void setHoverIntensity(double intensity);

protected:
    void paintEvent(QPaintEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void leaveEvent(QEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;

private slots:
    void updateFuzzyEffect();

private:
    void initializeOffscreenBuffer();
    void updateTextMetrics();
    double parseFontSize(const QString &fontSizeStr, double defaultSize);

    QString m_text;
    QString m_fontSizeStr;
    QFont::Weight m_fontWeight;
    QString m_fontFamily;
    QColor m_color;
    bool m_enableHover;
    double m_baseIntensity;
    double m_hoverIntensity;

    QTimer *m_animationTimer;
    QPixmap m_offscreenPixmap;
    bool m_isHovering;
    QRect m_textBoundingRect;
    double m_numericFontSize;
};

#endif // FUZZYTEXTWIDGET_H
cpp 复制代码
#include "FuzzyTextWidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QMouseEvent>
#include <QDebug>
#include <QScreen>
#include <QApplication>
#include <QFontMetricsF>
#include <QRandomGenerator>

FuzzyTextWidget::FuzzyTextWidget(QWidget *parent)
    : QWidget(parent),
    m_text("Hello, Qt!"),
    m_fontSizeStr("clamp(2rem, 10vw, 10rem)"),
    m_fontWeight(QFont::Black),
    m_fontFamily("Arial"),
    m_color(Qt::white),
    m_enableHover(true),
    m_baseIntensity(0.18),
    m_hoverIntensity(0.5),
    m_isHovering(false),
    m_numericFontSize(0.0)
{
    setAttribute(Qt::WA_TranslucentBackground);
    setMouseTracking(true);

    m_animationTimer = new QTimer(this);
    connect(m_animationTimer, &QTimer::timeout, this, &FuzzyTextWidget::updateFuzzyEffect);
    m_animationTimer->start(16);

    updateTextMetrics();
    initializeOffscreenBuffer();
    setMinimumSize(m_offscreenPixmap.size() + QSize(100, 0));
}

FuzzyTextWidget::~FuzzyTextWidget()
{
    m_animationTimer->stop();
    delete m_animationTimer;
}

void FuzzyTextWidget::setText(const QString &text)
{
    if (m_text != text) {
        m_text = text;
        updateTextMetrics();
        initializeOffscreenBuffer();
        update();
    }
}

void FuzzyTextWidget::setFontSize(const QString &fontSize)
{
    if (m_fontSizeStr != fontSize)
    {
        m_fontSizeStr = fontSize;
        updateTextMetrics();
        initializeOffscreenBuffer();
        update();
    }
}

void FuzzyTextWidget::setFontWeight(QFont::Weight fontWeight)
{
    if (m_fontWeight != fontWeight)
    {
        m_fontWeight = fontWeight;
        updateTextMetrics();
        initializeOffscreenBuffer();
        update();
    }
}

void FuzzyTextWidget::setFontFamily(const QString &fontFamily)
{
    if (m_fontFamily != fontFamily)
    {
        m_fontFamily = fontFamily;
        updateTextMetrics();
        initializeOffscreenBuffer();
        update();
    }
}

void FuzzyTextWidget::setColor(const QColor &color)
{
    if (m_color != color)
    {
        m_color = color;
        initializeOffscreenBuffer();
        update();
    }
}

void FuzzyTextWidget::setEnableHover(bool enable)
{
    m_enableHover = enable;
    if (!m_enableHover) {
        m_isHovering = false;
        update();
    }
}

void FuzzyTextWidget::setBaseIntensity(double intensity)
{
    m_baseIntensity = intensity;
}

void FuzzyTextWidget::setHoverIntensity(double intensity)
{
    m_hoverIntensity = intensity;
}

double FuzzyTextWidget::parseFontSize(const QString &fontSizeStr, double defaultSize)
{
    if (fontSizeStr.endsWith("px", Qt::CaseInsensitive))
    {
        return fontSizeStr.chopped(2).toDouble();
    }
    else if (fontSizeStr.endsWith("rem", Qt::CaseInsensitive))
    {
        return fontSizeStr.chopped(3).toDouble() * 16.0;
    }
    else if (fontSizeStr.endsWith("vw", Qt::CaseInsensitive))
    {
        QScreen *screen = QGuiApplication::primaryScreen();
        if (screen)
        {
            return fontSizeStr.chopped(2).toDouble() / 100.0 * screen->geometry().width();
        }
        return defaultSize;
    }
    else if (fontSizeStr.startsWith("clamp(", Qt::CaseInsensitive) && fontSizeStr.endsWith(")"))
    {
        QString content = fontSizeStr.mid(6, fontSizeStr.length() - 7);
        QStringList parts = content.split(",", Qt::SkipEmptyParts);
        if (parts.size() == 3) {
            double minVal = parseFontSize(parts[0].trimmed(), defaultSize);
            double preferredVal = parseFontSize(parts[1].trimmed(), defaultSize);
            double maxVal = parseFontSize(parts[2].trimmed(), defaultSize);
            return qBound(minVal, preferredVal, maxVal);
        }
    }
    return defaultSize;
}

void FuzzyTextWidget::updateTextMetrics()
{
    QFont font;
    font.setFamily(m_fontFamily);
    font.setWeight(m_fontWeight);
    m_numericFontSize = parseFontSize(m_fontSizeStr, 48.0);
    font.setPixelSize(static_cast<int>(m_numericFontSize));

    QFontMetricsF metrics(font);
    m_textBoundingRect = metrics.boundingRect(m_text).toRect();

    int extraWidthBuffer = 10;
    int horizontalMargin = 50;
    int verticalMargin = 0;

    int offscreenWidth = m_textBoundingRect.width() + extraWidthBuffer;
    int tightHeight = m_textBoundingRect.height();

    setFixedSize(offscreenWidth + horizontalMargin * 2, tightHeight + verticalMargin * 2);

    qDebug() << "Calculated font size:" << m_numericFontSize;
    qDebug() << "Text bounding rect:" << m_textBoundingRect;
    qDebug() << "Widget fixed size:" << size();
}

void FuzzyTextWidget::initializeOffscreenBuffer()
{
    if (m_text.isEmpty())
    {
        m_offscreenPixmap = QPixmap();
        return;
    }

    updateTextMetrics();

    int offscreenWidth = m_textBoundingRect.width() + 10;
    int offscreenHeight = m_textBoundingRect.height();

    m_offscreenPixmap = QPixmap(offscreenWidth, offscreenHeight);
    m_offscreenPixmap.fill(Qt::transparent);

    QPainter painter(&m_offscreenPixmap);
    painter.setRenderHint(QPainter::Antialiasing);

    QFont font;
    font.setFamily(m_fontFamily);
    font.setWeight(m_fontWeight);
    font.setPixelSize(static_cast<int>(m_numericFontSize));
    painter.setFont(font);
    painter.setPen(m_color);

    painter.drawText(5 - m_textBoundingRect.left(), -m_textBoundingRect.top(), m_text);
}

void FuzzyTextWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    if (m_offscreenPixmap.isNull() || m_text.isEmpty())
    {
        return;
    }

    const int fuzzRange = 30;
    double intensity = m_isHovering ? m_hoverIntensity : m_baseIntensity;

    for (int j = 0; j < m_offscreenPixmap.height(); j++)
    {
        int dx = static_cast<int>(intensity * (QRandomGenerator::global()->generateDouble() - 0.5) * fuzzRange);

        painter.drawPixmap(dx + 50,
                           j + 0,
                           m_offscreenPixmap,
                           0,
                           j,
                           m_offscreenPixmap.width(),
                           1);
    }
}

void FuzzyTextWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (m_enableHover)
    {
        QRect interactiveArea(55, 0, m_textBoundingRect.width(), m_textBoundingRect.height());
        bool nowHovering = interactiveArea.contains(event->pos());

        if (nowHovering != m_isHovering)
        {
            m_isHovering = nowHovering;
            update();
        }
    }
    QWidget::mouseMoveEvent(event);
}

void FuzzyTextWidget::leaveEvent(QEvent *event)
{
    if (m_enableHover && m_isHovering)
    {
        m_isHovering = false;
        update();
    }
    QWidget::leaveEvent(event);
}

void FuzzyTextWidget::resizeEvent(QResizeEvent *event)
{
    updateTextMetrics();
    initializeOffscreenBuffer();
    QWidget::resizeEvent(event);
}

void FuzzyTextWidget::updateFuzzyEffect()
{
    update();
}

使用说明

cpp 复制代码
FuzzyTextWidget *pt1=new FuzzyTextWidget();
   pt1-> setText("gogo");
    pt1->show();
相关推荐
前端 贾公子7 分钟前
uniapp中@input修改input内容不生效 | 过滤赋值无效 | 连续非法字符不更新的问题
开发语言·前端·javascript
五阿哥永琪9 分钟前
从零读懂 Java 函数式接口:Function、Consumer、Supplier、Predicate
java·开发语言
写不来代码的草莓熊11 分钟前
el-date-picker ,自定义输入数字自动转换显示yyyy-mm-dd HH:mm:ss格式 【仅双日历 datetimerange专用】
开发语言·前端·javascript
I疯子19 分钟前
2026-04-13 打卡第 6 天
开发语言·python
断眉的派大星20 分钟前
值传递和引用传递
开发语言
大邳草民25 分钟前
Python 对象模型与属性访问机制
开发语言·笔记·python
xyq202429 分钟前
Swift 下标脚本
开发语言
计算机安禾31 分钟前
【数据结构与算法】第44篇:堆(Heap)的实现
c语言·开发语言·数据结构·c++·算法·排序算法·图论
chushiyunen42 分钟前
python fastapi使用、uvicorn
开发语言·python·fastapi
成都易yisdong1 小时前
实现三北方向转换计算器(集成 WMM2025 地磁模型)
开发语言·windows·算法·c#·visual studio