Repository: daishuboluo/Anime_carousel Branch: main Commit: d4a4037fba56 Files: 145 Total size: 528.6 KB Directory structure: gitextract_vx1aefsx/ ├── Anime_Template_Project/ │ ├── Accordion/ │ │ ├── AccordionCard.cpp │ │ ├── AccordionCard.h │ │ ├── CMakeLists.txt │ │ ├── accordionwidget.cpp │ │ ├── accordionwidget.h │ │ ├── img.qrc │ │ └── main.cpp │ ├── Adaptive_Carousel/ │ │ ├── daen_no_kado.cpp │ │ ├── daen_no_kado.h │ │ ├── latticed_circle_button.cpp │ │ └── latticed_circle_button.h │ ├── Blur_Text/ │ │ ├── fuzzytextwidget.cpp │ │ └── fuzzytextwidget.h │ ├── Carousel_card/ │ │ ├── card_button.cpp │ │ ├── card_button.h │ │ ├── card_text.cpp │ │ ├── card_text.h │ │ ├── carousel_card.cpp │ │ ├── carousel_card.h │ │ ├── carrier_card.cpp │ │ └── carrier_card.h │ ├── Eye_of_Cthulhu/ │ │ ├── JellyfishWidget.cpp │ │ └── JellyfishWidget.h │ ├── Flowing_Gradient_Font/ │ │ ├── widget.cpp │ │ └── widget.h │ ├── FractalWidget/ │ │ ├── CMakeLists.txt │ │ ├── fractalwidget.cpp │ │ ├── fractalwidget.h │ │ ├── fractalworker.cpp │ │ ├── fractalworker.h │ │ ├── main.cpp │ │ ├── mainwindow.cpp │ │ └── mainwindow.h │ ├── Generative_Lines/ │ │ ├── widget.cpp │ │ └── widget.h │ ├── Glitch_Text/ │ │ ├── GlitchTextWidget.cpp │ │ └── GlitchTextWidget.h │ ├── Honeycomb_Grid/ │ │ ├── hexagonwidget.cpp │ │ ├── hexagonwidget.h │ │ ├── mainwindow.cpp │ │ └── mainwindow.h │ ├── Letter_Glitch/ │ │ ├── CMakeLists.txt │ │ ├── GlitchEffectWidget.cpp │ │ ├── GlitchEffectWidget.h │ │ └── main.cpp │ ├── Liquid/ │ │ ├── image_processing.cpp │ │ ├── image_processing.h │ │ ├── liquid_button.cpp │ │ ├── liquid_button.h │ │ ├── widget.cpp │ │ └── widget.h │ ├── LoadingAnime/ │ │ ├── Bouncing_Ball_Loading.h │ │ ├── CMakeLists.txt │ │ ├── Rectangle_Loading.h │ │ ├── Square_Pseudo_Bounce_Loading.cpp │ │ ├── Square_Pseudo_Bounce_Loading.h │ │ ├── black_white_ball_loading.cpp │ │ ├── black_white_ball_loading.h │ │ ├── bouncing_ball_loading.cpp │ │ ├── hexagonloaderwidget.cpp │ │ ├── hexagonloaderwidget.h │ │ ├── line_loading.cpp │ │ ├── line_loading.h │ │ ├── line_rotating_along_rectangular_border.cpp │ │ ├── line_rotating_along_rectangular_border.h │ │ ├── main.cpp │ │ ├── rectangle_loading.cpp │ │ ├── regular_loading.cpp │ │ ├── regular_loading.h │ │ ├── ring_loading.cpp │ │ ├── ring_loading.h │ │ ├── widget.cpp │ │ └── widget.h │ ├── Login_interface/ │ │ ├── hollow_button.cpp │ │ ├── hollow_button.h │ │ ├── input_box.cpp │ │ ├── input_box.h │ │ ├── login_button.cpp │ │ ├── login_button.h │ │ ├── login_form.cpp │ │ ├── login_form.h │ │ ├── other_login_buttons.cpp │ │ ├── other_login_buttons.h │ │ ├── registration_form.cpp │ │ ├── registration_form.h │ │ ├── responsive_form.cpp │ │ ├── responsive_form.h │ │ ├── scroll_bar.cpp │ │ ├── scroll_bar.h │ │ ├── transparent_transition_interface.cpp │ │ └── transparent_transition_interface.h │ ├── Magnet_Lines/ │ │ ├── CMakeLists.txt │ │ ├── RotatableLinesWidget.cpp │ │ ├── RotatableLinesWidget.h │ │ └── main.cpp │ ├── Mimic_Button/ │ │ ├── depressed_button.cpp │ │ ├── depressed_button.h │ │ ├── img.qrc │ │ ├── widget.cpp │ │ └── widget.h │ ├── Particle_Generation/ │ │ ├── CMakeLists.txt │ │ ├── ParticleWidget.h │ │ ├── main.cpp │ │ ├── mainwindow.cpp │ │ ├── mainwindow.h │ │ └── particlewidget.cpp │ ├── Physical_Text/ │ │ ├── DraggableElasticObject.cpp │ │ ├── draggableelasticobject.h │ │ └── main.cpp │ ├── PixelTransition/ │ │ ├── pixel_transition.cpp │ │ ├── pixel_transition.h │ │ ├── widget.cpp │ │ └── widget.h │ ├── Point_Wave/ │ │ ├── widget.cpp │ │ └── widget.h │ ├── Pressure_Block/ │ │ ├── squarepressurewidget.cpp │ │ └── squarepressurewidget.h │ ├── SplitText/ │ │ ├── Single_Text.cpp │ │ ├── Single_Text.h │ │ ├── SplitText.cpp │ │ └── SplitText.h │ ├── Star_Sky_Connection/ │ │ ├── MovingPointsWidget.cpp │ │ └── MovingPointsWidget.h │ ├── TreeScene/ │ │ ├── TreeScene.cpp │ │ └── TreeScene.h │ ├── Wave/ │ │ ├── waveswidget.cpp │ │ └── waveswidget.h │ ├── Zipper_Slider/ │ │ ├── pathwidget.cpp │ │ └── pathwidget.h │ ├── button_class/ │ │ ├── diffusion_button.cpp │ │ ├── diffusion_button.h │ │ ├── wave_button.cpp │ │ └── wave_button.h │ ├── dial_class/ │ │ ├── knob_page.cpp │ │ ├── knob_page.h │ │ ├── temperature_dial.cpp │ │ └── temperature_dial.h │ ├── json/ │ │ └── Anime_care_attributes.json │ ├── src.qrc │ └── utility_class/ │ ├── timer_animation.cpp │ ├── timer_animation.h │ ├── timer_animationgroup.cpp │ └── timer_animationgroup.h ├── LICENSE └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: Anime_Template_Project/Accordion/AccordionCard.cpp ================================================ #include "AccordionCard.h" #include #include #include #include #include AccordionCard::AccordionCard(const QString &text, const QString &imagePath, QWidget *parent) : QWidget(parent), m_currentStretch(1.0) { m_label = new QLabel(text, this); m_label->setAlignment(Qt::AlignCenter); m_label->hide(); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(m_label); setLayout(layout); setMinimumWidth(50); m_stretchAnimation = new QPropertyAnimation(this, "currentStretch", this); m_stretchAnimation->setDuration(280); m_stretchAnimation->setEasingCurve(QEasingCurve::OutQuad); connect(m_stretchAnimation, &QPropertyAnimation::valueChanged, [this](const QVariant &value){ emit stretchChanged(this, value.toReal()); }); m_originalImage.load(imagePath); if (m_originalImage.isNull()) { qWarning() << "Failed to load image for AccordionCard from path:" << imagePath; } m_cachedScaledImage = QPixmap(); m_cachedImageSize = QSize(0,0); } qreal AccordionCard::currentStretch() const { return m_currentStretch; } void AccordionCard::setCurrentStretch(qreal stretch) { if (qFuzzyCompare(m_currentStretch, stretch)) return; m_currentStretch = stretch; updateGeometry(); } void AccordionCard::setTargetStretch(qreal targetStretch) { if (m_stretchAnimation->state() == QAbstractAnimation::Running) { m_stretchAnimation->stop(); } m_stretchAnimation->setStartValue(m_currentStretch); m_stretchAnimation->setEndValue(targetStretch); m_stretchAnimation->start(); } void AccordionCard::enterEvent(QEnterEvent *event) { emit hovered(this); QWidget::enterEvent(event); } void AccordionCard::leaveEvent(QEvent *event) { emit left(this); QWidget::leaveEvent(event); } void AccordionCard::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); int w = width(); int h = height(); int cornerRadius = 5; QPainterPath path; path.addRoundedRect(0, 0, w, h, cornerRadius, cornerRadius); painter.setClipPath(path); painter.fillRect(rect(), QColor(100, 100, 100, 255)); if (!m_originalImage.isNull()) { if (m_cachedImageSize != QSize(w, h)) { QSize imageSize = m_originalImage.size(); qreal imageAspectRatio = (qreal)imageSize.width() / imageSize.height(); qreal cardAspectRatio = (qreal)w / h; QSize scaledTargetSize; if (imageAspectRatio > cardAspectRatio) { scaledTargetSize = QSize(qRound(h * imageAspectRatio), h); } else { scaledTargetSize = QSize(w, qRound(w / imageAspectRatio)); } m_cachedScaledImage = m_originalImage.scaled(scaledTargetSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_cachedImageSize = QSize(w, h); } int imageX = (w - m_cachedScaledImage.width()) / 2; int imageY = (h - m_cachedScaledImage.height()) / 2; painter.drawPixmap(imageX, imageY, m_cachedScaledImage); } } void AccordionCard::resizeEvent(QResizeEvent *event) { if (event->oldSize() != event->size()) { m_cachedScaledImage = QPixmap(); m_cachedImageSize = QSize(0,0); } QWidget::resizeEvent(event); } ================================================ FILE: Anime_Template_Project/Accordion/AccordionCard.h ================================================ #ifndef ACCORDIONCARD_H #define ACCORDIONCARD_H #include #include #include #include #include #include #include #include class AccordionCard : public QWidget { Q_OBJECT Q_PROPERTY(qreal currentStretch READ currentStretch WRITE setCurrentStretch) public: explicit AccordionCard(const QString &text, const QString &imagePath, QWidget *parent = nullptr); qreal currentStretch() const; void setCurrentStretch(qreal stretch); void setTargetStretch(qreal targetStretch); protected: void enterEvent(QEnterEvent *event) override; void leaveEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; // 添加 resizeEvent 以便在部件大小改变时清除缓存 void resizeEvent(QResizeEvent *event) override; signals: void hovered(AccordionCard *card); void left(AccordionCard *card); void stretchChanged(AccordionCard *card, qreal stretchValue); private: QLabel *m_label; qreal m_currentStretch; QPropertyAnimation *m_stretchAnimation; QPixmap m_originalImage; QPixmap m_cachedScaledImage; QSize m_cachedImageSize; }; #endif // ACCORDIONCARD_H ================================================ FILE: Anime_Template_Project/Accordion/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(untitled4 VERSION 0.1 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) set(PROJECT_SOURCES main.cpp AccordionCard.cpp AccordionCard.h ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(untitled4 MANUAL_FINALIZATION ${PROJECT_SOURCES} AccordionWidget.h AccordionWidget.cpp img.qrc ) # Define target properties for Android with Qt 6 as: # set_property(TARGET untitled4 APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR # ${CMAKE_CURRENT_SOURCE_DIR}/android) # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation else() if(ANDROID) add_library(untitled4 SHARED ${PROJECT_SOURCES} ) # Define properties for Android with Qt 5 after find_package() calls as: # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") else() add_executable(untitled4 ${PROJECT_SOURCES} ) endif() endif() target_link_libraries(untitled4 PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an # explicit, fixed bundle identifier manually though. if(${QT_VERSION} VERSION_LESS 6.1.0) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.untitled4) endif() set_target_properties(untitled4 PROPERTIES ${BUNDLE_ID_OPTION} MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) include(GNUInstallDirs) install(TARGETS untitled4 BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(untitled4) endif() ================================================ FILE: Anime_Template_Project/Accordion/accordionwidget.cpp ================================================ #include "AccordionWidget.h" #include AccordionWidget::AccordionWidget(QWidget *parent) : QWidget(parent) { m_layout = new QHBoxLayout(this); m_layout->setSpacing(5); setLayout(m_layout); } void AccordionWidget::addCard(AccordionCard *card) { m_cards.append(card); m_layout->addWidget(card); card->setCurrentStretch(1.0); m_cardCurrentStretches.insert(card, 1.0); connect(card, &AccordionCard::hovered, this, &AccordionWidget::onCardHovered); connect(card, &AccordionCard::left, this, &AccordionWidget::onCardLeft); connect(card, &AccordionCard::stretchChanged, this, &AccordionWidget::onCardStretchChanged); updateLayoutStretches(); } void AccordionWidget::onCardHovered(AccordionCard *hoveredCard) { qreal expandedStretch = 5.0; qreal defaultStretch = 1.0; for (AccordionCard *card : m_cards) { if (card == hoveredCard) { card->setTargetStretch(expandedStretch); } else { card->setTargetStretch(defaultStretch); } } } void AccordionWidget::onCardLeft(AccordionCard *leftCard) { Q_UNUSED(leftCard); qreal defaultStretch = 1.0; for (AccordionCard *card : m_cards) { card->setTargetStretch(defaultStretch); } } void AccordionWidget::onCardStretchChanged(AccordionCard *card, qreal stretchValue) { m_cardCurrentStretches.insert(card, stretchValue); updateLayoutStretches(); } void AccordionWidget::updateLayoutStretches() { if (m_cards.isEmpty()) return; for (int i = 0; i < m_cards.size(); ++i) { AccordionCard *card = m_cards.at(i); int layoutStretch = static_cast(m_cardCurrentStretches.value(card, 1.0) * 1000); qDebug() << "Card" << i << "current stretch:" << m_cardCurrentStretches.value(card, 1.0) << "layout stretch factor:" << layoutStretch; m_layout->setStretch(i, layoutStretch); } } ================================================ FILE: Anime_Template_Project/Accordion/accordionwidget.h ================================================ #ifndef ACCORDIONWIDGET_H #define ACCORDIONWIDGET_H #include #include #include #include #include "AccordionCard.h" class AccordionWidget : public QWidget { Q_OBJECT public: explicit AccordionWidget(QWidget *parent = nullptr); void addCard(AccordionCard *card); private slots: void onCardHovered(AccordionCard *hoveredCard); void onCardLeft(AccordionCard *leftCard); void onCardStretchChanged(AccordionCard *card, qreal stretchValue); private: QHBoxLayout *m_layout; QList m_cards; QMap m_cardCurrentStretches; void updateLayoutStretches(); }; #endif // ACCORDIONWIDGET_H // 预处理指令:结束条件编译块 ================================================ FILE: Anime_Template_Project/Accordion/img.qrc ================================================ img/FvCpS5naYAA06ej.jpg img/GaznbVDaMAAp7pf.jpg img/GCPNMbRbMAAsh0_.jpg img/GGE5u19acAAa7OA.jpg img/GGIv8piaUAA_myh.jpg img/GmGE-x0asAAuc9A.jpg img/GniQBbrbUAAq-HX.jpg img/GnkSUY9aMAAtOuY.jpg img/GWN1xIIbAAA50xM.jpg img/GS6v-JebIAIMmNY.jpg ================================================ FILE: Anime_Template_Project/Accordion/main.cpp ================================================ #include #include #include "AccordionWidget.h" #include "AccordionCard.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; window.setWindowTitle("手风琴"); window.setMinimumSize(800, 300); AccordionWidget *accordion = new AccordionWidget(&window); accordion->addCard(new AccordionCard("卡片 1", "://img/FvCpS5naYAA06ej.jpg")); accordion->addCard(new AccordionCard("卡片 2", "://img/GS6v-JebIAIMmNY.jpg")); accordion->addCard(new AccordionCard("卡片 3", "://img/GniQBbrbUAAq-HX.jpg")); accordion->addCard(new AccordionCard("卡片 4", "://img/GGE5u19acAAa7OA.jpg")); accordion->addCard(new AccordionCard("卡片 5", "://img/GGIv8piaUAA_myh.jpg")); window.setCentralWidget(accordion); window.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/Adaptive_Carousel/daen_no_kado.cpp ================================================ #include "daen_no_kado.h" Daen_no_Kado::Daen_no_Kado(QWidget *parent) : QWidget{parent} { this->resize(1200, 606); recta = QRect(0, 0, 1200, 606); QPalette pal(this->palette()); pal.setColor(QPalette::Window, QColor(243, 246, 253)); this->setPalette(pal); button = new Latticed_Circle_Button(this); button2 = new Latticed_Circle_Button(this); button->Shikisai_no_joutai = true; connect(button, &Latticed_Circle_Button::Latticed_Circle_Click_Signal, this, &Daen_no_Kado::Move_Left); connect(button2, &Latticed_Circle_Button::Latticed_Circle_Click_Signal, this, &Daen_no_Kado::Move_Right); this->Get_Image(); this->Kiso_Deta_Kouchiku(); this->Gazou_wo_nabebae_ru(); connect(&timerA, &QTimer::timeout, this, &Daen_no_Kado::koushin_suru); timerA.setInterval(16); connect(&timerB, &QTimer::timeout, this, &Daen_no_Kado::Progress_Bar_Data_Update); timerB.setInterval(16); timerB.start(); this->animations(); } void Daen_no_Kado::animations() { animation = new QPropertyAnimation(this->button, "geometry"); animation->setDuration(300); animation->setStartValue(this->button->geometry()); animation->setEndValue(QRect(this->button->pos().x() - zoom_rate / 2, this->button->pos().y() - zoom_rate / 2, button->width() + zoom_rate, button->height() + zoom_rate)); animation->setEasingCurve(QEasingCurve::Linear); animation2 = new QPropertyAnimation(this->button2, "geometry"); animation2->setDuration(300); animation2->setStartValue(this->button2->geometry()); animation2->setEndValue(QRect(this->button2->pos().x() - zoom_rate / 2, this->button2->pos().y() - zoom_rate / 2, button2->width() + zoom_rate, button2->height() + zoom_rate)); animation2->setEasingCurve(QEasingCurve::Linear); connect(button, &Latticed_Circle_Button::execute_animation_signal, this, [this](Latticed_Circle_Button::AnimationState state) { if (state == Latticed_Circle_Button::Execute) { animation->setDirection(QAbstractAnimation::Forward); animation->start(); } else if (state == Latticed_Circle_Button::Restore) { animation->setDirection(QAbstractAnimation::Backward); animation->start(); } }); connect(button2, &Latticed_Circle_Button::execute_animation_signal, this, [this](Latticed_Circle_Button::AnimationState state) { if (state == Latticed_Circle_Button::Execute) { animation2->setDirection(QAbstractAnimation::Forward); animation2->start(); } else if (state == Latticed_Circle_Button::Restore) { animation2->setDirection(QAbstractAnimation::Backward); animation2->start(); } }); animation3 = new QPropertyAnimation(this, "Expand_Collapse_Height"); animation3->setDuration(360); animation3->setStartValue(m_Expand_Collapse_Height); animation3->setEndValue(recta.height()); animation3->setEasingCurve(QEasingCurve::Linear); animation4 = new QPropertyAnimation(this, "Expand_Collapse_Opacity"); animation4->setDuration(360); animation4->setStartValue(m_Expand_Collapse_Opacity); animation4->setEndValue(0); animation4->setEasingCurve(QEasingCurve::Linear); } void Daen_no_Kado::Get_Image() { qreal x = 0; for (int i = 1; i < 99; i++) { QPixmap pixmap(QString("://img/card_image%1.jpg").arg(i)); if (pixmap.isNull()) return; Gazou_Shuu.append(pixmap); } } void Daen_no_Kado::Kiso_Deta_Kouchiku() { leftBound = recta.width() * 0.3750; Jouhan_Daen_no_Chuushin = QPointF(recta.width() / 2.0, 0); Jouhan_Daen_no_X_Hankei = recta.width() * 0.56; Jouhan_Daen_no_Y_Hankei = recta.height() * 0.2215; Kahan_Daen_no_Chuushin = QPointF(recta.width() / 2.0, recta.height()); Kahan_Daen_no_X_Hankei = recta.width() * 0.656; Kahan_Daen_no_Y_Hankei = recta.height() * 0.3261; int i = recta.width() / 30; button->resize(i, i); button2->resize(i, i); button->move(recta.width() * 0.45, recta.height() * 0.8); button2->move(recta.width() * 0.50, recta.height() * 0.8); Progress_Bar_Step = qreal(recta.width()) / qreal(recta.height()) * 1.6; m_Expand_Collapse_Height = recta.height() * 0.98; qDebug() << "进度条步长" << Progress_Bar_Step; qDebug() << "展开高度" << m_Expand_Collapse_Height; Kado_Suu.clear(); this->Gazou_wo_nabebae_ru(); this->animations(); } void Daen_no_Kado::Gazou_wo_nabebae_ru() { qreal x = Ratio_Position * recta.width(); for (int i = 0; i < Gazou_Shuu.size(); i++) { if (i == 0) Left_Limit_Margin = x + -leftBound * 0.4444; else if (i == Gazou_Shuu.size() - 1) Right_Limit_Margin = x + leftBound * 0.3333; Kado_Suu.append(qMakePair(x, leftBound * 0.6666 + x)); x += leftBound * 0.6888; } kankaku = Kado_Suu[3].second - Kado_Suu[3].first; } void Daen_no_Kado::resizeEvent(QResizeEvent* event) { recta = QRect(0, 0, event->size().width(), event->size().height()); this->Kiso_Deta_Kouchiku(); m_Progress_Bar_X = (m_Progress_Bar_X / event->oldSize().width()) * event->size().width(); m_Progress_Bar_Width = (m_Progress_Bar_Width / event->oldSize().width()) * event->size().width(); QWidget::resizeEvent(event); } void Daen_no_Kado::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { if (!Ima_no_joutai) return; this->setCursor(Qt::PointingHandCursor); mousePressed = true; startX = event->pos().x(); accumulatedDistance = 0; } QWidget::mousePressEvent(event); } void Daen_no_Kado::mouseMoveEvent(QMouseEvent* event) { if (mousePressed) { int currentX = event->pos().x(); int deltaX = currentX - startX; if (deltaX > 0) Execution_Direction = true; else if (deltaX <= 0) Execution_Direction = false; accumulatedDistance += qAbs(deltaX); if (accumulatedDistance >= 9) { this->koushin_suru(); accumulatedDistance -= 9; } startX = currentX; } QWidget::mouseMoveEvent(event); } void Daen_no_Kado::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { this->setCursor(Qt::ArrowCursor); mousePressed = false; accumulatedDistance = 0; } QWidget::mouseReleaseEvent(event); } bool Daen_no_Kado::event(QEvent* e) { if (e->type() == QEvent::Enter) { timerB.stop(); animation3->setDirection(QAbstractAnimation::Forward); animation3->start(); animation4->setDirection(QAbstractAnimation::Forward); animation4->start(); } else if (e->type() == QEvent::Leave) { timerB.start(); animation3->setDirection(QAbstractAnimation::Backward); animation3->start(); animation4->setDirection(QAbstractAnimation::Backward); animation4->start(); } return QWidget::event(e); } void Daen_no_Kado::wheelEvent(QWheelEvent *event) { if (event->angleDelta().y() > 0) this->Card_Move_Left_Behavior(); else if (event->angleDelta().y() < 0) this->Card_Move_Right_Behavior(); } void Daen_no_Kado::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::LosslessImageRendering); for (int i = 0; i < Kado_Suu.size(); i++) Byouga_Ryouiki(Kado_Suu[i].first, Kado_Suu[i].second, Gazou_Shuu[i]); this->draw_text(); this->Draw_Progress_Bar(); } void Daen_no_Kado::Byouga_Ryouiki(qreal& Gazou_Zen_XJiku, qreal& Gazou_Go_XJiku, QPixmap& Ehon) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::LosslessImageRendering); if (Gazou_Zen_XJiku > recta.width() || Gazou_Go_XJiku < 0) return; qreal Haba = Gazou_Go_XJiku - Gazou_Zen_XJiku; QRectF Kari_no_saizu(Gazou_Zen_XJiku, 0, Haba, recta.height()); QPainterPath targetPath1; targetPath1.addEllipse(Kahan_Daen_no_Chuushin, Kahan_Daen_no_X_Hankei, Kahan_Daen_no_Y_Hankei); QPainterPath targetPath2; targetPath2.addEllipse(Jouhan_Daen_no_Chuushin, Jouhan_Daen_no_X_Hankei, Jouhan_Daen_no_Y_Hankei); QPainterPath mergedTargetPath = targetPath1.united(targetPath2); QPainterPath entireArea; entireArea.addRect(recta); QPainterPath clipPath = entireArea.subtracted(mergedTargetPath); QPainterPath targetPath3; targetPath3.addRect(Kari_no_saizu); QPainterPath targetPath4 = clipPath.intersected(targetPath3); painter.setClipPath(targetPath4); qreal targetHeight = Kari_no_saizu.height() * Keisan_suru_shukusetsu_no_takasa(Gazou_Zen_XJiku); qreal aspectRatio = Ehon.width() / qreal(Ehon.height()); qreal targetWidth = targetHeight * aspectRatio; QRectF targetRect; targetRect.setSize(QSizeF(targetWidth, targetHeight)); targetRect.moveCenter(QPointF(Kari_no_saizu.center().rx(), Kari_no_saizu.height() * 0.45)); QTransform transform; QPointF center = targetRect.center(); transform.translate(center.x(), center.y()); transform.rotate(calculateRectRotation(Gazou_Zen_XJiku), Qt::YAxis, 2048.0); transform.translate(-center.x(), -center.y()); painter.setWorldTransform(transform); painter.drawPixmap(targetRect, Ehon, Ehon.rect()); QRect rect1(0, 0, recta.width(), recta.height()); QFont font1; font1.setPixelSize(recta.width() / 45); font1.setBold(true); font1.setWordSpacing(4); font1.setStyleHint(QFont::Decorative); painter.setFont(font1); QColor semiTransparent(255, 177, 248, 255); painter.setPen(semiTransparent); painter.drawText(Kari_no_saizu, Qt::AlignCenter, QString("夹心假面")); } void Daen_no_Kado::draw_text() { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); painter.setViewport(recta); painter.setWindow(recta); QRect rect1(0, 0, recta.width(), recta.height()); QFont font1; font1.setPixelSize(recta.width() / 35); font1.setBold(true); font1.setWordSpacing(4); font1.setStyleHint(QFont::Decorative); painter.setFont(font1); QColor semiTransparent(0, 0, 0, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, QString("Hello_World")); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() / 10)); painter.drawText(rect1, QString("Hello_World")); } void Daen_no_Kado::Draw_Progress_Bar() { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); painter.setViewport(recta); painter.setWindow(recta); painter.setPen(Qt::NoPen); QBrush brush; brush.setColor(QColor(129, 249, 255, m_Expand_Collapse_Opacity)); brush.setStyle(Qt::SolidPattern); painter.setBrush(brush); painter.drawRect(QRectF(m_Progress_Bar_X, m_Expand_Collapse_Height, m_Progress_Bar_Width, recta.height() * 0.04)); } void Daen_no_Kado::Progress_Bar_Data_Update() { if (Is_Execution_Step_Width) { m_Progress_Bar_Width += Progress_Bar_Step; m_Progress_Bar_X = 0; if (m_Progress_Bar_Width >= recta.width()) { Is_Execution_Step_Width = false; Is_Execution_Step_X = true; this->Card_Move_Right_Behavior(); } } else if (Is_Execution_Step_X) { m_Progress_Bar_X += Progress_Bar_Step; m_Progress_Bar_Width -= Progress_Bar_Step; if (m_Progress_Bar_X >= recta.width()) { m_Progress_Bar_Width = recta.width(); Is_Execution_Step_X = false; this->Card_Move_Right_Behavior(); } } else if (m_Progress_Bar_X > 0) { m_Progress_Bar_X -= Progress_Bar_Step; if (m_Progress_Bar_X <= 0) { m_Progress_Bar_X = 0; this->Card_Move_Right_Behavior(); } } else { m_Progress_Bar_Width -= Progress_Bar_Step; if (m_Progress_Bar_Width <= 0) { m_Progress_Bar_Width = 0; Is_Execution_Step_Width = true; this->Card_Move_Right_Behavior(); } } update(); } void Daen_no_Kado::koushin_suru() { for (auto& pair : Kado_Suu) { if (Execution_Direction == false) { pair.first -= Time_Step; pair.second -= Time_Step; } else if (Execution_Direction == true) { pair.first += Time_Step; pair.second += Time_Step; } } if (Kado_Suu[0].first < Left_Limit_Margin) { Kado_Suu.append(Kado_Suu.takeFirst()); Gazou_Shuu.append(Gazou_Shuu.takeFirst()); Kado_Suu[Kado_Suu.size() - 1].first = Kado_Suu[Kado_Suu.size() - 2].first + leftBound * 0.6888; Kado_Suu[Kado_Suu.size() - 1].second = Kado_Suu[Kado_Suu.size() - 2].second + leftBound * 0.6888; } else if (Kado_Suu[Kado_Suu.size() - 1].first > Right_Limit_Margin) { Kado_Suu.prepend(Kado_Suu.takeLast()); Gazou_Shuu.prepend(Gazou_Shuu.takeLast()); Kado_Suu[0].first = Kado_Suu[1].first - leftBound * 0.6888; Kado_Suu[0].second = Kado_Suu[1].second - leftBound * 0.6888; } if (mousePressed == true) { timerA.stop(); Ratio_Position = Kado_Suu[0].first / recta.width(); update(); return; } Kari_no_kankaku += Time_Step; if (Kari_no_kankaku >= leftBound * 0.6888) { timerA.stop(); Ima_no_joutai = true; Ratio_Position = Kado_Suu[0].first / recta.width(); } update(); } qreal Daen_no_Kado::Keisan_suru_shukusetsu_no_takasa(qreal& xLeft) { if (xLeft < leftBound) { qreal overflow = leftBound - xLeft; return overflow / leftBound / 4 + maxRadians; } else if (xLeft > leftBound) { qreal overflow = xLeft - leftBound; return overflow / leftBound / 4 + maxRadians; } else { return maxRadians; } } qreal Daen_no_Kado::calculateRectRotation(qreal& xLeft) { // 计算左侧影响 if (xLeft < leftBound) { qreal overflow = leftBound - xLeft; return -maxRadians2 * (overflow / leftBound); } else if (xLeft > leftBound) { qreal overflow = xLeft - leftBound; return maxRadians2 * (overflow / leftBound); } else return 0.0; } void Daen_no_Kado::Move_Left() { if (Ima_no_joutai != true) return; this->Card_Move_Left_Behavior(); } void Daen_no_Kado::Move_Right() { if (Ima_no_joutai != true) return; this->Card_Move_Right_Behavior(); } void Daen_no_Kado::Card_Move_Right_Behavior() { Ima_no_joutai = false; timerA.start(); Kari_no_kankaku = 0; Execution_Direction = true; } void Daen_no_Kado::Card_Move_Left_Behavior() { Ima_no_joutai = false; timerA.start(); Kari_no_kankaku = 0; Execution_Direction = false; } qreal Daen_no_Kado::Progress_Bar_X() const { return m_Progress_Bar_X; } void Daen_no_Kado::setProgress_Bar_X(qreal newProgress_Bar_X) { if (m_Progress_Bar_X == newProgress_Bar_X) return; m_Progress_Bar_X = newProgress_Bar_X; } qreal Daen_no_Kado::Progress_Bar_Width() const { return m_Progress_Bar_Width; } void Daen_no_Kado::setProgress_Bar_Width(qreal newProgress_Bar_Width) { if (m_Progress_Bar_Width == newProgress_Bar_Width) return; m_Progress_Bar_Width = newProgress_Bar_Width; } qreal Daen_no_Kado::Expand_Collapse_Height() const { return m_Expand_Collapse_Height; } void Daen_no_Kado::setExpand_Collapse_Height(qreal newExpand_Collapse_Height) { if (qFuzzyCompare(m_Expand_Collapse_Height, newExpand_Collapse_Height)) return; m_Expand_Collapse_Height = newExpand_Collapse_Height; update(); } int Daen_no_Kado::Expand_Collapse_Opacity() const { return m_Expand_Collapse_Opacity; } void Daen_no_Kado::setExpand_Collapse_Opacity(int newExpand_Collapse_Opacity) { if (m_Expand_Collapse_Opacity == newExpand_Collapse_Opacity) return; m_Expand_Collapse_Opacity = newExpand_Collapse_Opacity; update(); } ================================================ FILE: Anime_Template_Project/Adaptive_Carousel/daen_no_kado.h ================================================ #ifndef DAEN_NO_KADO_H #define DAEN_NO_KADO_H #include #include #include #include #include #include #include #include "latticed_circle_button.h" class Daen_no_Kado : public QWidget { Q_OBJECT Q_PROPERTY(qreal Progress_Bar_X READ Progress_Bar_X WRITE setProgress_Bar_X FINAL) Q_PROPERTY(qreal Progress_Bar_Width READ Progress_Bar_Width WRITE setProgress_Bar_Width FINAL) Q_PROPERTY(qreal Expand_Collapse_Height READ Expand_Collapse_Height WRITE setExpand_Collapse_Height FINAL) Q_PROPERTY(int Expand_Collapse_Opacity READ Expand_Collapse_Opacity WRITE setExpand_Collapse_Opacity FINAL) public: explicit Daen_no_Kado(QWidget* parent = nullptr); void Get_Image(); void Kiso_Deta_Kouchiku(); void Gazou_wo_nabebae_ru(); void Byouga_Ryouiki(qreal& Gazou_Zen_XJiku, qreal& Gazou_Go_XJiku, QPixmap& Ehon); void draw_text(); void Draw_Progress_Bar(); qreal calculateRectRotation(qreal& xLeft); qreal Keisan_suru_shukusetsu_no_takasa(qreal& xLeft); void animations(); QPropertyAnimation* animation; QPropertyAnimation* animation2; QPropertyAnimation* animation3; QPropertyAnimation* animation4; int zoom_rate = 10; Latticed_Circle_Button* button; Latticed_Circle_Button* button2; QTimer timerA; QTimer timerB; void Card_Move_Left_Behavior(); void Card_Move_Right_Behavior(); public: qreal Progress_Bar_X() const; void setProgress_Bar_X(qreal newProgress_Bar_X); qreal Progress_Bar_Width() const; void setProgress_Bar_Width(qreal newProgress_Bar_Width); qreal Expand_Collapse_Height() const; void setExpand_Collapse_Height(qreal newExpand_Collapse_Height); int Expand_Collapse_Opacity() const; void setExpand_Collapse_Opacity(int newExpand_Collapse_Opacity); public slots: void Move_Left(); void Move_Right(); void koushin_suru(); void Progress_Bar_Data_Update(); protected: void paintEvent(QPaintEvent* event); void resizeEvent(QResizeEvent* event); void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); bool event(QEvent* e); void wheelEvent(QWheelEvent* event); private: QVector Gazou_Shuu; QVector> Kado_Suu; qreal kankaku; qreal Kari_no_kankaku; qreal Left_Limit_Margin; qreal Right_Limit_Margin; qreal Substitute_Required_Spacing; bool Ima_no_joutai = true; bool Execution_Direction = false; QRect recta; qreal maxRadians = 0.5; qreal maxRadians2 = 28.0; qreal leftBound = 0.0; qreal Ratio_Position = -0.375; qreal Time_Step = 15.000; QPointF Jouhan_Daen_no_Chuushin; double Jouhan_Daen_no_Y_Hankei; double Jouhan_Daen_no_X_Hankei; QPointF Kahan_Daen_no_Chuushin; double Kahan_Daen_no_Y_Hankei; double Kahan_Daen_no_X_Hankei; bool mousePressed = false; int startX = 0; int accumulatedDistance = 0; qreal m_Progress_Bar_X = 0; qreal m_Progress_Bar_Width = 0; qreal Progress_Bar_Step = 1; bool Is_Execution_Step_Width = true; bool Is_Execution_Step_X = false; qreal m_Expand_Collapse_Height; int m_Expand_Collapse_Opacity = 255; }; #endif // DAEN_NO_KADO_H ================================================ FILE: Anime_Template_Project/Adaptive_Carousel/latticed_circle_button.cpp ================================================ #include "latticed_circle_button.h" Latticed_Circle_Button::Latticed_Circle_Button(QWidget* parent) : QPushButton{ parent } { this->resize(50, 50); this->setCursor(Qt::PointingHandCursor); QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this); shadow->setOffset(0, 13); shadow->setBlurRadius(30); shadow->setColor(QColor(0, 0, 0, 100)); this->setGraphicsEffect(shadow); animation1 = new QPropertyAnimation(this, "Tomeido"); animation1->setDuration(300); animation1->setStartValue(m_Tomeido); animation1->setEndValue(255); animation1->setEasingCurve(QEasingCurve::Linear); animation2 = new QPropertyAnimation(this, "Current_Color"); animation2->setDuration(300); animation2->setStartValue(m_Current_Color); animation2->setEndValue(QColor(255, 255, 255, 255)); animation2->setEasingCurve(QEasingCurve::Linear); } void Latticed_Circle_Button::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing); painter.setViewport(0, 0, width(), width()); painter.setWindow(0, 0, 50, 50); QPen pen; pen.setWidth(2.00); pen.setColor(Qt::black); painter.setPen(pen); QBrush brush; brush.setColor(QColor(0, 0, 0, m_Tomeido)); brush.setStyle(Qt::SolidPattern); painter.setBrush(brush); painter.drawEllipse(3, 3, 44, 44 ); QPen pen1; pen1.setWidth(4); pen1.setColor(m_Current_Color); pen1.setStyle(Qt::SolidLine); pen1.setCapStyle(Qt::FlatCap); pen1.setJoinStyle(Qt::MiterJoin); painter.setPen(pen1); if (Shikisai_no_joutai == true) { QPoint points[] = { QPoint(28, 15), QPoint(18, 25), QPoint(28, 35), }; painter.drawPolyline(points, 3); } else { QPoint points[] = { QPoint(23, 15), QPoint(33, 25), QPoint(23, 35), }; painter.drawPolyline(points, 3); } } bool Latticed_Circle_Button::event(QEvent* e) { if (e->type() == QEvent::Enter) { animation1->setDirection(QPropertyAnimation::Forward); animation1->start(); animation2->setDirection(QPropertyAnimation::Forward); animation2->start(); emit execute_animation_signal(AnimationState::Execute); update(); } else if (e->type() == QEvent::Leave) { animation1->setDirection(QPropertyAnimation::Backward); animation1->start(); animation2->setDirection(QPropertyAnimation::Backward); animation2->start(); emit execute_animation_signal(AnimationState::Restore); update(); } return QPushButton::event(e); } void Latticed_Circle_Button::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) emit Latticed_Circle_Click_Signal(); } int Latticed_Circle_Button::Tomeido() const { return m_Tomeido; } void Latticed_Circle_Button::setTomeido(int newTomeido) { if (m_Tomeido == newTomeido) return; m_Tomeido = newTomeido; update(); } QColor Latticed_Circle_Button::Current_Color() const { return m_Current_Color; } void Latticed_Circle_Button::setCurrent_Color(const QColor &newCurrent_Color) { if (m_Current_Color == newCurrent_Color) return; m_Current_Color = newCurrent_Color; update(); } ================================================ FILE: Anime_Template_Project/Adaptive_Carousel/latticed_circle_button.h ================================================ #ifndef LATTICED_CIRCLE_BUTTON_H #define LATTICED_CIRCLE_BUTTON_H #include #include #include #include #include class Latticed_Circle_Button : public QPushButton { Q_OBJECT Q_PROPERTY(int Tomeido READ Tomeido WRITE setTomeido FINAL) Q_PROPERTY(QColor Current_Color READ Current_Color WRITE setCurrent_Color FINAL) public: explicit Latticed_Circle_Button(QWidget* parent = nullptr); enum AnimationState { Execute, Restore }; QPropertyAnimation *animation1; QPropertyAnimation* animation2; bool Shikisai_no_joutai = false; int Tomeido() const; void setTomeido(int newTomeido); QColor Current_Color() const; void setCurrent_Color(const QColor &newCurrent_Color); signals: void Latticed_Circle_Click_Signal(); void execute_animation_signal(Latticed_Circle_Button::AnimationState state); protected: void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); bool event(QEvent* e); private: int m_Tomeido = 0; QColor m_Current_Color = QColor(0, 0, 0, 255); }; #endif // LATTICED_CIRCLE_BUTTON_H ================================================ FILE: Anime_Template_Project/Blur_Text/fuzzytextwidget.cpp ================================================ #include "FuzzyTextWidget.h" #include #include #include #include #include #include #include #include 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(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(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(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(); } ================================================ FILE: Anime_Template_Project/Blur_Text/fuzzytextwidget.h ================================================ #ifndef FUZZYTEXTWIDGET_H #define FUZZYTEXTWIDGET_H #include #include #include #include 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 ================================================ FILE: Anime_Template_Project/Carousel_card/card_button.cpp ================================================ #include "card_button.h" Card_button::Card_button(QString text, QColor button_color, QWidget* parent) : QPushButton{ parent } { this->btntext = text; this->m_button_color = button_color; this->resize(122, 32); this->color_control(); animation = new QPropertyAnimation(this, "color_opacity"); animation->setDuration(100); animation->setStartValue(this->color_opacity()); animation->setEndValue(0); connect(this, &Card_button::pressed, this, &Card_button::openWebsite); } void Card_button::openWebsite() { QDesktopServices::openUrl(QUrl(website_url)); } void Card_button::setWebsite_url(QString url) { this->website_url = url; } void Card_button::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, 122, 32); QPainterPath path; path.addRoundedRect(0, 0, 122, 32, 10, 10); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush Brush(color1); painter.setBrush(Brush); painter.drawRoundedRect(0, 0, 122, 32, 8, 8); color2.setAlpha(m_color_opacity); QBrush Brush1(color2); painter.setBrush(Brush1); painter.drawRoundedRect(0, -3, 122, 32, 8, 8); QPen pen; pen.setWidth(1); pen.setColor(Qt::white); pen.setStyle(Qt::SolidLine); pen.setCapStyle(Qt::FlatCap); pen.setJoinStyle(Qt::BevelJoin); painter.setPen(pen); QPoint points[] = { QPoint(28, 10), QPoint(33, 15), QPoint(28, 20), }; painter.drawPolyline(points, 3); painter.setPen(Qt::white); QFont font("Microsoft YaHei", 10, QFont::Normal); painter.setFont(font); painter.drawText(45, 21, btntext); } bool Card_button::event(QEvent* e) { if (e->type() == QEvent::Enter) { animation->setDirection(QPropertyAnimation::Forward); animation->start(); qDebug() << "鼠标进入"; } else if (e->type() == QEvent::Leave) { animation->setDirection(QPropertyAnimation::Backward); animation->start(); qDebug() << "鼠标离开"; update(); } return QPushButton::event(e); } QColor Card_button::getbutton_color() const { return m_button_color; } void Card_button::setButton_color(const QColor &newButton_color) { if (m_button_color == newButton_color) return; m_button_color = newButton_color; update(); } void Card_button::color_control() { m_button_color.setAlpha(255); color1 = m_button_color; color2 = QColor(m_button_color.red() * 0.8, m_button_color.green() * 0.8, m_button_color.blue() * 0.8); } int Card_button::color_opacity() const { return m_color_opacity; } void Card_button::setcolor_opacity(int newColor_opacity) { if (m_color_opacity == newColor_opacity) return; m_color_opacity = newColor_opacity; update(); } ================================================ FILE: Anime_Template_Project/Carousel_card/card_button.h ================================================ #ifndef CARD_BUTTON_H #define CARD_BUTTON_H #include #include #include #include #include #include #include class Card_button : public QPushButton { Q_OBJECT Q_PROPERTY(QColor button_color READ getbutton_color WRITE setButton_color FINAL) Q_PROPERTY(int color_opacity READ color_opacity WRITE setcolor_opacity FINAL) public: explicit Card_button(QString text, QColor button_color, QWidget* parent = nullptr); QColor getbutton_color() const; void setButton_color(const QColor &newButton_color); public: QString btntext; void color_control(); QColor color1; QColor color2; int color_opacity() const; void setcolor_opacity(int newColor_opacity); void openWebsite(); void setWebsite_url(QString url); protected: void paintEvent(QPaintEvent* event); bool event(QEvent* e); private: QPropertyAnimation* animation; QString website_url; QColor m_button_color; int m_color_opacity = 255; }; #endif // CARD_BUTTON_H ================================================ FILE: Anime_Template_Project/Carousel_card/card_text.cpp ================================================ #include "card_text.h" Card_text::Card_text(QString text, QString text1, QWidget *parent) : QWidget{parent} { this->text_main = text; this->text_sub = text1; } void Card_text::paintEvent(QPaintEvent *event) { QPainter painter(this); QRect rect(0, 0, width(), height()); painter.setViewport(rect); painter.setWindow(0, 0, m_shrink_width, height()); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, m_shrink_width, 0); QFont font1; font1.setPointSize(m_font_size); font1.setBold(true); painter.setFont(font1); QColor semiTransparent(255, 255, 255, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignBottom, text_main); rect1.setHeight(actualRect.height()); painter.drawText(rect1, text_main); QRect rect2(2, actualRect.height() + 7, width() - width()/4, 0); QFont font; font.setPointSize(m_font_size2); font.setBold(true); painter.setFont(font); QColor semiTransparent1(255, 255, 255, m_enlarge_width); painter.setPen(semiTransparent1); QRect actualRect1 = painter.boundingRect(rect2, Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignBottom, text_sub); rect2.setHeight(actualRect1.height()); painter.drawText(rect2, text_sub); this->resize(m_shrink_width, actualRect1.height() + actualRect.height() + 7); this->move(38, 480 - actualRect1.height() - actualRect.height() - m_top_margin); } void Card_text::start_animation(int animation_duration) { QPropertyAnimation *animation = new QPropertyAnimation(this, "shrink_width"); animation->setDuration(animation_duration); animation->setStartValue(this->m_shrink_width); animation->setEndValue(280); animation->setEasingCurve(QEasingCurve::Linear); animation->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation1 = new QPropertyAnimation(this, "enlarge_width"); animation1->setDuration(animation_duration); animation1->setStartValue(this->m_enlarge_width); animation1->setEndValue(255); animation1->setEasingCurve(QEasingCurve::Linear); animation1->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation2 = new QPropertyAnimation(this, "font_size"); animation2->setDuration(animation_duration); animation2->setStartValue(this->m_font_size); animation2->setEndValue(23); animation2->setEasingCurve(QEasingCurve::Linear); animation2->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation3 = new QPropertyAnimation(this, "font_size2"); animation3->setDuration(animation_duration); animation3->setStartValue(this->m_font_size2); animation3->setEndValue(10); animation3->setEasingCurve(QEasingCurve::Linear); animation3->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation4 = new QPropertyAnimation(this, "top_margin"); animation4->setDuration(animation_duration); animation4->setStartValue(this->m_top_margin); animation4->setEndValue(232); animation4->setEasingCurve(QEasingCurve::Linear); animation4->start(QAbstractAnimation::DeleteWhenStopped); } void Card_text::reset_animation(int animation_duration) { QPropertyAnimation *animation = new QPropertyAnimation(this, "shrink_width"); animation->setDuration(animation_duration); animation->setStartValue(this->m_shrink_width); animation->setEndValue(200); animation->setEasingCurve(QEasingCurve::Linear); animation->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation1 = new QPropertyAnimation(this, "enlarge_width"); animation1->setDuration(animation_duration); animation1->setStartValue(this->m_enlarge_width); animation1->setEndValue(0); animation1->setEasingCurve(QEasingCurve::Linear); animation1->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation2 = new QPropertyAnimation(this, "font_size"); animation2->setDuration(animation_duration); animation2->setStartValue(this->m_font_size); animation2->setEndValue(15); animation2->setEasingCurve(QEasingCurve::Linear); animation2->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation3 = new QPropertyAnimation(this, "font_size2"); animation3->setDuration(animation_duration); animation3->setStartValue(this->m_font_size2); animation3->setEndValue(1); animation3->setEasingCurve(QEasingCurve::Linear); animation3->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation4 = new QPropertyAnimation(this, "top_margin"); animation4->setDuration(animation_duration); animation4->setStartValue(this->m_top_margin); animation4->setEndValue(210); animation4->setEasingCurve(QEasingCurve::Linear); animation4->start(QAbstractAnimation::DeleteWhenStopped); } int Card_text::shrink_width() const { return m_shrink_width; } void Card_text::setShrink_width(int newShrink_width) { m_shrink_width = newShrink_width; update(); } int Card_text::enlarge_width() const { return m_enlarge_width; } void Card_text::setEnlarge_width(int newEnlarge_width) { m_enlarge_width = newEnlarge_width; update(); } int Card_text::font_size() const { return m_font_size; } void Card_text::setFont_size(int newFont_size) { m_font_size = newFont_size; update(); } int Card_text::font_size2() const { return m_font_size2; } void Card_text::setFont_size2(int newFont_size2) { m_font_size2 = newFont_size2; update(); } int Card_text::top_margin() const { return m_top_margin; } void Card_text::setTop_margin(int newTop_margin) { m_top_margin = newTop_margin; update(); } ================================================ FILE: Anime_Template_Project/Carousel_card/card_text.h ================================================ #ifndef CARD_TEXT_H #define CARD_TEXT_H #include #include #include #include #include class Card_text : public QWidget { Q_OBJECT Q_PROPERTY(int shrink_width READ shrink_width WRITE setShrink_width FINAL) Q_PROPERTY(int enlarge_width READ enlarge_width WRITE setEnlarge_width FINAL) Q_PROPERTY(int font_size READ font_size WRITE setFont_size FINAL) Q_PROPERTY(int font_size2 READ font_size2 WRITE setFont_size2 FINAL) Q_PROPERTY(int top_margin READ top_margin WRITE setTop_margin FINAL) public: explicit Card_text(QString text, QString text1, QWidget *parent = nullptr); QString text_main; QString text_sub; void start_animation(int animation_duration); void reset_animation(int animation_duration); int shrink_width() const; void setShrink_width(int newShrink_width); int enlarge_width() const; void setEnlarge_width(int newEnlarge_width); int font_size() const; void setFont_size(int newFont_size); int font_size2() const; void setFont_size2(int newFont_size2); int top_margin() const; void setTop_margin(int newTop_margin); protected: void paintEvent(QPaintEvent *event); private: int m_shrink_width = 200; int m_enlarge_width = 0; int m_font_size = 15; int m_font_size2 = 1; int m_top_margin = 210; }; #endif // CARD_TEXT_H ================================================ FILE: Anime_Template_Project/Carousel_card/carousel_card.cpp ================================================ #include "carousel_card.h" Carousel_card::Carousel_card(QWidget *parent) : QWidget{parent} { this->setMaximumSize(1900, 530); this->setMinimumSize(1300, 530); this->Anime_basic_information(); this->Anime_cards_sorting(Anime_seven_cards_list, Anime_cards_startX, Anime_cards_cardWidth, Anime_cards_spacing); } void Carousel_card::Anime_card_position_update(int m_carrier_card_id) { qDebug() << "m_carrier_card_id" << m_carrier_card_id; int timeA = animation_duration; if (m_carrier_card_id > 2) { int x = m_carrier_card_id - 2; int j = x; for (int i = 0; i < x; i++) { timeA = timeA - 70; Anime_seven_cards_list.append(Anime_seven_cards_list.takeFirst()); for (Carrier_card* Anime_cards : Anime_seven_cards_list) { if (Anime_cards == nullptr) continue; int j = Anime_seven_cards_list.indexOf(Anime_cards); Anime_cards->setcarrier_card_id(j); if (Anime_seven_cards_list.length() - 1 == j) Anime_cards->move(Anime_seven_cards_zasyo_list[j]); this->Anime_Anima_set(Anime_cards, Anime_seven_cards_zasyo_list[j], timeA); } } j--; } else if (m_carrier_card_id < 2) { int x = 2 - m_carrier_card_id; Anime_seven_cards_list.prepend(Anime_seven_cards_list.takeLast()); for (Carrier_card* Anime_cards : Anime_seven_cards_list) { if (Anime_cards == nullptr) continue; int j = Anime_seven_cards_list.indexOf(Anime_cards); Anime_cards->setcarrier_card_id(j); if (Anime_cards->carrier_card_id() == 0) Anime_cards->move(Anime_seven_cards_zasyo_list.first()); if (Anime_seven_cards_list.length() - 1 == j) Anime_cards->move(Anime_seven_cards_zasyo_list[j]); this->Anime_Anima_set(Anime_cards, Anime_seven_cards_zasyo_list[j], timeA); } } } void Carousel_card::onwheel_TimerTimeout() { clickTimer.stop(); } void Carousel_card::Anime_Anima_set(Carrier_card* Anime_cards,QPoint Anime_zasyo, int Anime_time) { Anime_cards->raise(); Anime_cards->Anime_card_transformation(Anime_time); QPropertyAnimation* Anime_anima = new QPropertyAnimation(Anime_cards, "pos"); Anime_anima->setDuration(Anime_time); Anime_anima->setStartValue(Anime_cards->pos()); Anime_anima->setEndValue(Anime_zasyo); Anime_anima->setEasingCurve(QEasingCurve::Linear); Anime_anima->start(QAbstractAnimation::DeleteWhenStopped); } void Carousel_card::Anime_basic_information() { QFile file(":/json/json/Anime_care_attributes.json"); if (!file.open(QIODevice::ReadOnly)) { qDebug() << "打开文件失败"; return; } QByteArray jsonData = file.readAll(); file.close(); QJsonDocument document = QJsonDocument::fromJson(jsonData); if (document.isNull() && !document.isObject()) { qDebug() << "解析JSON失败"; return; } QJsonObject rootObject = document.object(); QJsonArray hobbiesArray = rootObject["Anime_img_str_list"].toArray(); for (const QJsonValue& value : hobbiesArray) { QJsonObject addressObject = value.toObject(); if (!QFile::exists(addressObject["img"].toString())) { qDebug() << "图片资源文件不存在"; continue; } Carrier_card* Anime_cards = new Carrier_card(QPixmap(addressObject["img"].toString()), addressObject["str_1"].toString(), addressObject["str_2"].toString(), this); Anime_cards->Anime_button->setWebsite_url(addressObject["anime_url"].toString()); connect(Anime_cards, &Carrier_card::carrier_card_idChanged, this, &Carousel_card::Anime_card_position_update); QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(Anime_cards); shadow->setOffset(0, 0); shadow->setBlurRadius(80); shadow->setColor(Anime_cards->m_color3); Anime_cards->setGraphicsEffect(shadow); Anime_seven_cards_list.append(Anime_cards); } connect(this, &Carousel_card::wheel_signalChanged, this, &Carousel_card::Anime_card_position_update); connect(&clickTimer, &QTimer::timeout, this, &Carousel_card::onwheel_TimerTimeout); } void Carousel_card::Anime_cards_sorting(QList &Anime_seven_cards_list, int Anime_cards_startX, int Anime_cards_cardWidth, int Anime_cards_spacing) { int currentX = Anime_cards_startX; for (Carrier_card* Anime_cards : Anime_seven_cards_list) { if (Anime_cards == nullptr) { continue; } Anime_cards->setcarrier_card_id(Anime_seven_cards_list.indexOf(Anime_cards)); if (Anime_cards->carrier_card_id() == 2) { Anime_cards->Anime_card_1(); } if (Anime_cards->carrier_card_id() == 3) { currentX += 540; } Anime_cards->move(currentX, 13); Anime_seven_cards_zasyo_list.append(QPoint(currentX, 13)); currentX += Anime_cards_cardWidth + Anime_cards_spacing; } } void Carousel_card::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::RightButton) { emit wheel_signalChanged(San); } QWidget::mousePressEvent(event); } void Carousel_card::wheelEvent(QWheelEvent* event) { if (event->angleDelta().y() > 0) { if (!clickTimer.isActive()) { emit wheel_signalChanged(Yizi); clickTimer.start(310); } } else if (event->angleDelta().y() < 0) { if (!clickTimer.isActive()) { emit wheel_signalChanged(San); clickTimer.start(170); } } } ================================================ FILE: Anime_Template_Project/Carousel_card/carousel_card.h ================================================ #ifndef CAROUSEL_CARD_H #define CAROUSEL_CARD_H #include #include "carrier_card.h" #include #include #include #include #include #include #include #include class Carousel_card : public QWidget { Q_OBJECT public: explicit Carousel_card(QWidget *parent = nullptr); public: QList Anime_seven_cards_list; QList Anime_seven_cards_zasyo_list; void Anime_basic_information(); void Anime_cards_sorting(QList& Anime_seven_cards_list, int Anime_cards_startX, int Anime_cards_cardWidth, int Anime_cards_spacing); void Anime_Anima_set(Carrier_card* Anime_cards, QPoint Anime_zasyo, int Anime_time); public slots: void Anime_card_position_update(int m_carrier_card_id); void onwheel_TimerTimeout(); signals: void wheel_signalChanged(int wheel_signal); protected: void mousePressEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); private: QTimer clickTimer; int Yizi = 1; int San = 3; int Anime_cards_startX = -628; int Anime_cards_cardWidth = 320; int Anime_cards_spacing = 13; int animation_duration = 410; }; #endif // CAROUSEL_CARD_H ================================================ FILE: Anime_Template_Project/Carousel_card/carrier_card.cpp ================================================ #include "carrier_card.h" Carrier_card::Carrier_card(QPixmap m_carrier_card, QString main_title, QString sub_title,QWidget *parent) : QWidget{parent} { this->setMaximumSize(860, 480); this->resize(320, 480); this->m_carrier_card = m_carrier_card; this->font_related_construction(main_title, sub_title); this->set_gradient_color(); setMouseTracking(true); connect(&m_clickTimer, &QTimer::timeout, this, &Carrier_card::onClickTimerTimeout); connect(&timer4s, &QTimer::timeout, this, &Carrier_card::circle_reset); shadow = new QGraphicsDropShadowEffect(Anime_button); shadow->setOffset(0, 0); shadow->setBlurRadius(150); shadow->setColor(Qt::black); Anime_button->setGraphicsEffect(shadow); } void Carrier_card::font_related_construction(QString main_title, QString sub_title) { Anime_small_text = new Card_text(main_title, sub_title,this); Anime_small_text->reset_animation(0); qDebug() << "Card_text::" << Anime_small_text->height(); } void Carrier_card::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { if (!m_clickTimer.isActive()) { emit carrier_card_idChanged(this->m_carrier_card_id); m_clickTimer.start(500); } } QWidget::mousePressEvent(event); } void Carrier_card::mouseMoveEvent(QMouseEvent* event) { if (this->isClicked == true) { mousePos = event->pos(); update(); } } bool Carrier_card::event(QEvent* e) { if (e->type() == QEvent::Enter) { this->isClicked = true; if (this->m_carrier_card_id == 2) timer4s.stop(); } else if (e->type() == QEvent::Leave) { this->isClicked = false; if (this->m_carrier_card_id == 2) timer4s.start(100); update(); } return QWidget::event(e); } void Carrier_card::draw_radial_gradient_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QRadialGradient gradient1(mousePos.rx(), mousePos.ry(), qMax(width() , height() ) / 2); gradient1.setColorAt(0.0, QColor(255, 255, 255, 15)); gradient1.setColorAt(0.8, QColor(255, 255, 255, 0)); gradient1.setColorAt(1.0, QColor(255, 255, 255, 0)); painter.setBrush(gradient1); painter.setPen(Qt::NoPen); painter.drawRect(rect()); } void Carrier_card::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.setRenderHint(QPainter::LosslessImageRendering); QPainterPath path; path.addRoundedRect(0, 0, this->width(), this->height(), 10, 10); painter.setClipPath(path); int windowWidth = width(); int windowHeight = height(); double aspectRatio = static_cast(m_carrier_card.width()) / m_carrier_card.height(); int newWidth = static_cast(windowHeight * aspectRatio); int newHeight = windowHeight; int posX = (windowWidth - newWidth) / 2; int posY = 0; painter.drawPixmap(posX, posY, newWidth, newHeight, m_carrier_card, 0, 0, m_carrier_card.width(), m_carrier_card.height()); QLinearGradient gradient(0, 480, 860, 480); gradient.setColorAt(0, m_color1); gradient.setColorAt(0.35, m_color2); gradient.setColorAt(0.55, QColor(0, 0, 0, 0)); gradient.setColorAt(1, QColor(0, 0, 0, 0)); gradient.setSpread(QGradient::PadSpread); painter.setBrush(gradient); painter.setPen(Qt::NoPen); painter.drawRect(0, 0, width(), height()); QColor semiTransparentRed(0, 0, 0, m_opacity); painter.setBrush(semiTransparentRed); painter.drawRect(0, 0, width(), height()); if (isClicked == true) this->draw_radial_gradient_circle(); if (m_carrier_card_id == 2 && display_circle == true) draw_sector_circle(); } void Carrier_card::draw_sector_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); painter.setBrush(QColor(255, 255, 255, 140)); QRect rect(20, 450, 15, 15); int startAngle = 0 * 16; int spanAngle = m_circle_degrees * 16; painter.drawPie(rect, startAngle, spanAngle); } void Carrier_card::circle_reset() { TimerTimeout++; update(); if (TimerTimeout == 15) { display_circle = true; } if (TimerTimeout < 87 && display_circle == true) { m_circle_degrees -= 5; } if (TimerTimeout == 87) { restore_disc(); emit carrier_card_idChanged(3); } } void Carrier_card::set_gradient_color() { QColor clr = this->getMostFrequentColor(m_carrier_card); Anime_button = new Card_button(QString("查看详情"), clr, this); Anime_button->move(38, 315); clr.setAlpha(100); m_color1 = clr; clr.setAlpha(68); m_color2 = clr; clr.setAlpha(255); m_color3 = clr; } QColor Carrier_card::getMostFrequentColor(const QPixmap& pixmap) { QImage image = pixmap.toImage(); if (image.isNull()) { return QColor(); } image = image.convertToFormat(QImage::Format_RGB32); QHash colorCounts; const uchar* bits = image.constBits(); const int width = image.width(); const int height = image.height(); const int bytesPerLine = image.bytesPerLine(); const qreal minSaturation = 8.1; const qreal maxLightnessForWhite = 0.99; for (int y = 0; y < height; ++y) { const QRgb* line = reinterpret_cast(bits + y * bytesPerLine); for (int x = 0; x < width; ++x) { QRgb rgb = line[x]; QColor color = QColor::fromRgb(rgb); float h, s, l; color.getHslF(&h, &s, &l); if (s < minSaturation && l > maxLightnessForWhite) { continue; } colorCounts[rgb]++; } } if (colorCounts.isEmpty()) { return QColor(); } QHashIterator it(colorCounts); QRgb maxColor = it.peekNext().key(); int maxCount = it.peekNext().value(); while (it.hasNext()) { it.next(); if (it.value() > maxCount) { maxCount = it.value(); maxColor = it.key(); } } return QColor::fromRgb(maxColor); } void Carrier_card::Anime_card_transformation(int animation_duration) { if (m_carrier_card_id != 2) { timer4s.stop(); restore_disc(); Anime_small_text->reset_animation(animation_duration); QPropertyAnimation* animation1 = new QPropertyAnimation(this, "size"); animation1->setDuration(animation_duration); animation1->setStartValue(this->size()); animation1->setEndValue(QSize(320, 480)); animation1->setEasingCurve(QEasingCurve::Linear); QPropertyAnimation* animation3 = new QPropertyAnimation(this, "opacity"); animation3->setDuration(animation_duration); animation3->setStartValue(this->opacity()); animation3->setEndValue(90); animation3->setEasingCurve(QEasingCurve::Linear); animation1->start(QAbstractAnimation::DeleteWhenStopped); animation3->start(QAbstractAnimation::DeleteWhenStopped); } else { timer4s.start(100); Anime_small_text->start_animation(animation_duration); QPropertyAnimation* animation1 = new QPropertyAnimation(this, "size"); animation1->setDuration(animation_duration); animation1->setStartValue(this->size()); animation1->setEndValue(QSize(860, 480)); animation1->setEasingCurve(QEasingCurve::Linear); QPropertyAnimation* animation3 = new QPropertyAnimation(this, "opacity"); animation3->setDuration(animation_duration); animation3->setStartValue(this->opacity()); animation3->setEndValue(0); animation3->setEasingCurve(QEasingCurve::Linear); animation1->start(QAbstractAnimation::DeleteWhenStopped); animation3->start(QAbstractAnimation::DeleteWhenStopped); } } void Carrier_card::Anime_card_1() { this->resize(860, 480); Anime_small_text->start_animation(0); this->setOpacity(0); timer4s.start(100); } void Carrier_card::onClickTimerTimeout() { m_clickTimer.stop(); } void Carrier_card::restore_disc() { display_circle = false; TimerTimeout = 0; m_circle_degrees = 360; } int Carrier_card::carrier_card_id() const { return m_carrier_card_id; } void Carrier_card::setcarrier_card_id(int newCarrier_card_id) { if (m_carrier_card_id == newCarrier_card_id) return; m_carrier_card_id = newCarrier_card_id; update(); } int Carrier_card::opacity() const { return m_opacity; } void Carrier_card::setOpacity(int newOpacity) { m_opacity = newOpacity; update(); } int Carrier_card::circle_degrees() const { return m_circle_degrees; } void Carrier_card::setCircle_degrees(int newCircle_degrees) { if (m_circle_degrees == newCircle_degrees) return; m_circle_degrees = newCircle_degrees; qDebug() << "circle_degrees changed to:" << m_circle_degrees; update(); } ================================================ FILE: Anime_Template_Project/Carousel_card/carrier_card.h ================================================ #ifndef CARRIER_CARD_H #define CARRIER_CARD_H #include #include #include #include #include #include #include #include #include #include "card_text.h" #include "Card_button.h" class Carrier_card : public QWidget { Q_OBJECT Q_PROPERTY(int carrier_card_id READ carrier_card_id WRITE setcarrier_card_id NOTIFY carrier_card_idChanged FINAL) Q_PROPERTY(int opacity READ opacity WRITE setOpacity FINAL) Q_PROPERTY(int circle_degrees READ circle_degrees WRITE setCircle_degrees FINAL) public: explicit Carrier_card(QPixmap m_carrier_card, QString main_title, QString sub_title, QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); bool event(QEvent* e); public: QTimer m_clickTimer; QTimer timer4s; int TimerTimeout = 0; bool display_circle = false; void restore_disc(); void onClickTimerTimeout(); void circle_reset(); QPoint mousePos; bool isClicked = false; void draw_radial_gradient_circle(); public: Card_text* Anime_small_text; int position_small_after_enlargement = 267; int basic_small_position = 245; void font_related_construction(QString main_title, QString sub_title); Card_button* Anime_button; public: int carrier_card_id() const; QColor m_color1; QColor m_color2; QColor m_color3; int opacity() const; void setOpacity(int newOpacity); int circle_degrees() const; void setCircle_degrees(int newCircle_degrees); void Anime_card_1(); void setcarrier_card_id(int newCarrier_card_id); QColor getMostFrequentColor(const QPixmap& pixmap); void set_gradient_color(); void draw_sector_circle(); public slots: void Anime_card_transformation(int animation_duration); signals: void carrier_card_idChanged(int m_carrier_card_id); private: QGraphicsDropShadowEffect* shadow; QPixmap m_carrier_card; int m_carrier_card_id = 0; int m_opacity = 90; int m_circle_degrees = 360; }; #endif // CARRIER_CARD_H ================================================ FILE: Anime_Template_Project/Eye_of_Cthulhu/JellyfishWidget.cpp ================================================ #include "jellyfishwidget.h" #include #include #include #include #include JellyfishWidget::JellyfishWidget(QWidget *parent) : QWidget(parent), x_input(0.0), y_input(0.0), t_current(0.0), mousePosition(width() / 2.0, height() / 2.0), smoothedPupilTarget(0.0, 0.0), mouseIsInWindow(false), blinkProgress(0.0), isBlinking(false), pulseScale(1.0), m_targetPupilScaleFactor(1.0), m_currentPupilScaleFactor(1.0) { setWindowTitle("眼球克苏鲁"); resize(800, 600); setMouseTracking(true); animationTimer = new QTimer(this); connect(animationTimer, &QTimer::timeout, this, &JellyfishWidget::updateAnimation); animationTimer->start(15); for (int i = 0; i < 3; ++i) { TentacleShadow shadow; shadow.opacity = QRandomGenerator::global()->generateDouble() * 0.1 + 0.05; shadow.phaseOffset = QRandomGenerator::global()->generateDouble() * 100.0; shadow.thickness = QRandomGenerator::global()->bounded(50, 150); for (int j = 0; j < 5; ++j) { shadow.points.append(QPointF( QRandomGenerator::global()->bounded(-100, width() + 100), QRandomGenerator::global()->bounded(height() / 2, height() + 100) )); } backgroundTentacleShadows.append(shadow); } } JellyfishWidget::~JellyfishWidget() { } QPointF JellyfishWidget::calculateJellyfishPoint(double local_x, double local_y, double current_t) { double x_formula = local_x; double y_formula = local_y; double t = current_t; double k = 5.0 * qCos(x_formula / 14.0) * qCos(y_formula / 30.0); double e = y_formula / 8.0 - 13.0; double d = (k * k + e * e) / 59.0 + 4.0; double a = qAtan2(e, k); double d_safe = (d < 0.001 && d >= 0) ? 0.001 : d; double q = 60.0 - qSin(a * e) + k * (3.0 + (4.0 / d_safe) * qSin(d * d - 2.0 * t)); double c = d / 2.0 + e / 99.0 - t / 18.0; double X_result = q * qSin(c); double Y_result = (q + 9.0 * d) * qCos(c); return QPointF(X_result, Y_result); } void JellyfishWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(rect(), QColor(20, 0, 0)); drawBackgroundTentacleShadows(&painter); QPointF cthulhuCenter = QPointF(width() / 2.0, height() / 2.0); QPointF currentCthulhuPosition = calculateJellyfishPoint(x_input, y_input, t_current) + cthulhuCenter; double currentEyeballRadius = 80.0 * pulseScale; double currentEyeballHeight = 40.0 * pulseScale; drawEyeball(&painter, currentCthulhuPosition, currentEyeballRadius, currentEyeballHeight); drawGoreTendrils(&painter, currentCthulhuPosition, currentEyeballRadius * 0.9, 15, t_current); drawFallingGoreAndBlood(&painter); drawAcidDrips(&painter); } void JellyfishWidget::mouseMoveEvent(QMouseEvent *event) { mousePosition = event->pos(); mouseIsInWindow = true; } void JellyfishWidget::leaveEvent(QEvent *event) { Q_UNUSED(event); mousePosition = QPointF(width() / 2.0, height() / 2.0); mouseIsInWindow = false; } void JellyfishWidget::updateAnimation() { t_current += 0.02; QPointF cthulhuCenter = QPointF(width() / 2.0, height() / 2.0); QPointF currentCthulhuPosition = calculateJellyfishPoint(x_input, y_input, t_current) + cthulhuCenter; double eyeball_radius = 80.0; double maxPupilTravel = eyeball_radius * 0.4; QPointF targetPupilOffset; double basePupilRatio = 0.3; double minPupilScale = 0.8; double maxPupilScale = 1.2; double influenceDistance = 200.0; if (mouseIsInWindow) { QPointF vecToMouse = mousePosition - currentCthulhuPosition; double distToMouse = qSqrt(vecToMouse.x() * vecToMouse.x() + vecToMouse.y() * vecToMouse.y()); if (distToMouse > 0) { targetPupilOffset = vecToMouse / distToMouse * qMin(distToMouse, maxPupilTravel); } else { targetPupilOffset = QPointF(0, 0); } double normalizedDist = qMin(distToMouse, influenceDistance) / influenceDistance; m_targetPupilScaleFactor = minPupilScale + (1.0 - normalizedDist) * (maxPupilScale - minPupilScale); } else { QPointF wanderOffset(qSin(t_current * 0.7) * 20, qCos(t_current * 0.9) * 15); double wander_magnitude = qSqrt(wanderOffset.x()*wanderOffset.x() + wanderOffset.y()*wanderOffset.y()); if (wander_magnitude > maxPupilTravel) { wanderOffset = wanderOffset / wander_magnitude * maxPupilTravel; } targetPupilOffset = wanderOffset; m_targetPupilScaleFactor = 1.0; } double smoothness_factor = 0.05; smoothedPupilTarget = smoothedPupilTarget * (1.0 - smoothness_factor) + targetPupilOffset * smoothness_factor; double pupil_smoothness = 0.1; m_currentPupilScaleFactor += (m_targetPupilScaleFactor - m_currentPupilScaleFactor) * pupil_smoothness; if (isBlinking) { blinkProgress += 0.06; if (blinkProgress >= 1.0) { isBlinking = false; blinkProgress = 0.0; } } else { if (QRandomGenerator::global()->generateDouble() < 0.003) { isBlinking = true; blinkProgress = 0.0; } } double targetPulse = 1.0 + qSin(t_current * 5.0) * 0.02; pulseScale += (targetPulse - pulseScale) * 0.1; ParticleUpdateRunnable *runnable = new ParticleUpdateRunnable( t_current, height(), width(), currentCthulhuPosition, 80.0, fallingGoreParticles, acidDrips, backgroundTentacleShadows ); connect(runnable, &ParticleUpdateRunnable::particlesUpdated, this, &JellyfishWidget::handleParticlesUpdated, Qt::QueuedConnection); QThreadPool::globalInstance()->start(runnable); update(); } void JellyfishWidget::handleParticlesUpdated(QVector newGore, QVector newAcid, QVector newShadows) { fallingGoreParticles = newGore; acidDrips = newAcid; backgroundTentacleShadows = newShadows; } void JellyfishWidget::drawEyeball(QPainter *painter, const QPointF& center, double radius, double bell_height) { QRadialGradient eyeballGradient(center.x(), center.y() - bell_height / 2, radius * 1.5, center.x(), center.y()); eyeballGradient.setColorAt(0.0, QColor(255, 50, 50, 200)); eyeballGradient.setColorAt(0.4, QColor(180, 0, 0, 180)); eyeballGradient.setColorAt(0.8, QColor(80, 0, 0, 150)); eyeballGradient.setColorAt(1.0, QColor(30, 0, 0, 100)); painter->setBrush(eyeballGradient); painter->setPen(QPen(QColor(50, 0, 0, 180), 3)); painter->drawEllipse(QRectF(center.x() - radius, center.y() - bell_height, radius * 2, bell_height * 2)); painter->setPen(Qt::NoPen); for (int i = 0; i < 20; ++i) { double angle_start = QRandomGenerator::global()->generateDouble() * 2 * M_PI; double vein_start_radius = radius * (0.8 + QRandomGenerator::global()->generateDouble() * 0.1); QPointF veinStart = center + QPointF(qCos(angle_start) * vein_start_radius, qSin(angle_start) * vein_start_radius * (bell_height / radius)); double angle_end = angle_start + (QRandomGenerator::global()->generateDouble() * 0.5 - 0.25) * M_PI; double vein_end_radius = radius * (0.3 + QRandomGenerator::global()->generateDouble() * 0.3); QPointF veinEnd = center + QPointF(qCos(angle_end) * vein_end_radius, qSin(angle_end) * vein_end_radius * (bell_height / radius)); QColor veinColor(QRandomGenerator::global()->bounded(100, 201), 0, 0, QRandomGenerator::global()->bounded(100, 201)); int veinThickness = QRandomGenerator::global()->bounded(1, 3); painter->setPen(QPen(veinColor, veinThickness)); painter->drawLine(veinStart, veinEnd); } painter->save(); QPainterPath eyeballOutline; eyeballOutline.addEllipse(QRectF(center.x() - radius, center.y() - bell_height, radius * 2, bell_height * 2)); painter->setClipPath(eyeballOutline); double iris_radius = radius * 0.6; QPointF irisCenter = center + smoothedPupilTarget; QRadialGradient irisGradient(irisCenter, iris_radius); irisGradient.setColorAt(0.0, QColor(80, 150, 80, 200)); irisGradient.setColorAt(0.5, QColor(30, 80, 30, 220)); irisGradient.setColorAt(1.0, QColor(10, 40, 10, 255)); painter->setBrush(irisGradient); painter->setPen(QPen(QColor(0, 0, 0, 150), 2)); painter->drawEllipse(irisCenter, iris_radius, iris_radius); painter->setPen(QPen(QColor(0, 0, 0, 100), 1)); for (int i = 0; i < 8; ++i) { double angle = i * M_PI / 4.0 + t_current * 0.1; QPointF crackStart = irisCenter + QPointF(qCos(angle) * iris_radius * 0.5, qSin(angle) * iris_radius * 0.5); QPointF crackEnd = irisCenter + QPointF(qCos(angle) * iris_radius * 0.9, qSin(angle) * iris_radius * 0.9); painter->drawLine(crackStart, crackEnd); } double pupil_base_radius = radius * 0.3; double pupil_radius_adjusted = pupil_base_radius * m_currentPupilScaleFactor; QPointF pupilOffsetFinal = smoothedPupilTarget; pupilOffsetFinal.setX(pupilOffsetFinal.x() + qSin(t_current * 2.5) * 2); pupilOffsetFinal.setY(pupilOffsetFinal.y() + qCos(t_current * 2.8) * 2); QPointF pupilCenter = center + pupilOffsetFinal; double pupil_width = pupil_radius_adjusted * (1.0 + qSin(t_current * 1.5) * 0.05); double pupil_height = pupil_radius_adjusted * (1.0 + qCos(t_current * 1.7) * 0.05); painter->setBrush(QColor(0, 0, 0, 255)); painter->setPen(QPen(QColor(20, 0, 0, 200), 2)); painter->drawEllipse(QRectF(pupilCenter.x() - pupil_width / 2, pupilCenter.y() - pupil_height / 2, pupil_width, pupil_height)); painter->restore(); if (blinkProgress > 0.0) { double closure_amount; if (blinkProgress <= 0.5) { closure_amount = blinkProgress * 2.0; } else { closure_amount = (1.0 - blinkProgress) * 2.0; } closure_amount = qMax(0.0, qMin(1.0, closure_amount)); double eyelid_vertical_coverage = bell_height * closure_amount; painter->setBrush(QColor(20, 0, 0)); painter->setPen(Qt::NoPen); painter->drawRect(QRectF(center.x() - radius, center.y() - bell_height, radius * 2, eyelid_vertical_coverage)); painter->drawRect(QRectF(center.x() - radius, center.y() + bell_height - eyelid_vertical_coverage, radius * 2, eyelid_vertical_coverage)); } } void JellyfishWidget::drawGoreTendrils(QPainter *painter, const QPointF& center, double radius, int num_tendrils, double current_t) { for (int i = 0; i < num_tendrils; ++i) { double base_angle = i * 2 * M_PI / num_tendrils; QPointF startPoint(center.x() + radius * qCos(base_angle + qSin(current_t * 0.7 + i * 0.3) * 0.05), center.y() + radius * qSin(base_angle + qCos(current_t * 0.7 + i * 0.3) * 0.05)); int segments = 12; double tendril_length = 100.0 + 80.0 * qSin(current_t * 1.0 + i * 0.6); QPointF prevPoint = startPoint; for (int j = 1; j <= segments; ++j) { double segment_progress = (double)j / segments; double current_segment_length_factor = 0.5 + 0.5 * qCos(current_t * 2.0 + j * 0.9 + i * 0.4); double current_segment_length = tendril_length * segment_progress * current_segment_length_factor; double wave_amplitude = 25.0 * qSin(current_t * 2.5 + j * 1.0 + i * 0.3) * (1.0 - segment_progress * 0.5); double wave_angle_offset = qCos(base_angle + M_PI / 2.0 + qSin(current_t * 1.8)); QPointF dir(qCos(base_angle), qSin(base_angle)); QPointF normal_dir(-dir.y(), dir.x()); QPointF nextPoint(startPoint.x() + dir.x() * current_segment_length + normal_dir.x() * wave_amplitude, startPoint.y() + dir.y() * current_segment_length + normal_dir.y() * wave_amplitude); int alpha = 200 - j * 15; int pen_thickness = 2 + (segments - j) * 0.2; painter->setPen(QPen(QColor(180, 20, 20, qMax(0, alpha)), pen_thickness)); painter->drawLine(prevPoint, nextPoint); prevPoint = nextPoint; if (j % 3 == 0) { painter->setBrush(QColor(100, 0, 0, qMax(0, alpha - 50))); painter->setPen(Qt::NoPen); painter->drawEllipse(nextPoint, pen_thickness + 1, pen_thickness + 1); painter->setBrush(Qt::NoBrush); } } } } void JellyfishWidget::drawFallingGoreAndBlood(QPainter *painter) { for (const GoreParticle &p : fallingGoreParticles) { QColor particleColor = p.color; particleColor.setAlphaF(p.opacity); painter->setBrush(particleColor); painter->setPen(Qt::NoPen); painter->drawEllipse(p.position, p.size, p.size); } } void JellyfishWidget::drawAcidDrips(QPainter *painter) { for (const AcidDrip &d : acidDrips) { QColor dripColor = d.color; dripColor.setAlphaF(d.opacity); painter->setBrush(dripColor); painter->setPen(Qt::NoPen); painter->drawEllipse(d.position, d.size, d.size); if (d.trailOpacity > 0.01) { QColor trailColor = d.color; trailColor.setAlphaF(d.trailOpacity * 0.5); QPen trailPen(trailColor, d.size / 2.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); painter->setPen(trailPen); painter->drawLine(d.position - d.velocity * 3.0, d.position); painter->setPen(Qt::NoPen); } } } void JellyfishWidget::drawBackgroundTentacleShadows(QPainter *painter) { for (const TentacleShadow &shadow : backgroundTentacleShadows) { if (shadow.points.size() < 2) continue; QColor shadowColor = QColor(10, 0, 0, static_cast(shadow.opacity * 255)); QPen shadowPen(shadowColor, shadow.thickness, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); painter->setPen(shadowPen); painter->setBrush(Qt::NoBrush); QPainterPath path; path.moveTo(shadow.points.first()); for (int i = 1; i < shadow.points.size(); ++i) { path.lineTo(shadow.points[i]); } painter->drawPath(path); } } ================================================ FILE: Anime_Template_Project/Eye_of_Cthulhu/JellyfishWidget.h ================================================ #ifndef JELLYFISHWIDGET_H #define JELLYFISHWIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include #include struct GoreParticle { QPointF position; QPointF velocity; double size; double opacity; QColor color; bool active; }; struct AcidDrip { QPointF position; QPointF velocity; double size; double opacity; double trailOpacity; QColor color; bool active; }; struct TentacleShadow { QVector points; double opacity; double phaseOffset; double thickness; }; class ParticleUpdateRunnable : public QObject, public QRunnable { Q_OBJECT public: ParticleUpdateRunnable(double t_current, int widgetHeight, int widgetWidth, const QPointF& cthulhuCenter, double eyeball_radius_base, QVector currentGore, QVector currentAcid, QVector currentShadows) : m_t_current(t_current), m_widgetHeight(widgetHeight), m_widgetWidth(widgetWidth), m_cthulhuCenter(cthulhuCenter), m_eyeball_radius_base(eyeball_radius_base), m_goreParticles(currentGore), m_acidDrips(currentAcid), m_tentacleShadows(currentShadows) { setAutoDelete(true); } void run() override { for (int i = 0; i < m_goreParticles.size(); ) { GoreParticle &p = m_goreParticles[i]; p.position += p.velocity; p.velocity.setY(p.velocity.y() + 0.2); p.opacity -= 0.01; p.size *= 0.99; if (p.position.y() > m_widgetHeight + 50 || p.opacity <= 0.01) { m_goreParticles.removeAt(i); } else { ++i; } } if (QRandomGenerator::global()->generateDouble() < 0.2) { GoreParticle newParticle; newParticle.position = QPointF(m_cthulhuCenter.x() + QRandomGenerator::global()->bounded(-50, 51), m_cthulhuCenter.y() + m_eyeball_radius_base + QRandomGenerator::global()->bounded(10, 31)); newParticle.velocity = QPointF(QRandomGenerator::global()->generateDouble() * 2.0 - 1.0, QRandomGenerator::global()->generateDouble() * 3.0 + 1.0); newParticle.size = QRandomGenerator::global()->bounded(3, 11); newParticle.opacity = QRandomGenerator::global()->generateDouble() * 0.2 + 0.8; if (QRandomGenerator::global()->generateDouble() < 0.7) { newParticle.color = QColor(150, 0, 0); } else { newParticle.color = QColor(80, 20, 20); } m_goreParticles.append(newParticle); } for (int i = 0; i < m_acidDrips.size(); ) { AcidDrip &d = m_acidDrips[i]; d.position += d.velocity; d.velocity.setY(d.velocity.y() + 0.1); d.opacity -= 0.008; d.trailOpacity -= 0.012; d.size *= 0.995; if (d.position.y() > m_widgetHeight + 50 || d.opacity <= 0.01) { m_acidDrips.removeAt(i); } else { ++i; } } if (QRandomGenerator::global()->generateDouble() < 0.05) { AcidDrip newDrip; newDrip.position = QPointF(m_cthulhuCenter.x() + QRandomGenerator::global()->bounded(-60, 61), m_cthulhuCenter.y() + m_eyeball_radius_base * 0.8 + QRandomGenerator::global()->bounded(0, 20)); newDrip.velocity = QPointF(QRandomGenerator::global()->generateDouble() * 1.0 - 0.5, QRandomGenerator::global()->generateDouble() * 1.5 + 0.5); newDrip.size = QRandomGenerator::global()->bounded(4, 8); newDrip.opacity = QRandomGenerator::global()->generateDouble() * 0.3 + 0.7; newDrip.trailOpacity = 0.5; newDrip.color = QColor(50, 150, 50, 255); m_acidDrips.append(newDrip); } for (TentacleShadow &shadow : m_tentacleShadows) { for (int i = 0; i < shadow.points.size(); ++i) { shadow.points[i].setX(shadow.points[i].x() + qSin(m_t_current * 0.1 + shadow.phaseOffset + i * 0.5) * 0.5); shadow.points[i].setY(shadow.points[i].y() + qCos(m_t_current * 0.08 + shadow.phaseOffset + i * 0.7) * 0.5); if (shadow.points[i].x() < -200) shadow.points[i].setX(m_widgetWidth + 200); else if (shadow.points[i].x() > m_widgetWidth + 200) shadow.points[i].setX(-200); if (shadow.points[i].y() < -200) shadow.points[i].setY(m_widgetHeight + 200); else if (shadow.points[i].y() > m_widgetHeight + 200) shadow.points[i].setY(-200); } } emit particlesUpdated(m_goreParticles, m_acidDrips, m_tentacleShadows); } signals: void particlesUpdated(QVector newGore, QVector newAcid, QVector newShadows); private: double m_t_current; int m_widgetHeight; int m_widgetWidth; QPointF m_cthulhuCenter; double m_eyeball_radius_base; QVector m_goreParticles; QVector m_acidDrips; QVector m_tentacleShadows; }; class JellyfishWidget : public QWidget { Q_OBJECT public: explicit JellyfishWidget(QWidget *parent = nullptr); ~JellyfishWidget(); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void leaveEvent(QEvent *event) override; private slots: void updateAnimation(); void handleParticlesUpdated(QVector newGore, QVector newAcid, QVector newShadows); private: double x_input; double y_input; double t_current; QTimer *animationTimer; QPointF mousePosition; QPointF smoothedPupilTarget; bool mouseIsInWindow; double blinkProgress; bool isBlinking; QVector fallingGoreParticles; QVector acidDrips; QVector backgroundTentacleShadows; double pulseScale; double m_targetPupilScaleFactor; double m_currentPupilScaleFactor; QPen eyeballBorderPen; QBrush eyeballGradientBrush; QPen irisBorderPen; QBrush irisGradientBrush; QPen pupilPen; QBrush pupilBrush; QBrush eyelidBrush; QPen goreTendrilPen; QBrush goreTendrilBrush; QPen fallingGorePen; QBrush fallingGoreBrush; QPen acidDripPen; QBrush acidDripBrush; QPen acidDripTrailPen; QPen backgroundShadowPen; QPointF calculateJellyfishPoint(double local_x, double local_y, double current_t); void drawEyeball(QPainter *painter, const QPointF& center, double radius, double eyeball_height); void drawGoreTendrils(QPainter *painter, const QPointF& center, double radius, int num_tentacles, double current_t); void drawFallingGoreAndBlood(QPainter *painter); void drawAcidDrips(QPainter *painter); void drawBackgroundTentacleShadows(QPainter *painter); }; #endif ================================================ FILE: Anime_Template_Project/Flowing_Gradient_Font/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { this->resize(1440, 880); animation = new QPropertyAnimation(this, "Gradient_Position"); animation->setDuration(5000); animation->setStartValue(0); animation->setEndValue(360); animation->start(); connect(animation, &QPropertyAnimation::finished, this, [this] { animation->start(); }); animation2 = new QPropertyAnimation(this, "Circle_Ratio"); animation2->setDuration(1000); animation2->setStartValue(m_Circle_Ratio); animation2->setEndValue(1.0); button = new QPushButton(this); button->setText("更改背景颜色"); button->setGeometry(20, 20, 100, 30); lineEdit = new QLineEdit(this); lineEdit->setGeometry(130, 20, 100, 30); connect(lineEdit, &QLineEdit::textChanged, this, [this](const QString &text) { color_str = text; update(); }); connect(button, &QPushButton::clicked, this, [this] { if (m_isForward) { this->forward(); m_isForward = false; } else { this->backward(); m_isForward = true; } }); } Widget::~Widget() {} void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); int w = this->width(); int h = this->height(); int sum = w + h; painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(QColor(255, 255, 255, 255))); painter.drawRect(0, 0, w, h); painter.setBrush(QBrush(QColor(0, 0, 0, 255))); painter.drawEllipse(button->rect().bottomRight(), int(sum * m_Circle_Ratio), int(sum * m_Circle_Ratio)); QConicalGradient gradient(w / 2, h, m_Gradient_Position); gradient.setColorAt(0, QColor(255, 167, 69, 255)); gradient.setColorAt(0.125, QColor(254, 134, 159, 255)); gradient.setColorAt(0.25, QColor(239, 122, 200, 255)); gradient.setColorAt(0.375, QColor(160, 131, 237, 255)); gradient.setColorAt(0.5, QColor(67, 174, 255, 255)); gradient.setColorAt(0.625, QColor(160, 131, 237, 255)); gradient.setColorAt(0.75, QColor(239, 122, 200, 255)); gradient.setColorAt(0.875, QColor(254, 134, 159, 255)); gradient.setColorAt(1, QColor(255, 167, 69, 255)); painter.setBrush(gradient); QFont font; font.setPixelSize(h / 5); font.setFamily("微软雅黑"); font.setBold(true); painter.setFont(font); painter.setPen(QPen(QBrush(gradient), 2)); painter.drawText(QRect(0, 0, w, h), Qt::AlignCenter, color_str); } void Widget::resizeEvent(QResizeEvent *event) {} void Widget::forward() { animation2->setDirection(QAbstractAnimation::Forward); animation2->start(); } void Widget::backward() { animation2->setDirection(QAbstractAnimation::Backward); animation2->start(); } int Widget::Gradient_Position() const { return m_Gradient_Position; } void Widget::setGradient_Position(const int &value) { if (m_Gradient_Position == value) return; m_Gradient_Position = value; update(); } qreal Widget::Circle_Ratio() const { return m_Circle_Ratio; } void Widget::setCircle_Ratio(const qreal &value) { if (m_Circle_Ratio == value) return; m_Circle_Ratio = value; update(); } ================================================ FILE: Anime_Template_Project/Flowing_Gradient_Font/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include #include #include #include class Widget : public QWidget { Q_OBJECT Q_PROPERTY(int Gradient_Position READ Gradient_Position WRITE setGradient_Position) Q_PROPERTY(qreal Circle_Ratio READ Circle_Ratio WRITE setCircle_Ratio) public: Widget(QWidget *parent = nullptr); ~Widget(); QPropertyAnimation *animation; QPropertyAnimation* animation2; QPushButton *button; QLineEdit *lineEdit; void forward(); void backward(); int Gradient_Position() const; void setGradient_Position(const int&value); qreal Circle_Ratio() const; void setCircle_Ratio(const qreal&value); protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); private: int m_Gradient_Position; QColor Global_Color = QColor(255, 255, 255 , 255); QString color_str; qreal m_Circle_Ratio = 0.0; bool m_isForward = true; }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/FractalWidget/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(FractalWidget VERSION 0.1 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) set(PROJECT_SOURCES main.cpp fractalwidget.cpp fractalwidget.h ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(FractalWidget MANUAL_FINALIZATION ${PROJECT_SOURCES} mainwindow.h mainwindow.cpp fractalworker.h fractalworker.cpp ) # Define target properties for Android with Qt 6 as: # set_property(TARGET FractalWidget APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR # ${CMAKE_CURRENT_SOURCE_DIR}/android) # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation else() if(ANDROID) add_library(FractalWidget SHARED ${PROJECT_SOURCES} ) # Define properties for Android with Qt 5 after find_package() calls as: # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") else() add_executable(FractalWidget ${PROJECT_SOURCES} ) endif() endif() target_link_libraries(FractalWidget PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an # explicit, fixed bundle identifier manually though. if(${QT_VERSION} VERSION_LESS 6.1.0) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.FractalWidget) endif() set_target_properties(FractalWidget PROPERTIES ${BUNDLE_ID_OPTION} MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) include(GNUInstallDirs) install(TARGETS FractalWidget BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(FractalWidget) endif() ================================================ FILE: Anime_Template_Project/FractalWidget/fractalwidget.cpp ================================================ #include "fractalwidget.h" #include #include #include #include #include #include #include #include FractalWidget::FractalWidget(QWidget *parent) : QWidget(parent), m_currentFractalType(Mandelbrot), m_juliaCr(-0.7), m_juliaCi(0.27015), m_animationTimer(new QTimer(this)), m_animationStep(0), m_maxAnimationSteps(500), m_centerX(0.0), m_centerY(0.0), m_scale(2.0), m_defaultCenterX(0.0), m_defaultCenterY(0.0), m_defaultScale(2.0), m_lineFractalOrder(1), m_defaultLineFractalOrder(1), m_workerThread(new QThread(this)), m_fractalWorker(new FractalWorker()), m_isCalculating(false), m_isDragging(false), m_accumulatedDragDelta(0, 0), m_imageTopLeft(0, 0) { resize(600, 600); connect(m_animationTimer, &QTimer::timeout, this, &FractalWidget::animateFractal); m_fractalWorker->moveToThread(m_workerThread); connect(m_fractalWorker, &FractalWorker::imageReady, this, &FractalWidget::onImageReady); connect(m_workerThread, &QThread::finished, m_fractalWorker, &QObject::deleteLater); connect(m_workerThread, &QThread::finished, m_workerThread, &QObject::deleteLater); connect(m_fractalWorker, &FractalWorker::finished, this, &FractalWidget::onWorkerFinished); m_workerThread->start(); } FractalWidget::~FractalWidget() { if (m_workerThread->isRunning()) { m_workerThread->quit(); m_workerThread->wait(); } } void FractalWidget::setFractalType(FractalType type) { bool typeChanged = (m_currentFractalType != type); m_currentFractalType = type; m_imageTopLeft = QPointF(0, 0); if (m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider) { if (typeChanged || (std::abs(m_centerX - m_defaultCenterX) > 1e-9 || std::abs(m_centerY - m_defaultCenterY) > 1e-9 || std::abs(m_scale - m_defaultScale) > 1e-9)) { m_centerX = m_defaultCenterX; m_centerY = m_defaultCenterY; m_scale = m_defaultScale; qDebug() << "迭代型分形视图已重置到默认。"; } } else { if (typeChanged || m_lineFractalOrder != m_defaultLineFractalOrder) { m_lineFractalOrder = m_defaultLineFractalOrder; qDebug() << "线条分形阶数已重置到默认。"; } } startAnimation(); } void FractalWidget::setJuliaC(double cr, double ci) { m_juliaCr = cr; m_juliaCi = ci; if (m_currentFractalType == Julia || m_currentFractalType == BurningShipJulia) { startAnimation(); } } void FractalWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.fillRect(rect(), Qt::black); if (!m_image.isNull() && (m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider)) { if (m_isDragging) { painter.drawImage(m_imageTopLeft, m_image); } else { painter.drawImage(0, 0, m_image); } } else if (m_currentFractalType == KochSnowflake || m_currentFractalType == SierpinskiCurve) { painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(Qt::white); qreal minSide = std::min(width(), height()) * 0.8; if (m_currentFractalType == KochSnowflake) { QPointF p1(width() / 2.0 - minSide / 2.0, height() / 2.0 + minSide * std::sqrt(3.0) / 6.0); QPointF p2(width() / 2.0 + minSide / 2.0, height() / 2.0 + minSide * std::sqrt(3.0) / 6.0); QPointF p3(width() / 2.0, height() / 2.0 - minSide * std::sqrt(3.0) / 3.0); generateKochSnowflake(painter, m_animationStep, p1, p2); generateKochSnowflake(painter, m_animationStep, p2, p3); generateKochSnowflake(painter, m_animationStep, p3, p1); } else if (m_currentFractalType == SierpinskiCurve) { qreal side = minSide * 0.8; QPointF p1(width() / 2.0 - side / 2.0, height() / 2.0 + side * std::sqrt(3.0) / 6.0); QPointF p2(width() / 2.0 + side / 2.0, height() / 2.0 + side * std::sqrt(3.0) / 6.0); QPointF p3(width() / 2.0, height() / 2.0 - side * std::sqrt(3.0) / 3.0); generateSierpinskiCurve(painter, m_animationStep, p1, p2, p3); } } if (m_isCalculating && (m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider)) { painter.setPen(Qt::white); painter.setFont(QFont("Arial", 24)); painter.drawText(rect(), Qt::AlignCenter, "计算中..."); } } void FractalWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); setFractalType(m_currentFractalType); } void FractalWidget::wheelEvent(QWheelEvent *event) { if (!(m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider)) { event->ignore(); return; } QPoint numPixels = event->pixelDelta(); QPoint numDegrees = event->angleDelta() / 4; qreal zoomFactor = 1.0; if (!numPixels.isNull()) { zoomFactor = std::pow(1.0015, numPixels.y()); } else if (!numDegrees.isNull()) { zoomFactor = std::pow(1.1, numDegrees.y() / 15.0); } if (zoomFactor == 1.0) { event->ignore(); return; } QPoint mousePos = event->position().toPoint(); double mouseX = mousePos.x(); double mouseY = mousePos.y(); double oldReal = (mouseX - width() / 2.0) * m_scale / width() + m_centerX; double oldImag = (mouseY - height() / 2.0) * m_scale / height() + m_centerY; m_scale /= zoomFactor; m_centerX = oldReal - (mouseX - width() / 2.0) * m_scale / width(); m_centerY = oldImag - (mouseY - height() / 2.0) * m_scale / height(); m_scale = std::max(1e-10, std::min(1000.0, m_scale)); startAnimation(); event->accept(); } void FractalWidget::mousePressEvent(QMouseEvent *event) { if (!(m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider)) { event->ignore(); return; } if (event->button() == Qt::LeftButton) { m_isDragging = true; m_lastMousePos = event->position().toPoint(); m_accumulatedDragDelta = QPoint(0, 0); m_imageTopLeft = QPointF(0, 0); updateLoadingState(true); event->accept(); } else { event->ignore(); } } void FractalWidget::mouseMoveEvent(QMouseEvent *event) { if (m_isDragging && (m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider)) { QPoint currentMousePos = event->position().toPoint(); QPoint delta = currentMousePos - m_lastMousePos; m_imageTopLeft += delta; m_centerX -= delta.x() * m_scale / width(); m_centerY -= delta.y() * m_scale / height(); m_lastMousePos = currentMousePos; m_accumulatedDragDelta += delta; if (std::abs(m_accumulatedDragDelta.x()) >= 5 || std::abs(m_accumulatedDragDelta.y()) >= 5) { update(); m_accumulatedDragDelta = QPoint(0, 0); } event->accept(); } else { event->ignore(); } } void FractalWidget::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { if (m_isDragging) { m_isDragging = false; startAnimation(); } event->accept(); } else { event->ignore(); } } void FractalWidget::startAnimation() { stopAnimation(); m_animationStep = 0; if (m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider) { m_maxAnimationSteps = 500; m_animationTimer->start(25); } else { int orderVariation = QRandomGenerator::global()->bounded(-2, 3); m_maxAnimationSteps = m_lineFractalOrder + orderVariation; m_maxAnimationSteps = std::max(4, m_maxAnimationSteps); m_animationTimer->start(1); } } void FractalWidget::stopAnimation() { if (m_animationTimer->isActive()) { m_animationTimer->stop(); } if (m_isCalculating) { qDebug() << "动画停止,但分形计算仍在进行中。当前帧将完成。"; } updateLoadingState(false); } void FractalWidget::animateFractal() { if (m_animationStep > m_maxAnimationSteps) { stopAnimation(); return; } QElapsedTimer timer; timer.start(); if (m_currentFractalType == Mandelbrot || m_currentFractalType == Julia || m_currentFractalType == BurningShip || m_currentFractalType == Newton || m_currentFractalType == Tricorn || m_currentFractalType == BurningShipJulia || m_currentFractalType == Spider) { if (!m_isCalculating) { updateLoadingState(true); startFractalCalculationThread(m_animationStep); } } else { update(); qDebug() << "线条分形 动画步进:" << m_animationStep << "耗时:" << timer.elapsed() << "ms"; } if (m_currentFractalType == KochSnowflake || m_currentFractalType == SierpinskiCurve) { m_animationStep += 1; } else { m_animationStep += 10; } m_animationStep = std::min(m_animationStep, m_maxAnimationSteps + 1); } void FractalWidget::onImageReady(const QImage &image) { m_image = image; update(); qDebug() << "收到工作线程图像。更新UI。"; updateLoadingState(false); } void FractalWidget::onWorkerFinished() { } void FractalWidget::startFractalCalculationThread(int currentMaxIterations) { QMetaObject::invokeMethod(m_fractalWorker, "doWork", Qt::QueuedConnection, Q_ARG(FractalWorker::FractalType, (FractalWorker::FractalType)m_currentFractalType), Q_ARG(int, currentMaxIterations), Q_ARG(double, m_juliaCr), Q_ARG(double, m_juliaCi), Q_ARG(double, m_centerX), Q_ARG(double, m_centerY), Q_ARG(double, m_scale), Q_ARG(int, width()), Q_ARG(int, height()), Q_ARG(int, m_maxAnimationSteps)); m_isCalculating = true; } void FractalWidget::updateLoadingState(bool loading) { m_isCalculating = loading; update(); } void FractalWidget::generateKochSnowflake(QPainter &painter, int order, QPointF p1, QPointF p2) { if (order == 0) { painter.drawLine(p1, p2); return; } QPointF p_a = p1; QPointF p_b(p1.x() + (p2.x() - p1.x()) / 3.0, p1.y() + (p2.y() - p1.y()) / 3.0); QPointF p_c(p1.x() + 2.0 * (p2.x() - p1.x()) / 3.0, p1.y() + 2.0 * (p2.y() - p1.y()) / 3.0); QPointF p_d = p2; qreal dx = p2.x() - p1.x(); qreal dy = p2.y() - p1.y(); QPointF p_e(p_b.x() + dx * std::cos(M_PI / 3.0) - dy * std::sin(M_PI / 3.0), p_b.y() + dx * std::sin(M_PI / 3.0) + dy * std::cos(M_PI / 3.0)); generateKochSnowflake(painter, order - 1, p_a, p_b); generateKochSnowflake(painter, order - 1, p_b, p_e); generateKochSnowflake(painter, order - 1, p_e, p_c); generateKochSnowflake(painter, order - 1, p_c, p_d); } void FractalWidget::generateSierpinskiCurve(QPainter &painter, int order, QPointF p1, QPointF p2, QPointF p3) { if (order == 0) { painter.drawLine(p1, p2); painter.drawLine(p2, p3); painter.drawLine(p3, p1); return; } QPointF p12((p1.x() + p2.x()) / 2.0, (p1.y() + p2.y()) / 2.0); QPointF p23((p2.x() + p3.x()) / 2.0, (p2.y() + p3.y()) / 2.0); QPointF p31((p3.x() + p1.x()) / 2.0, (p1.y() + p3.y()) / 2.0); generateSierpinskiCurve(painter, order - 1, p1, p12, p31); generateSierpinskiCurve(painter, order - 1, p12, p2, p23); generateSierpinskiCurve(painter, order - 1, p31, p23, p3); } ================================================ FILE: Anime_Template_Project/FractalWidget/fractalwidget.h ================================================ #ifndef FRACTALWIDGET_H #define FRACTALWIDGET_H #include #include #include #include #include #include #include "fractalworker.h" class FractalWidget : public QWidget { Q_OBJECT public: explicit FractalWidget(QWidget *parent = nullptr); ~FractalWidget(); enum FractalType { Mandelbrot, Julia, BurningShip, Newton, Tricorn, BurningShipJulia, Spider, SierpinskiCurve, KochSnowflake }; Q_ENUM(FractalType) void setFractalType(FractalType type); void setJuliaC(double cr, double ci); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; void wheelEvent(QWheelEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private slots: void animateFractal(); void onImageReady(const QImage &image); void onWorkerFinished(); private: FractalType m_currentFractalType; double m_juliaCr; double m_juliaCi; QTimer *m_animationTimer; int m_animationStep; int m_maxAnimationSteps; double m_centerX; double m_centerY; double m_scale; double m_defaultCenterX; double m_defaultCenterY; double m_defaultScale; int m_lineFractalOrder; int m_defaultLineFractalOrder; QThread *m_workerThread; FractalWorker *m_fractalWorker; QImage m_image; bool m_isCalculating; bool m_isDragging; QPoint m_lastMousePos; QPoint m_accumulatedDragDelta; QPointF m_imageTopLeft; void startAnimation(); void stopAnimation(); void startFractalCalculationThread(int currentMaxIterations); void updateLoadingState(bool loading); void generateKochSnowflake(QPainter &painter, int order, QPointF p1, QPointF p2); void generateSierpinskiCurve(QPainter &painter, int order, QPointF p1, QPointF p2, QPointF p3); }; #endif // FRACTALWIDGET_H ================================================ FILE: Anime_Template_Project/FractalWidget/fractalworker.cpp ================================================ #include "fractalworker.h" #include #include #include #include #include #include FractalWorker::FractalWorker(QObject *parent) : QObject(parent) { } void FractalWorker::doWork(FractalType type, int currentMaxIterations, double juliaCr, double juliaCi, double centerX, double centerY, double scale, int imageWidth, int imageHeight, int maxAnimationSteps) { m_juliaCr = juliaCr; m_juliaCi = juliaCi; m_centerX = centerX; m_centerY = centerY; m_scale = scale; m_maxAnimationSteps = maxAnimationSteps; if (!(type == Mandelbrot || type == Julia || type == BurningShip || type == Newton || type == Tricorn || type == BurningShipJulia || type == Spider)) { emit finished(); return; } QElapsedTimer timer; timer.start(); int numThreads = QThreadPool::globalInstance()->maxThreadCount(); if (numThreads == 0) { numThreads = QThread::idealThreadCount(); } if (numThreads <= 0) numThreads = 1; QVector chunks; int rowsPerChunk = imageHeight / numThreads; int remainderRows = imageHeight % numThreads; for (int i = 0; i < numThreads; ++i) { FractalChunkData data; data.type = type; data.currentMaxIterations = currentMaxIterations; data.juliaCr = juliaCr; data.juliaCi = juliaCi; data.centerX = centerX; data.centerY = centerY; data.scale = scale; data.imageWidth = imageWidth; data.imageHeight = imageHeight; data.maxAnimationSteps = maxAnimationSteps; data.startY = i * rowsPerChunk; data.endY = data.startY + rowsPerChunk; if (i == numThreads - 1) { data.endY += remainderRows; } chunks.append(data); } QVector> futures; futures.reserve(chunks.size()); for (const FractalChunkData& data : chunks) { futures.append(QtConcurrent::run(FractalWorker::computeFractalChunk, data)); } for (QFuture& future : futures) { future.waitForFinished(); } QImage finalImage(imageWidth, imageHeight, QImage::Format_ARGB32); for (int i = 0; i < futures.size(); ++i) { const QImage &chunkImage = futures.at(i).result(); const FractalChunkData &chunkData = chunks.at(i); for (int y = 0; y < chunkImage.height(); ++y) { for (int x = 0; x < chunkImage.width(); ++x) { finalImage.setPixel(x, chunkData.startY + y, chunkImage.pixel(x, y)); } } } qDebug() << "分形计算完成,耗时" << timer.elapsed() << "ms,使用" << numThreads << "个线程。"; emit imageReady(finalImage); emit finished(); } QImage FractalWorker::computeFractalChunk(const FractalChunkData &data) { QImage chunkImage(data.imageWidth, data.endY - data.startY, QImage::Format_ARGB32); for (int y = 0; y < (data.endY - data.startY); ++y) { for (int x = 0; x < data.imageWidth; ++x) { double globalY = data.startY + y; double real = (x - data.imageWidth / 2.0) * data.scale / data.imageWidth + data.centerX; double imag = (globalY - data.imageHeight / 2.0) * data.scale / data.imageHeight + data.centerY; int iterations = 0; QRgb pixelColor = qRgb(0, 0, 0); if (data.type == Mandelbrot) { double zr = 0.0; double zi = 0.0; while (zr * zr + zi * zi < 4.0 && iterations < data.currentMaxIterations) { double tempZr = zr * zr - zi * zi + real; zi = 2.0 * zr * zi + imag; zr = tempZr; iterations++; } pixelColor = getColor(iterations, data.maxAnimationSteps); } else if (data.type == Julia) { double zr = real; double zi = imag; while (zr * zr + zi * zi < 4.0 && iterations < data.currentMaxIterations) { double tempZr = zr * zr - zi * zi + data.juliaCr; zi = 2.0 * zr * zi + data.juliaCi; zr = tempZr; iterations++; } pixelColor = getColor(iterations, data.maxAnimationSteps); } else if (data.type == BurningShip) { double zr = 0.0; double zi = 0.0; while (zr * zr + zi * zi < 4.0 && iterations < data.currentMaxIterations) { double absZr = std::abs(zr); double absZi = std::abs(zi); double tempZr = absZr * absZr - absZi * absZi + real; zi = 2.0 * absZr * absZi + imag; zr = tempZr; iterations++; } pixelColor = getColor(iterations, data.maxAnimationSteps); } else if (data.type == Newton) { std::complex root1(1.0, 0.0); std::complex root2(-0.5, std::sqrt(3.0) / 2.0); std::complex root3(-0.5, -std::sqrt(3.0) / 2.0); const double tolerance = 1e-6; std::complex z(real, imag); std::complex finalZ = z; while (iterations < data.currentMaxIterations) { std::complex f_z = z * z * z - 1.0; std::complex f_prime_z = 3.0 * z * z; if (std::abs(f_prime_z) < 1e-10) { iterations = data.currentMaxIterations; break; } std::complex z_next = z - f_z / f_prime_z; if (std::abs(z_next - z) < tolerance) { finalZ = z_next; break; } z = z_next; iterations++; } pixelColor = getNewtonColor(iterations, data.maxAnimationSteps, finalZ.real(), finalZ.imag()); } else if (data.type == Tricorn) { double zr = 0.0; double zi = 0.0; while (zr * zr + zi * zi < 4.0 && iterations < data.currentMaxIterations) { double tempZr = zr * zr - zi * zi + real; zi = -2.0 * zr * zi + imag; zr = tempZr; iterations++; } pixelColor = getColor(iterations, data.maxAnimationSteps); } else if (data.type == BurningShipJulia) { double zr = real; double zi = imag; while (zr * zr + zi * zi < 4.0 && iterations < data.currentMaxIterations) { double absZr = std::abs(zr); double absZi = std::abs(zi); double tempZr = absZr * absZi - absZi * absZi + data.juliaCr; zi = 2.0 * absZr * absZi + data.juliaCi; zr = tempZr; iterations++; } pixelColor = getColor(iterations, data.maxAnimationSteps); } else if (data.type == Spider) { double zr = 0.0; double zi = 0.0; double cr = real; double ci = imag; while (zr * zr + zi * zi < 4.0 && iterations < data.currentMaxIterations) { double tempZr = zr * zr - zi * zi + cr; double tempZi = 2.0 * zr * zi + ci; cr = zr; ci = zi; zr = tempZr; zi = tempZi; iterations++; } pixelColor = getColor(iterations, data.maxAnimationSteps); } chunkImage.setPixel(x, y, pixelColor); } } return chunkImage; } QRgb FractalWorker::getColor(int iterations, int maxIterationsUsed) { if (iterations == maxIterationsUsed) { return qRgb(0, 0, 0); } double t = (double)iterations / maxIterationsUsed; int r = (int)(9 * (1 - t) * t * t * t * 255); int g = (int)(15 * (1 - t) * (1 - t) * t * t * 255); int b = (int)(8.5 * (1 - t) * (1 - t) * (1 - t) * t * 255); return qRgb(r, g, b); } QRgb FractalWorker::getNewtonColor(int iterations, int maxIterationsUsed, double finalZr, double finalZi) { if (iterations == maxIterationsUsed) { return qRgb(0, 0, 0); } QRgb baseColor; std::complex finalZ(finalZr, finalZi); std::complex root1(1.0, 0.0); std::complex root2(-0.5, std::sqrt(3.0) / 2.0); std::complex root3(-0.5, -std::sqrt(3.0) / 2.0); if (std::abs(finalZ - root1) < 1e-5) { baseColor = qRgb(255, 0, 0); } else if (std::abs(finalZ - root2) < 1e-5) { baseColor = qRgb(0, 255, 0); } else if (std::abs(finalZ - root3) < 1e-5) { baseColor = qRgb(0, 0, 255); } else { baseColor = qRgb(128, 128, 128); } double brightness = 1.0 - (double)iterations / maxIterationsUsed; int r = qRed(baseColor) * brightness; int g = qGreen(baseColor) * brightness; int b = qBlue(baseColor) * brightness; return qRgb(r, g, b); } ================================================ FILE: Anime_Template_Project/FractalWidget/fractalworker.h ================================================ #ifndef FRACTALWORKER_H #define FRACTALWORKER_H #include #include #include #include class FractalWorker : public QObject { Q_OBJECT public: explicit FractalWorker(QObject *parent = nullptr); enum FractalType { Mandelbrot, Julia, BurningShip, Newton, Tricorn, BurningShipJulia, Spider, SierpinskiCurve, KochSnowflake }; signals: void imageReady(const QImage &image); void finished(); public slots: void doWork(FractalType type, int currentMaxIterations, double juliaCr, double juliaCi, double centerX, double centerY, double scale, int imageWidth, int imageHeight, int maxAnimationSteps); private: struct FractalChunkData { FractalType type; int currentMaxIterations; double juliaCr; double juliaCi; double centerX; double centerY; double scale; int imageWidth; int imageHeight; int maxAnimationSteps; int startY; int endY; }; static QImage computeFractalChunk(const FractalChunkData &data); static QRgb getColor(int iterations, int maxIterationsUsed); static QRgb getNewtonColor(int iterations, int maxIterationsUsed, double finalZr, double finalZi); double m_juliaCr; double m_juliaCi; double m_centerX; double m_centerY; double m_scale; int m_maxAnimationSteps; }; #endif // FRACTALWORKER_H ================================================ FILE: Anime_Template_Project/FractalWidget/main.cpp ================================================ #include "mainwindow.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/FractalWidget/mainwindow.cpp ================================================ #include "mainwindow.h" #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupUi(); setWindowTitle("Qt 分形生成器 - She Chen Zhi Ma"); setMinimumSize(800, 700); } MainWindow::~MainWindow() { } void MainWindow::setupUi() { QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QHBoxLayout *mainHorizontalLayout = new QHBoxLayout(centralWidget); QGroupBox *controlGroup = new QGroupBox("控制", this); controlGroup->setMaximumWidth(250); QVBoxLayout *controlLayout = new QVBoxLayout(controlGroup); m_fractalTypeComboBox = new QComboBox(this); m_fractalTypeComboBox->addItem("曼德布罗集", FractalWidget::Mandelbrot); m_fractalTypeComboBox->addItem("朱利亚集", FractalWidget::Julia); m_fractalTypeComboBox->addItem("燃烧船分形", FractalWidget::BurningShip); m_fractalTypeComboBox->addItem("牛顿分形", FractalWidget::Newton); m_fractalTypeComboBox->addItem("Tricorn", FractalWidget::Tricorn); m_fractalTypeComboBox->addItem("燃烧船朱利亚集", FractalWidget::BurningShipJulia); m_fractalTypeComboBox->addItem("蜘蛛分形", FractalWidget::Spider); m_fractalTypeComboBox->addItem("谢尔宾斯基曲线", FractalWidget::SierpinskiCurve); m_fractalTypeComboBox->addItem("科赫雪花", FractalWidget::KochSnowflake); connect(m_fractalTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &MainWindow::onFractalTypeChanged); controlLayout->addWidget(new QLabel("选择分形类型:")); controlLayout->addWidget(m_fractalTypeComboBox); m_juliaCrLineEdit = new QLineEdit(this); m_juliaCrLineEdit->setValidator(new QDoubleValidator(-1000.0, 1000.0, 5, this)); m_juliaCrLineEdit->setText("-0.7"); m_juliaCiLineEdit = new QLineEdit(this); m_juliaCiLineEdit->setValidator(new QDoubleValidator(-1000.0, 1000.0, 5, this)); m_juliaCiLineEdit->setText("0.27015"); m_applyJuliaButton = new QPushButton("应用朱利亚参数", this); connect(m_applyJuliaButton, &QPushButton::clicked, this, &MainWindow::onApplyJuliaParams); QHBoxLayout *juliaCrLayout = new QHBoxLayout(); juliaCrLayout->addWidget(new QLabel("Julia Cr:")); juliaCrLayout->addWidget(m_juliaCrLineEdit); QHBoxLayout *juliaCiLayout = new QHBoxLayout(); juliaCiLayout->addWidget(new QLabel("Julia Ci:")); juliaCiLayout->addWidget(m_juliaCiLineEdit); controlLayout->addLayout(juliaCrLayout); controlLayout->addLayout(juliaCiLayout); controlLayout->addWidget(m_applyJuliaButton); controlLayout->addStretch(); mainHorizontalLayout->addWidget(controlGroup); m_fractalWidget = new FractalWidget(this); mainHorizontalLayout->addWidget(m_fractalWidget); m_fractalTypeComboBox->setCurrentIndex(0); } void MainWindow::onFractalTypeChanged(int index) { Q_UNUSED(index); FractalWidget::FractalType type = static_cast( m_fractalTypeComboBox->currentData().toInt()); m_fractalWidget->setFractalType(type); bool isJuliaRelated = (type == FractalWidget::Julia || type == FractalWidget::BurningShipJulia); m_juliaCrLineEdit->setVisible(isJuliaRelated); m_juliaCiLineEdit->setVisible(isJuliaRelated); m_applyJuliaButton->setVisible(isJuliaRelated); if (type == FractalWidget::BurningShipJulia) { m_juliaCrLineEdit->setText("-0.1"); m_juliaCiLineEdit->setText("0.29015"); m_fractalWidget->setJuliaC(-0.1, 0.29015); } else if (type == FractalWidget::Julia) { m_juliaCrLineEdit->setText("-0.7"); m_juliaCiLineEdit->setText("0.27015"); m_fractalWidget->setJuliaC(-0.7, 0.27015); } } void MainWindow::onApplyJuliaParams() { double cr = m_juliaCrLineEdit->text().toDouble(); double ci = m_juliaCiLineEdit->text().toDouble(); m_fractalWidget->setJuliaC(cr, ci); } ================================================ FILE: Anime_Template_Project/FractalWidget/mainwindow.h ================================================ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "fractalwidget.h" #include #include #include #include #include #include #include #include #include class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onFractalTypeChanged(int index); void onApplyJuliaParams(); private: FractalWidget *m_fractalWidget; QComboBox *m_fractalTypeComboBox; QLineEdit *m_juliaCrLineEdit; QLineEdit *m_juliaCiLineEdit; QPushButton *m_applyJuliaButton; void setupUi(); }; #endif // MAINWINDOW_H ================================================ FILE: Anime_Template_Project/Generative_Lines/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { // 设置窗口大小 resize(600, 600); // 初始化半径和角度步长 outerRadius = 200.0; innerRadius = 200.0; angleStep = 15; // 每15度一个点 animation = new QPropertyAnimation(this, "innerRadius"); animation->setDuration(700); // 动画持续时间为5秒 animation->setEasingCurve(QEasingCurve::InOutCirc); // 使用缓动曲线 animation2 = new QPropertyAnimation(this, "angleStep"); animation2->setDuration(700); // 动画持续时间为5秒 animation2->setEasingCurve(QEasingCurve::InOutCirc); // 使用缓动曲线 timer = new QTimer(this); connect(timer, &QTimer::timeout, this,[this]{ count += 1; switch (count) { case 1: startangleStepAnimation(5,15); startAnimation(200,100); break; case 2: startAnimation(100,220); break; case 3: startAnimation(220,200); break; case 4: startangleStepAnimation(15,120); startAnimation(200,150); break; case 5: startangleStepAnimation(120,15); startAnimation(150,180); break; case 6: startangleStepAnimation(15,90); startAnimation(180,60); break; case 7: startAnimation(60,200); startangleStepAnimation(90,15); break; case 8: startAnimation(200,100); startangleStepAnimation(15,5); break; case 9: startAnimation(100,170); startangleStepAnimation(5,180); break; case 10: startAnimation(170,200); startangleStepAnimation(180,5); count = 0; // 重置计数器 break; } }); // 每次定时器超时更新界面 connect(animation, &QPropertyAnimation::valueChanged, this, [=]{ timer->start(100); // 启动定时器,每100毫秒触发一次 }); // 当动画值变化时更新界面 timer->start(100); // 启动定时器,每100毫秒触发一次 } Widget::~Widget() {} void Widget::startAnimation(qreal Start, qreal End) { timer->stop(); // 设置动画的起始和结束值 animation->setStartValue(Start); animation->setEndValue(End); // 启动动画 animation->start(); } void Widget::startangleStepAnimation(qreal Start, qreal End) { // 设置动画的起始和结束值 animation2->setStartValue(Start); animation2->setEndValue(End); // 启动动画 animation2->start(); } void Widget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,使线条更平滑 // 将原点移动到窗口中心 painter.translate(width() / 2, height() / 2); QPen pen(QColor(115,236,206), 2); // 设置画笔颜色为蓝色,线宽为2 painter.setPen(pen); painter.setBrush(Qt::NoBrush); QPolygonF polygon; // 计算多边形的顶点 for (qreal angle = 0; angle < 360; angle += angleStep) { // 计算外半径上的点 qreal xOuter = outerRadius * qCos(qDegreesToRadians(angle)); qreal yOuter = outerRadius * qSin(qDegreesToRadians(angle)); polygon << QPointF(xOuter, yOuter); // 计算内半径上的点 // 注意:下一个点是内半径上的点,角度与外半径点相同 // 这样可以确保内外半径点交替出现,形成星形或锯齿状 qreal xInner = innerRadius * qCos(qDegreesToRadians(angle + angleStep / 2)); // 调整内半径点的角度,使其在内外点之间 qreal yInner = innerRadius * qSin(qDegreesToRadians(angle + angleStep / 2)); polygon << QPointF(xInner, yInner); } // 绘制多边形 painter.drawPolygon(polygon); } qreal Widget::getOuterRadius() const { return outerRadius; } void Widget::setOuterRadius(qreal newOuterRadius) { outerRadius = newOuterRadius; update(); // 更新界面以反映新的外半径 } qreal Widget::getInnerRadius() const { return innerRadius; } void Widget::setInnerRadius(qreal newInnerRadius) { innerRadius = newInnerRadius; update(); // 更新界面以反映新的内半径 } qreal Widget::getAngleStep() const { return angleStep; } void Widget::setAngleStep(qreal newAngleStep) { angleStep = newAngleStep; update(); // 更新界面以反映新的角度步长 } ================================================ FILE: Anime_Template_Project/Generative_Lines/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include // 引入 QMetaType #include #include #include // For qDegreesToRadians #include //动画组 #include //定时器 #include class Widget : public QWidget { Q_OBJECT Q_PROPERTY(qreal outerRadius READ getOuterRadius WRITE setOuterRadius) Q_PROPERTY(qreal innerRadius READ getInnerRadius WRITE setInnerRadius) Q_PROPERTY(qreal angleStep READ getAngleStep WRITE setAngleStep) public: Widget(QWidget *parent = nullptr); ~Widget(); qreal getOuterRadius() const; void setOuterRadius(qreal newOuterRadius); qreal getInnerRadius() const; void setInnerRadius(qreal newInnerRadius); qreal getAngleStep() const; void setAngleStep(qreal newAngleStep); public: void startAnimation(qreal Start, qreal End); void startangleStepAnimation(qreal Start, qreal End); //计算边数量 protected: void paintEvent(QPaintEvent *event) override; private: qreal outerRadius; qreal innerRadius; qreal angleStep; // 角度步长 QTimer *timer; // 定时器 // 计数 int count = 0;; QPropertyAnimation *animation; // 属性动画 QPropertyAnimation *animation2; // 属性动画 }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/Glitch_Text/GlitchTextWidget.cpp ================================================ #include "GlitchTextWidget.h" #include #include #include #include GlitchTextWidget::GlitchTextWidget(const QString& text, QWidget *parent) : QWidget(parent), m_text(text), m_speed(1.0), m_enableShadows(true), m_enableOnHover(false), m_glitchFrameIndex(0), m_isMouseOver(false) { QFontDatabase::addApplicationFont("://Cyberpunk.ttf"); setFont(QFont("Cyberpunk", 72, QFont::Black)); setMouseTracking(true); m_animationAfter = new QPropertyAnimation(this, "clipRectAfter"); m_animationBefore = new QPropertyAnimation(this, "clipRectBefore"); m_animationAfter->setLoopCount(-1); m_animationBefore->setLoopCount(-1); m_animationAfter->setEasingCurve(QEasingCurve::Linear); m_animationBefore->setEasingCurve(QEasingCurve::Linear); m_glitchTimer = new QTimer(this); connect(m_glitchTimer, &QTimer::timeout, this, [this](){ if (m_clipKeyframes.isEmpty()) { updateClipKeyframes(); } if (!m_clipKeyframes.isEmpty()) { QMargins margins = m_clipKeyframes[m_glitchFrameIndex]; int clipY = (height() * margins.top()) / 100; int clipHeight = height() - clipY - (height() * margins.bottom()) / 100; m_clipRectAfter = QRect(0, clipY, width(), clipHeight); m_clipRectBefore = QRect(0, clipY, width(), clipHeight); m_glitchFrameIndex = (m_glitchFrameIndex + 1) % m_clipKeyframes.size(); update(); } }); setupAnimations(); if (!m_enableOnHover) { startGlitchAnimation(); } } void GlitchTextWidget::setText(const QString& text) { if (m_text != text) { m_text = text; update(); updateGeometry(); } } void GlitchTextWidget::setSpeed(qreal speed) { if (m_speed != speed) { m_speed = speed; setupAnimations(); if (!m_enableOnHover && m_glitchTimer->isActive()) { startGlitchAnimation(); } else if (m_enableOnHover && m_isMouseOver) { startGlitchAnimation(); } } } void GlitchTextWidget::setEnableShadows(bool enable) { if (m_enableShadows != enable) { m_enableShadows = enable; update(); } } void GlitchTextWidget::setEnableOnHover(bool enable) { if (m_enableOnHover != enable) { m_enableOnHover = enable; if (m_enableOnHover) { stopGlitchAnimation(); } else { startGlitchAnimation(); } update(); } } void GlitchTextWidget::setCustomClassName(const QString& className) { m_customClassName = className; } void GlitchTextWidget::updateClipKeyframes() { m_clipKeyframes.clear(); m_clipKeyframes.append(QMargins(0, 20, 0, 50)); m_clipKeyframes.append(QMargins(0, 10, 0, 60)); m_clipKeyframes.append(QMargins(0, 15, 0, 55)); m_clipKeyframes.append(QMargins(0, 25, 0, 35)); m_clipKeyframes.append(QMargins(0, 30, 0, 40)); m_clipKeyframes.append(QMargins(0, 40, 0, 20)); m_clipKeyframes.append(QMargins(0, 10, 0, 60)); m_clipKeyframes.append(QMargins(0, 15, 0, 55)); m_clipKeyframes.append(QMargins(0, 25, 0, 35)); m_clipKeyframes.append(QMargins(0, 00, 0, 40)); m_clipKeyframes.append(QMargins(0, 20, 0, 50)); m_clipKeyframes.append(QMargins(0, 10, 0, 60)); m_clipKeyframes.append(QMargins(0, 15, 0, 55)); m_clipKeyframes.append(QMargins(0, 25, 0, 35)); m_clipKeyframes.append(QMargins(0, 30, 0, 111)); m_clipKeyframes.append(QMargins(0, 40, 0, 20)); m_clipKeyframes.append(QMargins(0, 20, 0, 50)); m_clipKeyframes.append(QMargins(0, 10, 0, 60)); m_clipKeyframes.append(QMargins(0, 15, 0, 55)); m_clipKeyframes.append(QMargins(0, 25, 0, 35)); m_clipKeyframes.append(QMargins(0, 30, 0, 40)); } void GlitchTextWidget::setupAnimations() { updateClipKeyframes(); int totalAfterDurationMs = static_cast(m_speed * 3000); int totalBeforeDurationMs = static_cast(m_speed * 2000); if (m_clipKeyframes.isEmpty()) { m_glitchTimer->setInterval(30); } else { m_glitchTimer->setInterval(totalAfterDurationMs / m_clipKeyframes.size()); } m_animationAfter->setDuration(totalAfterDurationMs); m_animationBefore->setDuration(totalBeforeDurationMs); } void GlitchTextWidget::startGlitchAnimation() { if (!m_glitchTimer->isActive()) { m_glitchFrameIndex = 0; m_glitchTimer->start(); } } void GlitchTextWidget::stopGlitchAnimation() { if (m_glitchTimer->isActive()) { m_glitchTimer->stop(); m_clipRectAfter = rect(); m_clipRectBefore = rect(); update(); } } void GlitchTextWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setFont(font()); painter.fillRect(rect(), QColor("#060606")); QFontMetrics fm(font()); int textWidth = fm.horizontalAdvance(m_text); int textHeight = fm.height(); int textX = (width() - textWidth) / 2; int textY = (height() - textHeight) / 2 + fm.ascent(); painter.setPen(QColor(Qt::white)); painter.drawText(textX, textY, m_text); if (!m_enableOnHover || (m_enableOnHover && m_isMouseOver)) { painter.save(); painter.setPen(QColor(Qt::red)); painter.setClipRect(m_clipRectAfter); painter.drawText(textX - 10, textY, m_text); painter.restore(); painter.save(); painter.setPen(QColor(Qt::cyan)); painter.setClipRect(m_clipRectBefore); painter.drawText(textX + 10, textY, m_text); painter.restore(); } } void GlitchTextWidget::enterEvent(QEnterEvent *event) { m_isMouseOver = true; if (m_enableOnHover) { startGlitchAnimation(); } QWidget::enterEvent(event); } void GlitchTextWidget::leaveEvent(QEvent *event) { m_isMouseOver = false; if (m_enableOnHover) { stopGlitchAnimation(); } QWidget::leaveEvent(event); } QSize GlitchTextWidget::sizeHint() const { QFontMetrics fm(font()); int widthHint = fm.horizontalAdvance(m_text) + 20; int heightHint = fm.height() + 20; return QSize(widthHint, heightHint); } QRect GlitchTextWidget::clipRectAfter() const { return m_clipRectAfter; } void GlitchTextWidget::setClipRectAfter(const QRect& rect) { m_clipRectAfter = rect; update(); } QRect GlitchTextWidget::clipRectBefore() const { return m_clipRectBefore; } void GlitchTextWidget::setClipRectBefore(const QRect& rect) { m_clipRectBefore = rect; update(); } ================================================ FILE: Anime_Template_Project/Glitch_Text/GlitchTextWidget.h ================================================ #ifndef GLITCHTEXTWIDGET_H #define GLITCHTEXTWIDGET_H #include #include #include #include #include #include #include #include #include class GlitchTextWidget : public QWidget { Q_OBJECT Q_PROPERTY(QRect clipRectAfter READ clipRectAfter WRITE setClipRectAfter) Q_PROPERTY(QRect clipRectBefore READ clipRectBefore WRITE setClipRectBefore) public: explicit GlitchTextWidget(const QString& text, QWidget *parent = nullptr); void setText(const QString& text); void setSpeed(qreal speed); void setEnableShadows(bool enable); void setEnableOnHover(bool enable); void setCustomClassName(const QString& className); protected: void paintEvent(QPaintEvent *event) override; void enterEvent(QEnterEvent *event) override; void leaveEvent(QEvent *event) override; QSize sizeHint() const override; private: QString m_text; qreal m_speed; bool m_enableShadows; bool m_enableOnHover; QString m_customClassName; QPropertyAnimation *m_animationAfter; QPropertyAnimation *m_animationBefore; QTimer *m_glitchTimer; int m_glitchFrameIndex; QList m_clipKeyframes; bool m_isMouseOver; QRect m_clipRectAfter; QRect m_clipRectBefore; QRect clipRectAfter() const; void setClipRectAfter(const QRect& rect); QRect clipRectBefore() const; void setClipRectBefore(const QRect& rect); void setupAnimations(); void startGlitchAnimation(); void stopGlitchAnimation(); void updateClipKeyframes(); }; #endif // GLITCHTEXTWIDGET_H ================================================ FILE: Anime_Template_Project/Honeycomb_Grid/hexagonwidget.cpp ================================================ // Copyright (C) 2016 The Qt Company Ltd. // 版权所有 (C) 2016 Qt 公司。 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // SPDX-许可证标识符:LicenseRef-Qt-Commercial 或 LGPL-3.0-only 或 GPL-2.0-only 或 GPL-3.0-only #include "hexagonwidget.h" // 包含 HexagonWidget 的头文件。 #include // 包含 QPainter 类,用于在部件上进行绘制。 #include // 包含 Qt 数学函数,例如 qSqrt, qDegreesToRadians。 #include // 确保包含 QMouseEvent 头文件,用于处理鼠标事件。 // HexagonWidget 类的构造函数。 HexagonWidget::HexagonWidget(QWidget *parent) : QWidget(parent) // 调用基类 QWidget 的构造函数。 , m_hexagonSize(50.0) // 初始化六边形边长。 , m_hexagonSpacing(5.0) // 初始化六边形间距。 , m_isHovering(false) // 初始状态为未悬停。 { setMouseTracking(true); // 启用鼠标跟踪,即使没有按键也会触发 mouseMoveEvent。 calculateHexagonDimensions(); // 计算六边形的尺寸(宽度和高度)。 } // 设置六边形边长的方法。 void HexagonWidget::setHexagonSize(double size) { if (size > 0 && m_hexagonSize != size) // 如果新尺寸有效且与当前尺寸不同。 { m_hexagonSize = size; // 更新六边形边长。 calculateHexagonDimensions(); // 重新计算六边形尺寸。 update(); // 触发重绘以更新显示。 } } // 设置六边形间距的方法。 void HexagonWidget::setHexagonSpacing(double spacing) { if (spacing >= 0 && m_hexagonSpacing != spacing) // 如果新间距有效且与当前间距不同。 { m_hexagonSpacing = spacing; // 更新六边形间距。 calculateHexagonDimensions(); // 重新计算六边形尺寸。 update(); // 触发重绘以更新显示。 } } void HexagonWidget::startAnimation() { QPropertyAnimation* m_animation1 = new QPropertyAnimation(this, "Center_Hexagon"); // 创建一个新的动画,目标属性为 Center_Hexagon。 m_animation1->setDuration(350); // 设置动画持续时间为 1000 毫秒(1 秒)。 m_animation1->setStartValue(1.0); // 设置动画起始值为 1.0。 m_animation1->setEndValue(1.2); // 设置动画结束值为 2.0。 m_animation1->setEasingCurve(QEasingCurve::InOutQuad); // 设置缓动曲线为 InOutQuad,使动画平滑过渡。 QPropertyAnimation * m_animation2 = new QPropertyAnimation(this, "Edge_Hexagon"); // 创建另一个动画,目标属性为 Edge_Hexagon。 m_animation2->setDuration(350); // 设置动画持续时间为 1000 毫秒(1 秒)。 m_animation2->setStartValue(1.0); // 设置动画起始值为 1.0。 m_animation2->setEndValue(0.6); // 设置动画结束值为 0.7。 m_animation2->setEasingCurve(QEasingCurve::InOutQuad); // 设置缓动曲线为 InOutQuad,使动画平滑过渡。 m_animation1->start(QAbstractAnimation::DeleteWhenStopped); // 启动动画。 m_animation2->start(QAbstractAnimation::DeleteWhenStopped); // 启动第二个动画。 } // 计算六边形宽度和高度的方法。 void HexagonWidget::calculateHexagonDimensions() { m_hexagonWidth = m_hexagonSize * qSqrt(3.0); // 计算六边形宽度(对边距离)。 m_hexagonHeight = 2.0 * m_hexagonSize; // 计算六边形高度(顶点到对边顶点的距离)。 } // 修改 createHexagon 以便接收当前绘制的六边形大小。 // 根据中心点和六边形大小创建六边形的 QPolygonF 对象。 QPolygonF HexagonWidget::createHexagon(QPointF center, double currentHexagonSize) const { QPolygonF hexagon; // 创建一个 QPolygonF 对象。 for (int i = 0; i < 6; ++i) // 循环 6 次,为六边形的每个顶点。 { double angle_deg = 60 * i + 30; // 计算当前顶点的角度(度),加30度是为了使六边形平放。 double angle_rad = qDegreesToRadians(angle_deg); // 将角度从度转换为弧度。 hexagon << QPointF(center.x() + currentHexagonSize * qCos(angle_rad), // 计算顶点 X 坐标。 center.y() + currentHexagonSize * qSin(angle_rad)); // 计算顶点 Y 坐标。 } return hexagon; // 返回构建好的六边形多边形。 } // 新增:查找哪个六边形被悬停。 // 根据鼠标位置查找被悬停的六边形的中心点。 QPointF HexagonWidget::getHoveredHexagonCenter(const QPoint &pos) { // 计算实际的六边形宽度和高度,考虑间距。 double hexGridWidth = m_hexagonWidth + m_hexagonSpacing; // 计算网格中六边形的水平总宽度(包括间距)。 double hexGridHeight = m_hexagonHeight * 0.75 + m_hexagonSpacing; // 计算网格中六边形的垂直总高度(包括间距)。 // 初始偏移量。 double xOffset = m_hexagonWidth / 2.0; // 六边形第一列的 X 偏移。 double yOffset = m_hexagonSize; // 六边形第一行的 Y 偏移。 // 我们可以反向计算鼠标点可能落在哪一行哪一列。 // 这是一个简化的方法,更精确的需要考虑六边形形状。 // 粗略判断鼠标点在哪个“单元格”内。 // 考虑y轴偏移来找到最近的行。 int estimatedRow = qRound((pos.y() - yOffset) / hexGridHeight); // 估算鼠标所在行。 if (estimatedRow < 0) estimatedRow = 0; // 确保行号非负。 // 根据行数判断x轴偏移。 double currentXOffset = xOffset; // 默认 X 偏移。 if (estimatedRow % 2 != 0) { // 如果是奇数行。 currentXOffset += hexGridWidth / 2.0; // 奇数行会向右偏移半个网格宽度。 } // 考虑x轴偏移来找到最近的列。 int estimatedCol = qRound((pos.x() - currentXOffset) / hexGridWidth); // 估算鼠标所在列。 if (estimatedCol < 0) estimatedCol = 0; // 确保列号非负。 // 遍历鼠标点附近的几个六边形,判断精确的悬停。 // 遍历周围的 3x3 区域,以确保找到正确的六边形。 for (int r_offset = -1; r_offset <= 1; ++r_offset) { for (int c_offset = -1; c_offset <= 1; ++c_offset) { int row = estimatedRow + r_offset; // 计算实际行。 int col = estimatedCol + c_offset; // 计算实际列。 if (row < 0 || col < 0) continue; // 避免负索引,跳过无效的行或列。 double x = col * hexGridWidth + xOffset; // 计算当前六边形的 X 坐标。 double y = row * hexGridHeight + yOffset; // 计算当前六边形的 Y 坐标。 if (row % 2 != 0) { // 奇数行交错。 x += hexGridWidth / 2.0; // 如果是奇数行,X 坐标需要额外偏移。 } QPointF center(x, y); // 构成当前六边形的中心点。 // 检查鼠标点是否在当前计算出的六边形内部。 QPolygonF hexPolygon = createHexagon(center, m_hexagonSize); // 根据中心点和默认大小创建六边形多边形。 if (hexPolygon.containsPoint(pos, Qt::OddEvenFill)) { // 判断鼠标点是否在该多边形内。 return center; // 找到被悬停的六边形中心,并返回。 } } } return QPointF(); // 没有六边形被悬停,返回空 QPointF。 } // 新增:获取指定六边形的所有相邻六边形中心点。 // 根据给定的六边形中心点,计算并返回其所有相邻六边形的中心点列表。 QVector HexagonWidget::getNeighborHexagonCenters(QPointF center) const { QVector neighbors; // 用于存储相邻六边形中心的向量。 double hexGridWidth = m_hexagonWidth + m_hexagonSpacing; // 计算网格中六边形的水平总宽度。 double hexGridHeight = m_hexagonHeight * 0.75 + m_hexagonSpacing; // 计算网格中六边形的垂直总高度。 // 相邻偏移量 (相对一个六边形的中心)。 // 这是一个简化的表示,需要根据六边形中心推算其相邻六边形中心。 // 考虑六边形网格的两种相邻模式:水平方向和对角线方向。 // 直接水平相邻。 neighbors.append(QPointF(center.x() + hexGridWidth, center.y())); // 右侧相邻。 neighbors.append(QPointF(center.x() - hexGridWidth, center.y())); // 左侧相邻。 // 上下对角线相邻 (左上, 右上, 左下, 右下)。 // 这里的偏移量需要精确计算。 double x_offset_half = hexGridWidth / 2.0; // 水平偏移量的一半。 double y_offset_quarter_height = m_hexagonHeight * 0.75; // 六边形堆叠的垂直步长。 // 上方两相邻。 neighbors.append(QPointF(center.x() + x_offset_half, center.y() - y_offset_quarter_height - m_hexagonSpacing)); // 右上方相邻。 neighbors.append(QPointF(center.x() - x_offset_half, center.y() - y_offset_quarter_height - m_hexagonSpacing)); // 左上方相邻。 // 下方两相邻。 neighbors.append(QPointF(center.x() + x_offset_half, center.y() + y_offset_quarter_height + m_hexagonSpacing)); // 右下方相邻。 neighbors.append(QPointF(center.x() - x_offset_half, center.y() + y_offset_quarter_height + m_hexagonSpacing)); // 左下方相邻。 return neighbors; // 返回所有相邻六边形的中心点列表。 } // 绘制事件处理函数,负责绘制六边形网格。 void HexagonWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); // 标记 event 未使用,避免编译警告。 QPainter painter(this); // 创建 QPainter 对象,在当前部件上绘制。 painter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿,使图形更平滑。 painter.setPen(QPen(Qt::black, 1)); // 设置画笔为黑色,宽度为 1。 double hexGridWidth = m_hexagonWidth + m_hexagonSpacing; // 计算网格中六边形的水平总宽度。 double hexGridHeight = m_hexagonHeight * 0.75 + m_hexagonSpacing; // 计算网格中六边形的垂直总高度。 double xOffset = m_hexagonWidth / 2.0; // 六边形第一列的 X 偏移。 double yOffset = m_hexagonSize; // 六边形第一行的 Y 偏移。 // 获取相邻六边形的中心列表。 QVector neighborCenters; // 存储相邻六边形中心点的向量。 if (m_isHovering && !m_hoveredHexagonCenter.isNull()) { // 如果鼠标悬停且悬停中心点有效。 neighborCenters = getNeighborHexagonCenters(m_hoveredHexagonCenter); // 获取悬停六边形的相邻中心点。 } // 增加绘制范围,确保覆盖放大和缩小的六边形。 // 计算需要绘制的列数和行数,增加一些冗余以确保覆盖。 int numCols = qCeil((width() + m_hexagonSize * 2) / hexGridWidth) + 3; int numRows = qCeil((height() + m_hexagonSize * 2) / hexGridHeight) + 3; // 遍历所有可能的六边形位置。 for (int row = 0; row < numRows; ++row) { for (int col = 0; col < numCols; ++col) { double x = col * hexGridWidth + xOffset; // 计算当前六边形的 X 坐标。 double y = row * hexGridHeight + yOffset; // 计算当前六边形的 Y 坐标。 if (row % 2 != 0) { // 如果是奇数行。 x += hexGridWidth / 2.0; // 奇数行向右偏移。 } QPointF currentHexagonCenter(x, y); // 当前六边形的中心点。 // 只有当六边形完全或部分在可见区域内时才绘制,并考虑放大后的尺寸。 // 检查六边形是否在可见区域内。 if (x - m_hexagonSize * 2 < width() && x + m_hexagonSize * 2 > 0 && y - m_hexagonSize * 2 < height() && y + m_hexagonSize * 2 > 0) { double currentDrawSize = m_hexagonSize; // 默认绘制大小为 m_hexagonSize。 QBrush currentBrush = QBrush(Qt::cyan); // 默认填充颜色为青色。 if (m_isHovering) // 如果正在悬停。 { if (currentHexagonCenter == m_hoveredHexagonCenter) { // 如果是悬停的六边形。 currentDrawSize = m_hexagonSize * m_Center_Hexagon; // 放大1.3倍。 currentBrush = QBrush(Qt::red); // 悬停六边形颜色为红色。 } else if (neighborCenters.contains(currentHexagonCenter)) { // 如果是悬停六边形的相邻六边形。 currentDrawSize = m_hexagonSize * m_Edge_Hexagon; // 缩小0.7倍。 currentBrush = QBrush(Qt::darkCyan); // 相邻六边形颜色为深青色。 } } painter.setBrush(currentBrush); // 设置画刷。 painter.drawPolygon(createHexagon(currentHexagonCenter, currentDrawSize)); // 绘制六边形。 } } } } // 新增:鼠标移动事件处理。 void HexagonWidget::mouseMoveEvent(QMouseEvent *event) { QPointF oldHoverCenter = m_hoveredHexagonCenter; // 记录旧的悬停中心。 m_hoveredHexagonCenter = getHoveredHexagonCenter(event->pos()); // 获取新的悬停中心。 if (m_hoveredHexagonCenter.isNull() && m_isHovering) { // 鼠标移出所有六边形,或从一个六边形移到空白区域。 m_isHovering = false; // 设置未悬停状态。 update(); // 触发重绘,恢复所有六边形到默认大小。 } else if (!m_hoveredHexagonCenter.isNull() && m_hoveredHexagonCenter != oldHoverCenter) { // 鼠标移到新的六边形上。 m_isHovering = true; // 设置悬停状态。 startAnimation(); // 启动放大动画。 } QWidget::mouseMoveEvent(event); // 调用基类的事件处理。 } // 新增:鼠标离开控件事件处理。 void HexagonWidget::leaveEvent(QEvent *event) { if (m_isHovering) { // 如果之前处于悬停状态。 m_isHovering = true; // 设置未悬停状态。 m_hoveredHexagonCenter = QPointF(); // 清空悬停中心。 } QWidget::leaveEvent(event); // 调用基类的事件处理。 } qreal HexagonWidget::Center_Hexagon() const { return m_Center_Hexagon; } void HexagonWidget::setCenter_Hexagon(qreal newCenter_Hexagon) { if (qFuzzyCompare(m_Center_Hexagon, newCenter_Hexagon)) return; m_Center_Hexagon = newCenter_Hexagon; update(); // 更新绘制以反映新的中心六边形大小。 emit Center_HexagonChanged(); } qreal HexagonWidget::Edge_Hexagon() const { return m_Edge_Hexagon; } void HexagonWidget::setEdge_Hexagon(qreal newEdge_Hexagon) { if (qFuzzyCompare(m_Edge_Hexagon, newEdge_Hexagon)) return; m_Edge_Hexagon = newEdge_Hexagon; emit Edge_HexagonChanged(); } ================================================ FILE: Anime_Template_Project/Honeycomb_Grid/hexagonwidget.h ================================================ // Copyright (C) 2016 The Qt Company Ltd. // 版权所有 (C) 2016 Qt 公司。 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // SPDX-许可证标识符:LicenseRef-Qt-Commercial 或 LGPL-3.0-only 或 GPL-2.0-only 或 GPL-3.0-only #ifndef HEXAGONWIDGET_H // 如果未定义 HEXAGONWIDGET_H。 #define HEXAGONWIDGET_H // 定义 HEXAGONWIDGET_H。 #include // 包含 QWidget 类,它是所有用户界面对象的基类。 #include // 包含 QVector 类,提供动态数组。 #include // 包含 QPointF 类,表示浮点精确度的点。 #include // 用于存储六边形中心点和其对应的多边形。 //动画类 #include class HexagonWidget : public QWidget // HexagonWidget 类声明,继承自 QWidget。 { Q_OBJECT // 宏,用于启用 Qt 的元对象系统,支持信号与槽。 //放大 Q_PROPERTY(qreal Center_Hexagon READ Center_Hexagon WRITE setCenter_Hexagon NOTIFY Center_HexagonChanged FINAL) Q_PROPERTY(qreal Edge_Hexagon READ Edge_Hexagon WRITE setEdge_Hexagon NOTIFY Edge_HexagonChanged FINAL) public: explicit HexagonWidget(QWidget *parent = nullptr); // 显式构造函数,允许指定父部件。 void setHexagonSize(double size); // 设置六边形的基准边长。 void setHexagonSpacing(double spacing); // 设置六边形之间的间距。 //执行动画 void startAnimation(); // 启动六边形放大动画。 qreal Center_Hexagon() const; void setCenter_Hexagon(qreal newCenter_Hexagon); qreal Edge_Hexagon() const; void setEdge_Hexagon(qreal newEdge_Hexagon); signals: void Center_HexagonChanged(); void Edge_HexagonChanged(); protected: void paintEvent(QPaintEvent *event) override; // 覆盖 paintEvent,处理部件的绘制事件。 void mouseMoveEvent(QMouseEvent *event) override; // 新增:鼠标移动事件处理函数。 void leaveEvent(QEvent *event) override; // 新增:鼠标离开部件事件处理函数。 QPolygonF createHexagon(QPointF center, double currentHexagonSize) const; // 修改:增加一个参数来控制当前绘制的六边形大小。根据中心点和给定大小创建六边形多边形。 private: double m_hexagonSize; // 六边形基准边长。 double m_hexagonSpacing; // 六边形间距。 double m_hexagonWidth; // 六边形宽度 (两相对边距离)。 double m_hexagonHeight; // 六边形高度 (两相对顶点距离)。 QPointF m_hoveredHexagonCenter; // 存储当前鼠标悬停的六边形中心点。 bool m_isHovering; // 标记是否正在悬停在某个六边形上。 void calculateHexagonDimensions(); // 计算六边形的内部尺寸(宽度和高度)。 // 根据鼠标位置查找哪个六边形被悬停。 QPointF getHoveredHexagonCenter(const QPoint &pos); // 获取指定中心点的六边形的所有相邻六边形的中心点。 QVector getNeighborHexagonCenters(QPointF center) const; qreal m_Center_Hexagon; // 当前中心六边形的大小。 qreal m_Edge_Hexagon; // 当前边缘六边形的大小。 }; #endif // HEXAGONWIDGET_H // 结束 HEXAGONWIDGET_H 头文件保护。 ================================================ FILE: Anime_Template_Project/Honeycomb_Grid/mainwindow.cpp ================================================ #include "mainwindow.h" #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 创建中心Widget和布局 QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 添加 HexagonWidget m_hexagonWidget = new HexagonWidget(this); mainLayout->addWidget(m_hexagonWidget); // 添加控制滑块 QHBoxLayout *controlsLayout = new QHBoxLayout(); // 边长控制 m_sizeLabel = new QLabel("边长: 50", this); m_sizeSlider = new QSlider(Qt::Horizontal, this); m_sizeSlider->setRange(10, 100); m_sizeSlider->setValue(50); connect(m_sizeSlider, &QSlider::valueChanged, this, &MainWindow::onSizeSliderChanged); controlsLayout->addWidget(m_sizeLabel); controlsLayout->addWidget(m_sizeSlider); // 间距控制 m_spacingLabel = new QLabel("间距: 5", this); m_spacingSlider = new QSlider(Qt::Horizontal, this); m_spacingSlider->setRange(0, 30); m_spacingSlider->setValue(5); connect(m_spacingSlider, &QSlider::valueChanged, this, &MainWindow::onSpacingSliderChanged); controlsLayout->addWidget(m_spacingLabel); controlsLayout->addWidget(m_spacingSlider); mainLayout->addLayout(controlsLayout); setWindowTitle("蜂巢网格"); resize(800, 600); } MainWindow::~MainWindow() { } void MainWindow::onSizeSliderChanged(int value) { m_hexagonWidget->setHexagonSize(static_cast(value)); m_sizeLabel->setText(QString("边长: %1").arg(value)); } void MainWindow::onSpacingSliderChanged(int value) { m_hexagonWidget->setHexagonSpacing(static_cast(value)); m_spacingLabel->setText(QString("间距: %1").arg(value)); } ================================================ FILE: Anime_Template_Project/Honeycomb_Grid/mainwindow.h ================================================ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "hexagonwidget.h" #include #include class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onSizeSliderChanged(int value); void onSpacingSliderChanged(int value); private: HexagonWidget *m_hexagonWidget; QSlider *m_sizeSlider; QSlider *m_spacingSlider; QLabel *m_sizeLabel; QLabel *m_spacingLabel; }; #endif // MAINWINDOW_H ================================================ FILE: Anime_Template_Project/Letter_Glitch/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.19) project(untitled4 LANGUAGES CXX) find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets) find_package(Qt6 REQUIRED COMPONENTS Widgets) find_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets) qt_standard_project_setup() qt_add_executable(untitled4 WIN32 MACOSX_BUNDLE main.cpp GlitchEffectWidget.cpp GlitchEffectWidget.h ) target_link_libraries(untitled4 PRIVATE Qt::Core Qt::Widgets ) target_link_libraries(untitled4 PRIVATE Qt6::Widgets) target_link_libraries(untitled4 PRIVATE Qt6::OpenGLWidgets) include(GNUInstallDirs) install(TARGETS untitled4 BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) qt_generate_deploy_app_script( TARGET untitled4 OUTPUT_SCRIPT deploy_script NO_UNSUPPORTED_PLATFORM_ERROR ) install(SCRIPT ${deploy_script}) ================================================ FILE: Anime_Template_Project/Letter_Glitch/GlitchEffectWidget.cpp ================================================ #include "GlitchEffectWidget.h" #include #include #include #include #include #include GlitchRenderThread::GlitchRenderThread(QObject *parent) : QThread(parent), m_columns(0), m_rows(0), m_charWidth(0), m_charHeight(0), m_fontSize(0), m_centerVignette(false), m_outerVignette(false), m_widgetWidth(0), m_widgetHeight(0), m_isDirtyFullRedraw(false) { setObjectName("GlitchRenderThread"); } GlitchRenderThread::~GlitchRenderThread() { requestInterruption(); m_condition.wakeAll(); wait(); } void GlitchRenderThread::setRenderData(const std::vector& letters, int columns, int rows, int charWidth, int charHeight, int fontSize, bool centerVignette, bool outerVignette, int widgetWidth, int widgetHeight) { QMutexLocker locker(&m_mutex); m_letters = letters; m_columns = columns; m_rows = rows; m_charWidth = charWidth; m_charHeight = charHeight; m_fontSize = fontSize; m_centerVignette = centerVignette; m_outerVignette = outerVignette; m_widgetWidth = widgetWidth; m_widgetHeight = widgetHeight; m_isDirtyFullRedraw = true; m_pendingChangedIndexes.clear(); } void GlitchRenderThread::requestRender(const QList& changedIndexes) { QMutexLocker locker(&m_mutex); if (!changedIndexes.isEmpty()) { m_pendingChangedIndexes.append(changedIndexes); } if (!m_isDirtyFullRedraw || !changedIndexes.isEmpty()) { m_condition.wakeOne(); } } void GlitchRenderThread::run() { qDebug() << "GlitchRenderThread started"; while (!isInterruptionRequested()) { m_mutex.lock(); if (!m_isDirtyFullRedraw && m_pendingChangedIndexes.empty()) { m_condition.wait(&m_mutex); } bool doFullRedraw = m_isDirtyFullRedraw; m_isDirtyFullRedraw = false; QList currentChangedIndexes = m_pendingChangedIndexes; m_pendingChangedIndexes.clear(); m_mutex.unlock(); if (isInterruptionRequested()) { break; } if (m_widgetWidth <= 0 || m_widgetHeight <= 0 || m_letters.empty() || m_columns <= 0 || m_rows <= 0) { continue; } QSharedPointer currentFrame; QList dirtyRects; if (doFullRedraw || m_lastRenderedImage.isNull() || m_lastRenderedImage->width() != m_widgetWidth || m_lastRenderedImage->height() != m_widgetHeight) { currentFrame = QSharedPointer::create(m_widgetWidth, m_widgetHeight, QImage::Format_ARGB32_Premultiplied); currentFrame->fill(Qt::black); dirtyRects.append(currentFrame->rect()); QPainter painter(currentFrame.data()); painter.setRenderHint(QPainter::Antialiasing, true); QFont font("monospace", m_fontSize); painter.setFont(font); for (size_t i = 0; i < m_letters.size(); ++i) { const GlitchLetter& letter = m_letters.at(i); int x = (i % m_columns) * m_charWidth; int y = (i / m_columns) * m_charHeight; painter.setPen(letter.color); painter.drawText(x, y + m_fontSize, QString(letter.character)); } drawVignettes(painter, m_widgetWidth, m_widgetHeight); } else { currentFrame = m_lastRenderedImage; QPainter painter(currentFrame.data()); painter.setRenderHint(QPainter::Antialiasing, true); QFont font("monospace", m_fontSize); painter.setFont(font); for (int index : currentChangedIndexes) { if (index < 0 || index >= static_cast(m_letters.size())) continue; const GlitchLetter& letter = m_letters.at(index); int x = (index % m_columns) * m_charWidth; int y = (index / m_columns) * m_charHeight; QRect charRect(x, y, m_charWidth, m_charHeight); painter.fillRect(charRect, Qt::black); painter.setPen(letter.color); painter.drawText(x, y + m_fontSize, QString(letter.character)); dirtyRects.append(charRect); } } m_lastRenderedImage = currentFrame; emit frameReady(currentFrame, dirtyRects); } qDebug() << "GlitchRenderThread stopped"; } QList GlitchRenderThread::mergeRects(const QList& rects) { if (rects.isEmpty()) return QList(); QRect bounding = rects.first(); for (int i = 1; i < rects.size(); ++i) { bounding = bounding.united(rects.at(i)); } return QList() << bounding; } void GlitchRenderThread::drawVignettes(QPainter& painter, int w, int h) { if (m_outerVignette) { QRadialGradient outerGradient(w / 2.0, h / 2.0, qMin(w, h) / 2.0); outerGradient.setColorAt(0.6, Qt::transparent); outerGradient.setColorAt(1.0, Qt::black); painter.setBrush(outerGradient); painter.setPen(Qt::NoPen); painter.drawRect(0, 0, w, h); } if (m_centerVignette) { QRadialGradient centerGradient(w / 2.0, h / 2.0, qMin(w, h) / 2.0); centerGradient.setColorAt(0.0, QColor(0, 0, 0, 204)); centerGradient.setColorAt(0.6, Qt::transparent); painter.setBrush(centerGradient); painter.setPen(Qt::NoPen); painter.drawRect(0, 0, w, h); } } GlitchEffectWidget::GlitchEffectWidget(QWidget *parent) : QWidget(parent), m_glitchColors({QColor(43, 69, 57, 255), QColor(97, 220, 163, 255), QColor(97, 179, 220, 255)}), m_glitchSpeed(22), m_smoothTransitions(true), m_centerVignette(false), m_outerVignette(true), m_fontSize(16) { QPalette pal = palette(); pal.setColor(QPalette::Window, Qt::black); setAutoFillBackground(true); setPalette(pal); m_lettersAndSymbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$&*()-_+=/[]{};:<>,0123456789"; QFont font("monospace", m_fontSize); QFontMetricsF fm(font); m_charWidth = static_cast(fm.averageCharWidth()); m_charHeight = static_cast(fm.height()); m_glitchTimer = new QTimer(this); connect(m_glitchTimer, &QTimer::timeout, this, &GlitchEffectWidget::updateGlitch); m_glitchTimer->start(m_glitchSpeed); m_smoothTimer = new QTimer(this); connect(m_smoothTimer, &QTimer::timeout, this, &GlitchEffectWidget::handleSmoothColorTransitions); m_smoothTimer->start(16); int numThreads = QThread::idealThreadCount(); if (numThreads <= 0) { numThreads = 1; } qDebug() << "Creating" << numThreads << "render threads."; for (int i = 0; i < numThreads; ++i) { GlitchRenderThread *thread = new GlitchRenderThread(this); thread->setObjectName(QString("GlitchRenderThread_%1").arg(i)); connect(thread, &GlitchRenderThread::frameReady, this, &GlitchEffectWidget::handleFrameReady); m_renderThreads.append(thread); thread->start(); } m_displayTimer = new QTimer(this); connect(m_displayTimer, &QTimer::timeout, this, &GlitchEffectWidget::displayNextFrame); m_displayTimer->start(16); } GlitchEffectWidget::~GlitchEffectWidget() { m_displayTimer->stop(); for (GlitchRenderThread *thread : m_renderThreads) { if (thread->isRunning()) { thread->requestInterruption(); } } m_queueCondition.wakeAll(); for (GlitchRenderThread *thread : m_renderThreads) { thread->wait(); delete thread; } m_renderThreads.clear(); } void GlitchEffectWidget::setGlitchColors(const QList& colors) { m_glitchColors = colors; QList changedIndexes; QMutexLocker locker(&m_lettersMutex); for (size_t i = 0; i < m_letters.size(); ++i) { GlitchLetter& letter = m_letters[i]; letter.color = getRandomColor(); letter.targetColor = getRandomColor(); letter.colorProgress = 1.0; changedIndexes.append(static_cast(i)); } for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(changedIndexes); } } void GlitchEffectWidget::setGlitchSpeed(int speed) { m_glitchSpeed = speed; m_glitchTimer->start(m_glitchSpeed); } void GlitchEffectWidget::setSmoothTransitions(bool smooth) { m_smoothTransitions = smooth; if (m_smoothTransitions) { m_smoothTimer->start(16); } else { m_smoothTimer->stop(); QList changedIndexes; QMutexLocker locker(&m_lettersMutex); for (size_t i = 0; i < m_letters.size(); ++i) { GlitchLetter& letter = m_letters[i]; if (letter.colorProgress < 1.0) { letter.color = letter.targetColor; letter.colorProgress = 1.0; changedIndexes.append(static_cast(i)); } } for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(changedIndexes); } } } void GlitchEffectWidget::setCenterVignette(bool enabled) { m_centerVignette = enabled; for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(); } } void GlitchEffectWidget::setOuterVignette(bool enabled) { m_outerVignette = enabled; for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(); } } QColor GlitchEffectWidget::hexToQColor(const QString& hex) { QString actualHex = hex; if (actualHex.startsWith("#")) { actualHex = actualHex.mid(1); } if (actualHex.length() == 3) { actualHex = QString(actualHex.at(0)) + actualHex.at(0) + QString(actualHex.at(1)) + actualHex.at(1) + QString(actualHex.at(2)) + actualHex.at(2); } return QColor("#" + actualHex); } QChar GlitchEffectWidget::getRandomChar() { return m_lettersAndSymbols.at(QRandomGenerator::global()->bounded(m_lettersAndSymbols.length())); } QColor GlitchEffectWidget::getRandomColor() { return m_glitchColors.at(QRandomGenerator::global()->bounded(m_glitchColors.length())); } void GlitchEffectWidget::calculateGrid(int width, int height) { QFont font("monospace", m_fontSize); QFontMetricsF fm(font); m_charWidth = static_cast(fm.averageCharWidth()); m_charHeight = static_cast(fm.height()); qreal dpr = devicePixelRatioF(); int actualWidth = static_cast(width * dpr); int actualHeight = static_cast(height * dpr); m_columns = qCeil(static_cast(actualWidth) / m_charWidth); m_rows = qCeil(static_cast(actualHeight) / m_charHeight); } void GlitchEffectWidget::initializeLetters() { QMutexLocker locker(&m_lettersMutex); m_letters.clear(); int totalLetters = m_columns * m_rows; m_letters.reserve(totalLetters); for (int i = 0; i < totalLetters; ++i) { m_letters.push_back({getRandomChar(), getRandomColor(), getRandomColor(), 1.0}); } for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(); } } void GlitchEffectWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); if (!m_currentDisplayedImage.isNull()) { if (!m_currentDirtyRects.isEmpty()) { qreal dpr = devicePixelRatioF(); for (const QRect& dirtyRect : m_currentDirtyRects) { QRect actualDirtyRect(dirtyRect.x() * dpr, dirtyRect.y() * dpr, dirtyRect.width() * dpr, dirtyRect.height() * dpr); painter.drawImage(dirtyRect.topLeft(), *m_currentDisplayedImage, actualDirtyRect); } } else { painter.drawImage(0, 0, *m_currentDisplayedImage); } } } void GlitchEffectWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); calculateGrid(event->size().width(), event->size().height()); initializeLetters(); } void GlitchEffectWidget::updateGlitch() { QMutexLocker locker(&m_lettersMutex); if (m_letters.empty()) { return; } int updateCount = qMax(1, static_cast(m_letters.size() * 0.05)); QList changedIndexes; for (int i = 0; i < updateCount; ++i) { int index = QRandomGenerator::global()->bounded(static_cast(m_letters.size())); GlitchLetter& letter = m_letters[index]; letter.character = getRandomChar(); letter.targetColor = getRandomColor(); if (!m_smoothTransitions) { letter.color = letter.targetColor; letter.colorProgress = 1.0; } else { letter.colorProgress = 0.0; } changedIndexes.append(index); } for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(changedIndexes); } } void GlitchEffectWidget::handleSmoothColorTransitions() { if (!m_smoothTransitions) { return; } QList changedIndexes; QMutexLocker locker(&m_lettersMutex); for (size_t i = 0; i < m_letters.size(); ++i) { GlitchLetter& letter = m_letters[i]; if (letter.colorProgress < 1.0) { letter.colorProgress += 0.05; if (letter.colorProgress > 1.0) { letter.colorProgress = 1.0; } int startR = letter.color.red(); int startG = letter.color.green(); int startB = letter.color.blue(); int endR = letter.targetColor.red(); int endG = letter.targetColor.green(); int endB = letter.targetColor.blue(); int r = qRound(startR + (endR - startR) * letter.colorProgress); int g = qRound(startG + (endG - startG) * letter.colorProgress); int b = qRound(startB + (endB - startB) * letter.colorProgress); letter.color = QColor(qBound(0, r, 255), qBound(0, g, 255), qBound(0, b, 255)); changedIndexes.append(static_cast(i)); } } if (!changedIndexes.isEmpty()) { for (GlitchRenderThread* thread : m_renderThreads) { thread->setRenderData(m_letters, m_columns, m_rows, m_charWidth, m_charHeight, m_fontSize, m_centerVignette, m_outerVignette, static_cast(width() * devicePixelRatioF()), static_cast(height() * devicePixelRatioF())); thread->requestRender(changedIndexes); } } } void GlitchEffectWidget::handleFrameReady(QSharedPointer image, const QList& dirtyRects) { QMutexLocker locker(&m_queueMutex); m_renderedFrameQueue.enqueue(qMakePair(image, dirtyRects)); m_queueCondition.wakeOne(); } void GlitchEffectWidget::displayNextFrame() { QMutexLocker locker(&m_queueMutex); if (!m_renderedFrameQueue.isEmpty()) { QPair, QList> nextFrame = m_renderedFrameQueue.dequeue(); m_currentDisplayedImage = nextFrame.first; m_currentDirtyRects = nextFrame.second; if (!m_currentDirtyRects.isEmpty()) { QRect unitedRect = m_currentDirtyRects.first(); for (int i = 1; i < m_currentDirtyRects.size(); ++i) { unitedRect = unitedRect.united(m_currentDirtyRects.at(i)); } update(unitedRect); } else { update(); } } } ================================================ FILE: Anime_Template_Project/Letter_Glitch/GlitchEffectWidget.h ================================================ #ifndef GLITCHEFFECTWIDGET_H #define GLITCHEFFECTWIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct GlitchLetter { QChar character; QColor color; QColor targetColor; double colorProgress; }; class GlitchRenderThread : public QThread { Q_OBJECT public: GlitchRenderThread(QObject *parent = nullptr); ~GlitchRenderThread(); void setRenderData(const std::vector& letters, int columns, int rows, int charWidth, int charHeight, int fontSize, bool centerVignette, bool outerVignette, int widgetWidth, int widgetHeight); void requestRender(const QList& changedIndexes = QList()); signals: void frameReady(QSharedPointer image, const QList& dirtyRects); protected: void run() override; private: std::vector m_letters; int m_columns; int m_rows; int m_charWidth; int m_charHeight; int m_fontSize; bool m_centerVignette; bool m_outerVignette; int m_widgetWidth; int m_widgetHeight; bool m_isDirtyFullRedraw; QList m_pendingChangedIndexes; QMutex m_mutex; QWaitCondition m_condition; QSharedPointer m_lastRenderedImage; void drawVignettes(QPainter& painter, int w, int h); QList mergeRects(const QList& rects); }; class GlitchEffectWidget : public QWidget { Q_OBJECT public: explicit GlitchEffectWidget(QWidget *parent = nullptr); ~GlitchEffectWidget(); void setGlitchColors(const QList& colors); void setGlitchSpeed(int speed); void setSmoothTransitions(bool smooth); void setCenterVignette(bool enabled); void setOuterVignette(bool enabled); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void updateGlitch(); void handleSmoothColorTransitions(); void handleFrameReady(QSharedPointer image, const QList& dirtyRects); void displayNextFrame(); private: QList m_glitchColors; int m_glitchSpeed; bool m_smoothTransitions; bool m_centerVignette; bool m_outerVignette; QTimer *m_glitchTimer; QTimer *m_smoothTimer; QTimer *m_displayTimer; std::vector m_letters; int m_columns; int m_rows; int m_fontSize; int m_charWidth; int m_charHeight; QString m_lettersAndSymbols; QColor hexToQColor(const QString& hex); QChar getRandomChar(); QColor getRandomColor(); void calculateGrid(int width, int height); void initializeLetters(); QList m_renderThreads; QQueue, QList>> m_renderedFrameQueue; QMutex m_queueMutex; QWaitCondition m_queueCondition; QSharedPointer m_currentDisplayedImage; QList m_currentDirtyRects; QMutex m_lettersMutex; }; #endif ================================================ FILE: Anime_Template_Project/Letter_Glitch/main.cpp ================================================ #include #include #include "GlitchEffectWidget.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; window.resize(800, 600); QWidget *centralWidget = new QWidget(&window); QVBoxLayout *layout = new QVBoxLayout(centralWidget); GlitchEffectWidget *glitchWidget = new GlitchEffectWidget(centralWidget); layout->addWidget(glitchWidget); QPushButton *smoothToggleBtn = new QPushButton("平滑过渡开关按钮", centralWidget); QObject::connect(smoothToggleBtn, &QPushButton::clicked, [glitchWidget]() { glitchWidget->setSmoothTransitions(!glitchWidget->property("smoothTransitions").toBool()); glitchWidget->setProperty("smoothTransitions", !glitchWidget->property("smoothTransitions").toBool()); }); glitchWidget->setProperty("smoothTransitions", true); QPushButton *centerVignetteToggleBtn = new QPushButton("中心渐晕开关按钮", centralWidget); QObject::connect(centerVignetteToggleBtn, &QPushButton::clicked, [glitchWidget]() { glitchWidget->setCenterVignette(!glitchWidget->property("centerVignette").toBool()); glitchWidget->setProperty("centerVignette", !glitchWidget->property("centerVignette").toBool()); }); glitchWidget->setProperty("centerVignette", false); QPushButton *outerVignetteToggleBtn = new QPushButton("外部渐晕开关按钮", centralWidget); QObject::connect(outerVignetteToggleBtn, &QPushButton::clicked, [glitchWidget]() { glitchWidget->setOuterVignette(!glitchWidget->property("outerVignette").toBool()); glitchWidget->setProperty("outerVignette", !glitchWidget->property("outerVignette").toBool()); }); glitchWidget->setProperty("outerVignette", true); layout->addWidget(smoothToggleBtn); layout->addWidget(centerVignetteToggleBtn); layout->addWidget(outerVignetteToggleBtn); window.setCentralWidget(centralWidget); window.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/Liquid/image_processing.cpp ================================================ #include "image_processing.h" Q_GLOBAL_STATIC(Image_Processing, fileRelatedInstance) Image_Processing::Image_Processing(QObject* parent) : QObject{ parent } { } Image_Processing* Image_Processing::instance() { return fileRelatedInstance; } template void Image_Processing::qt_memrotate270_tiled_unpacked(const T* src, int w, int h, int sstride, T* dest, int dstride) { const int numTilesX = (w + tileSize - 1) / tileSize; const int numTilesY = (h + tileSize - 1) / tileSize; for (int tx = 0; tx < numTilesX; ++tx) { const int startx = tx * tileSize; const int stopx = qMin(startx + tileSize, w); for (int ty = 0; ty < numTilesY; ++ty) { const int starty = h - 1 - ty * tileSize; const int stopy = qMax(starty - tileSize, 0); for (int x = startx; x < stopx; ++x) { T* d = (T*)((char*)dest + x * dstride) + h - 1 - starty; const char* s = (const char*)(src + x) + starty * sstride; for (int y = starty; y >= stopy; --y) { *d++ = *(const T*)s; s -= sstride; } } } } } template void Image_Processing::qt_memrotate270_template(const T* src, int srcWidth, int srcHeight, int srcStride, T* dest, int dstStride) { qt_memrotate270_tiled_unpacked(src, srcWidth, srcHeight, srcStride, dest, dstStride); } template void Image_Processing::qt_memrotate90_tiled_unpacked(const T* src, int w, int h, int sstride, T* dest, int dstride) { const int numTilesX = (w + tileSize - 1) / tileSize; const int numTilesY = (h + tileSize - 1) / tileSize; for (int tx = 0; tx < numTilesX; ++tx) { const int startx = w - tx * tileSize - 1; const int stopx = qMax(startx - tileSize, 0); for (int ty = 0; ty < numTilesY; ++ty) { const int starty = ty * tileSize; const int stopy = qMin(starty + tileSize, h); for (int x = startx; x >= stopx; --x) { T* d = (T*)((char*)dest + (w - x - 1) * dstride) + starty; const char* s = (const char*)(src + x) + starty * sstride; for (int y = starty; y < stopy; ++y) { *d++ = *(const T*)(s); s += sstride; } } } } } template void Image_Processing::qt_memrotate90_template(const T* src, int srcWidth, int srcHeight, int srcStride, T* dest, int dstStride) { qt_memrotate90_tiled_unpacked(src, srcWidth, srcHeight, srcStride, dest, dstStride); } template inline int qt_static_shift(int value) { if (shift == 0) return value; else if (shift > 0) return value << (uint(shift) & 0x1f); else return value >> (uint(-shift) & 0x1f); } template void Image_Processing::qt_blurinner(uchar* bptr, int& zR, int& zG, int& zB, int& zA, int alpha) { QRgb* pixel = (QRgb*)bptr; const int A_zprec = qt_static_shift(*pixel) & (0xff << zprec); const int R_zprec = qt_static_shift(*pixel) & (0xff << zprec); const int G_zprec = qt_static_shift(*pixel) & (0xff << zprec); const int B_zprec = qt_static_shift(*pixel) & (0xff << zprec); const int zR_zprec = zR >> aprec; const int zG_zprec = zG >> aprec; const int zB_zprec = zB >> aprec; const int zA_zprec = zA >> aprec; zR += alpha * (R_zprec - zR_zprec); zG += alpha * (G_zprec - zG_zprec); zB += alpha * (B_zprec - zB_zprec); zA += alpha * (A_zprec - zA_zprec); *pixel = qt_static_shift<24 - zprec - aprec>(zA & (0xff << (zprec + aprec))) | qt_static_shift<16 - zprec - aprec>(zR & (0xff << (zprec + aprec))) | qt_static_shift<8 - zprec - aprec>(zG & (0xff << (zprec + aprec))) | qt_static_shift<-zprec - aprec>(zB & (0xff << (zprec + aprec))); } const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3); template void Image_Processing::qt_blurinner_alphaOnly(uchar* bptr, int& z, int alpha) { const int A_zprec = int(*(bptr)) << zprec; const int z_zprec = z >> aprec; z += alpha * (A_zprec - z_zprec); *(bptr) = z >> (zprec + aprec); } template void Image_Processing::qt_blurrow(QImage& im, int line, int alpha) { uchar* bptr = im.scanLine(line); int zR = 0, zG = 0, zB = 0, zA = 0; if (alphaOnly && im.format() != QImage::Format_Indexed8) bptr += alphaIndex; const int stride = im.depth() >> 3; const int im_width = im.width(); for (int index = 0; index < im_width; ++index) { if (alphaOnly) qt_blurinner_alphaOnly(bptr, zA, alpha); else qt_blurinner(bptr, zR, zG, zB, zA, alpha); bptr += stride; } bptr -= stride; for (int index = im_width - 2; index >= 0; --index) { bptr -= stride; if (alphaOnly) qt_blurinner_alphaOnly(bptr, zA, alpha); else qt_blurinner(bptr, zR, zG, zB, zA, alpha); } } template void Image_Processing::expblur(QImage& img, qreal radius, bool improvedQuality, int transposed) { if (improvedQuality) radius *= qreal(0.5); Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied || img.format() == QImage::Format_RGB32 || img.format() == QImage::Format_Indexed8 || img.format() == QImage::Format_Grayscale8); const qreal cutOffIntensity = 2; int alpha = radius <= qreal(1e-5) ? ((1 << aprec) - 1) : qRound((1 << aprec) * (1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius))); int img_height = img.height(); for (int row = 0; row < img_height; ++row) { for (int i = 0; i <= int(improvedQuality); ++i) qt_blurrow(img, row, alpha); } QImage temp(img.height(), img.width(), img.format()); temp.setDevicePixelRatio(img.devicePixelRatioF()); if (transposed >= 0) { if (img.depth() == 8) { qt_memrotate270_template(reinterpret_cast(img.bits()), img.width(), img.height(), img.bytesPerLine(), reinterpret_cast(temp.bits()), temp.bytesPerLine()); } else { qt_memrotate270_template(reinterpret_cast(img.bits()), img.width(), img.height(), img.bytesPerLine(), reinterpret_cast(temp.bits()), temp.bytesPerLine()); } } else { if (img.depth() == 8) { qt_memrotate90_template(reinterpret_cast(img.bits()), img.width(), img.height(), img.bytesPerLine(), reinterpret_cast(temp.bits()), temp.bytesPerLine()); } else { qt_memrotate90_template(reinterpret_cast(img.bits()), img.width(), img.height(), img.bytesPerLine(), reinterpret_cast(temp.bits()), temp.bytesPerLine()); } } img_height = temp.height(); for (int row = 0; row < img_height; ++row) { for (int i = 0; i <= int(improvedQuality); ++i) qt_blurrow(temp, row, alpha); } if (transposed == 0) { if (img.depth() == 8) { qt_memrotate90_template(reinterpret_cast(temp.bits()), temp.width(), temp.height(), temp.bytesPerLine(), reinterpret_cast(img.bits()), img.bytesPerLine()); } else { qt_memrotate90_template(reinterpret_cast(temp.bits()), temp.width(), temp.height(), temp.bytesPerLine(), reinterpret_cast(img.bits()), img.bytesPerLine()); } } else { img = temp; } } QImage Image_Processing::qt_halfScaled(const QImage& source) { if (source.width() < 2 || source.height() < 2) return QImage(); QImage srcImage = source; if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) { QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); dest.setDevicePixelRatio(source.devicePixelRatioF()); const uchar* src = reinterpret_cast(const_cast(srcImage).bits()); int sx = srcImage.bytesPerLine(); int sx2 = sx << 1; uchar* dst = reinterpret_cast(dest.bits()); int dx = dest.bytesPerLine(); int ww = dest.width(); int hh = dest.height(); for (int y = hh; y; --y, dst += dx, src += sx2) { const uchar* p1 = src; const uchar* p2 = src + sx; uchar* q = dst; for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2) *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2; } return dest; } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) { QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); dest.setDevicePixelRatio(source.devicePixelRatioF()); const uchar* src = reinterpret_cast(const_cast(srcImage).bits()); int sx = srcImage.bytesPerLine(); int sx2 = sx << 1; uchar* dst = reinterpret_cast(dest.bits()); int dx = dest.bytesPerLine(); int ww = dest.width(); int hh = dest.height(); for (int y = hh; y; --y, dst += dx, src += sx2) { const uchar* p1 = src; const uchar* p2 = src + sx; uchar* q = dst; for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) { q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3])); const quint16 p16_1 = (p1[2] << 8) | p1[1]; const quint16 p16_2 = (p1[5] << 8) | p1[4]; const quint16 p16_3 = (p2[2] << 8) | p2[1]; const quint16 p16_4 = (p2[5] << 8) | p2[4]; const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4)); q[1] = result & 0xff; q[2] = result >> 8; } } return dest; } else if (source.format() != QImage::Format_ARGB32_Premultiplied && source.format() != QImage::Format_RGB32) { srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied); } QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); dest.setDevicePixelRatio(source.devicePixelRatioF()); const quint32* src = reinterpret_cast(const_cast(srcImage).bits()); int sx = srcImage.bytesPerLine() >> 2; int sx2 = sx << 1; quint32* dst = reinterpret_cast(dest.bits()); int dx = dest.bytesPerLine() >> 2; int ww = dest.width(); int hh = dest.height(); for (int y = hh; y; --y, dst += dx, src += sx2) { const quint32* p1 = src; const quint32* p2 = src + sx; quint32* q = dst; for (int x = ww; x; --x, q++, p1 += 2, p2 += 2) *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1])); } return dest; } void Image_Processing::qt_blurImage(QImage& blurImage, qreal radius, bool quality, bool alphaOnly, int transposed) { if (blurImage.format() != QImage::Format_ARGB32_Premultiplied && blurImage.format() != QImage::Format_RGB32) { blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); } qreal scale = 1; if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) { blurImage = qt_halfScaled(blurImage); scale = 2; radius *= qreal(0.5); } if (alphaOnly) expblur<12, 10, true>(blurImage, radius, quality, transposed); else expblur<12, 10, false>(blurImage, radius, quality, transposed); } ================================================ FILE: Anime_Template_Project/Liquid/image_processing.h ================================================ #ifndef IMAGE_PROCESSING_H #define IMAGE_PROCESSING_H #include #include #define IMAGE_PROCESSING Image_Processing::instance() #define AVG(a, b) (((((a) ^ (b)) & 0xfefefefeUL) >> 1) + ((a) & (b))) #define AVG16(a, b) (((((a) ^ (b)) & 0xf7deUL) >> 1) + ((a) & (b))) class Image_Processing : public QObject { Q_OBJECT public: explicit Image_Processing(QObject* parent = nullptr); static Image_Processing* instance(); void qt_blurImage(QImage& blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); template void qt_memrotate270_tiled_unpacked(const T* src, int w, int h, int sstride, T* dest, int dstride); template void qt_memrotate270_template(const T* src, int srcWidth, int srcHeight, int srcStride, T* dest, int dstStride); template void qt_memrotate90_tiled_unpacked(const T* src, int w, int h, int sstride, T* dest, int dstride); template void qt_memrotate90_template(const T* src, int srcWidth, int srcHeight, int srcStride, T* dest, int dstStride); template void qt_blurinner(uchar* bptr, int& zR, int& zG, int& zB, int& zA, int alpha); template void qt_blurinner_alphaOnly(uchar* bptr, int& z, int alpha); template void qt_blurrow(QImage& im, int line, int alpha); template void expblur(QImage& img, qreal radius, bool improvedQuality = false, int transposed = 0); QImage qt_halfScaled(const QImage& source); private: Q_DISABLE_COPY_MOVE(Image_Processing) const int tileSize = 32; }; #endif // IMAGE_PROCESSING_H ================================================ FILE: Anime_Template_Project/Liquid/liquid_button.cpp ================================================ #include "liquid_button.h" Liquid_Button::Liquid_Button(QWidget *parent) : QWidget{parent} { this->setFixedSize(200, 70); m_animation = new QPropertyAnimation(this, "color1"); m_animation->setDuration(400); m_animation->setStartValue(m_color1); m_animation->setEndValue(QColor(255, 0, 0)); m_animation2 = new QPropertyAnimation(this, "textColor"); m_animation2->setDuration(400); m_animation2->setStartValue(m_textColor); m_animation2->setEndValue(QColor(255, 255, 255)); } void Liquid_Button::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(rect(), 20, 20); painter.setClipPath(path); painter.setPen(Qt::NoPen); painter.setBrush(m_color1); painter.drawRoundedRect(rect(), 20, 20); painter.setPen(m_textColor); QFont font; font.setPixelSize(this->height() / 3); painter.setFont(font); painter.drawText(rect(), Qt::AlignCenter, m_text); } bool Liquid_Button::event(QEvent* event) { if (event->type() == QEvent::Enter) { m_animation->setDirection(QAbstractAnimation::Forward); m_animation->start(); m_animation2->setDirection(QAbstractAnimation::Forward); m_animation2->start(); } else if (event->type() == QEvent::Leave) { m_animation->setDirection(QAbstractAnimation::Backward); m_animation->start(); m_animation2->setDirection(QAbstractAnimation::Backward); m_animation2->start(); } return QWidget::event(event); } QColor Liquid_Button::color1() const { return m_color1; } void Liquid_Button::setColor1(const QColor& color1) { m_color1 = color1; update(); } QColor Liquid_Button::textColor() const { return m_textColor; } void Liquid_Button::setTextColor(const QColor& textColor) { m_textColor = textColor; update(); } ================================================ FILE: Anime_Template_Project/Liquid/liquid_button.h ================================================ #ifndef LIQUID_BUTTON_H #define LIQUID_BUTTON_H #include #include #include #include #include class Liquid_Button : public QWidget { Q_OBJECT Q_PROPERTY(QColor color1 READ color1 WRITE setColor1) Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) public: explicit Liquid_Button(QWidget *parent = nullptr); QPropertyAnimation *m_animation; QPropertyAnimation *m_animation2; QColor color1() const; void setColor1(const QColor &color1); QColor textColor() const; void setTextColor(const QColor &textColor); protected: void paintEvent(QPaintEvent *event) override; bool event(QEvent *event) override; signals: private: QColor m_color1 = QColor(0, 0, 0); QColor m_textColor = QColor(255, 0, 0); QString m_text = "液 体"; }; #endif // LIQUID_BUTTON_H ================================================ FILE: Anime_Template_Project/Liquid/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { this->resize(655, 655); button = new Liquid_Button(this); button->show(); timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &Widget::updateCircles); timer->start(16); } Widget::~Widget() {} void Widget::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing); painter.setBrush(QColor(0, 0, 0, 255)); painter.setPen(Qt::NoPen); painter.drawRect(0, 0, width(), height()); QImage blur_image(this->size(), QImage::Format_ARGB32); blur_image.fill(QColor(0, 0, 0, 0)); QPainter image_painter(&blur_image); image_painter.setRenderHints(QPainter::Antialiasing); image_painter.setPen(Qt::NoPen); image_painter.setBrush(QColor(255, 0, 0, 255)); image_painter.drawRoundedRect(button->pos().x() + 10, button->pos().y() + 10, button->width() - 20, button->height() - 20, 10, 10); for (const auto& circle : circles) { QColor currentColor = circle.color; currentColor.setAlphaF(circle.alpha); image_painter.setBrush(currentColor); image_painter.drawEllipse(circle.position.x() - 20, circle.position.y() - 20, 40, 40); } image_painter.end(); IMAGE_PROCESSING->qt_blurImage(blur_image, 111, true, false); this->processImageTransparency(blur_image, 3, 147); this->process(blur_image, 4); painter.drawImage(this->rect(), blur_image); } void Widget::resizeEvent(QResizeEvent* event) { button->move(this->width() / 2 - button->width() / 2, this->height() / 2 - button->height() / 2); } void Widget::updateCircles() { static int counter = 0; if (counter % 4 == 0) { Circle newCircle; newCircle.position = QPointF(width() / 2, height() / 2); newCircle.alpha = 1.01; qreal angle = QRandomGenerator::global()->bounded(360); newCircle.direction = QPointF(qCos(qDegreesToRadians(angle)), qSin(qDegreesToRadians(angle))); circles.append(newCircle); } for (auto it = circles.begin(); it != circles.end(); ) { it->position += it->direction * 2.7; it->alpha -= 0.007; if (it->alpha <= 0 || it->position.x() < -20 || it->position.x() > width() + 20 || it->position.y() < -20 || it->position.y() > height() + 20) { it = circles.erase(it); } else { ++it; } } update(); counter++; } void Widget::processImageTransparency(QImage& image, int alphaMultiplier, int threshold) { if (image.isNull()) { return; } if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } const int width = image.width(); const int height = image.height(); const int size = width * height; for (int i = 0; i < size; i++) { QRgb* pixel = reinterpret_cast(image.bits()) + i; int r = qRed(*pixel) * alphaMultiplier; int g = qGreen(*pixel) * alphaMultiplier; int b = qBlue(*pixel) * alphaMultiplier; int a = qAlpha(*pixel) * alphaMultiplier; r = qBound(0, r, 255); g = qBound(0, g, 255); b = qBound(0, b, 255); a = qBound(0, a, 255); *pixel = qRgba(r, g, b, a); } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { QRgb* pixel = reinterpret_cast(image.scanLine(y)) + x; int r = qRed(*pixel); int g = qGreen(*pixel); int b = qBlue(*pixel); int a = qAlpha(*pixel); auto softThreshold = [threshold](int value) { if (value <= threshold * 0.7) return 0; if (value >= threshold * 1.3) return value; return static_cast((value - threshold * 0.7) * (255 / (0.8 * threshold))); }; r = softThreshold(r); g = softThreshold(g); b = softThreshold(b); a = softThreshold(a); *pixel = qRgba(r, g, b, a); } } } void Widget::process(QImage& image, int alphaMultiplier) { uint8_t* rgb = image.bits(); if (nullptr == rgb) { return; } int width = image.width(); int height = image.height(); int size = width * height; for (int i = 0; i < size; i++) { int r = rgb[i * 4 + 0] * alphaMultiplier; int g = rgb[i * 4 + 1] * alphaMultiplier; int b = rgb[i * 4 + 2] * alphaMultiplier; int a = rgb[i * 4 + 3] * alphaMultiplier; r = qBound(0, r, 255); g = qBound(0, g, 255); b = qBound(0, b, 255); a = qBound(0, a, 255); rgb[i * 4 + 0] = r; rgb[i * 4 + 1] = g; rgb[i * 4 + 2] = b; rgb[i * 4 + 3] = a; } } ================================================ FILE: Anime_Template_Project/Liquid/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include #include #include "image_processing.h" #include #include #include #include #include #include #include "liquid_button.h" struct Circle { QPointF position; QColor color = QColor(255, 0, 0, 255); qreal alpha; QPointF direction; }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); QPropertyAnimation *animation; QPropertyAnimation *animation2; QPoint startPoint = QPoint(295, 200); QPoint endPoint = QPoint(400, 200); void process(QImage &image, int alphaMultiplier); void processImageTransparency(QImage &image, int alphaMultiplier, int threshold); private slots: void updateCircles(); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: QTimer *timer; QVector circles; Liquid_Button *button; }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/LoadingAnime/Bouncing_Ball_Loading.h ================================================ #ifndef BOUNCING_BALL_LOADING_H #define BOUNCING_BALL_LOADING_H #include #include #include #include #include #include #include #include class Sphere : public QObject { Q_OBJECT Q_PROPERTY(QPointF position READ position WRITE setPosition NOTIFY positionChanged) Q_PROPERTY(int widthRadius READ widthRadius WRITE setWidthRadius NOTIFY widthRadiusChanged) Q_PROPERTY(int heightRadius READ heightRadius WRITE setHeightRadius NOTIFY heightRadiusChanged) Q_PROPERTY(QPointF fallEndPosition READ fallEndPosition WRITE setFallEndPosition NOTIFY fallEndPositionChanged) public: explicit Sphere(QObject *parent = nullptr); void startAnimation(); void configureAnimation(); void setStartAndEndPosition(QPointF startPosition, QPointF endPosition); public: QPointF position() const; void setPosition(QPointF newPosition); int widthRadius() const; void setWidthRadius(int newWidthRadius); int heightRadius() const; void setHeightRadius(int newHeightRadius); QPointF fallEndPosition() const; void setFallEndPosition(QPointF newFallEndPosition); QColor ballColor() const { return m_ballColor; } void setBallColor(const QColor &color) { m_ballColor = color; } bool animationFinished() const { return m_animationFinished; } void setAnimationFinished(bool finished) { m_animationFinished = finished; } signals: void positionChanged(); void widthRadiusChanged(); void heightRadiusChanged(); void fallEndPositionChanged(); private: QPointF m_position; int m_widthRadius{30}; int m_heightRadius{30}; QPointF m_fallEndPosition; QColor m_ballColor = QColor(255,64,91,255); QPropertyAnimation *animation; QPropertyAnimation *animation2; QPropertyAnimation *animation3; QPropertyAnimation *animation4; bool m_animationFinished = true; }; class Bouncing_Ball_Loading : public QWidget { Q_OBJECT public: explicit Bouncing_Ball_Loading(QWidget *parent = nullptr); void updateBallPositions(int ballCount); void setSize(QSize size); protected: void paintEvent(QPaintEvent *event) override; private slots: private: int m_ballCount = 3; std::vector m_balls; QTimer *m_timer; int m_timerCount = 0; int m_currentTime = 240; QTimer *m_updateTimer; }; #endif // BOUNCING_BALL_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.19) project(LoadingAnime LANGUAGES CXX) find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets) find_package(Qt6 REQUIRED COMPONENTS Widgets) find_package(Qt6 REQUIRED COMPONENTS Widgets) find_package(Qt6 REQUIRED COMPONENTS Widgets) qt_standard_project_setup() qt_add_executable(LoadingAnime # WIN32 MACOSX_BUNDLE main.cpp widget.cpp widget.h ring_loading.h ring_loading.cpp bouncing_ball_loading.h bouncing_ball_loading.cpp rectangle_loading.h rectangle_loading.cpp line_rotating_along_rectangular_border.h line_rotating_along_rectangular_border.cpp Square_Pseudo_Bounce_Loading.h Square_Pseudo_Bounce_Loading.cpp regular_loading.h regular_loading.cpp line_loading.h line_loading.cpp black_white_ball_loading.h black_white_ball_loading.cpp hexagonloaderwidget.h hexagonloaderwidget.cpp ) target_link_libraries(LoadingAnime PRIVATE Qt::Core Qt::Widgets ) target_link_libraries(LoadingAnime PRIVATE Qt6::Widgets) target_link_libraries(LoadingAnime PRIVATE Qt6::Widgets) target_link_libraries(LoadingAnime PRIVATE Qt6::Widgets) include(GNUInstallDirs) install(TARGETS LoadingAnime BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) qt_generate_deploy_app_script( TARGET LoadingAnime OUTPUT_SCRIPT deploy_script NO_UNSUPPORTED_PLATFORM_ERROR ) install(SCRIPT ${deploy_script}) ================================================ FILE: Anime_Template_Project/LoadingAnime/Rectangle_Loading.h ================================================ #ifndef RECTANGLE_LOADING_H #define RECTANGLE_LOADING_H #include #include #include #include class Rectangle_Loading : public QWidget { Q_OBJECT Q_PROPERTY(qreal rotationAngle READ rotationAngle WRITE setRotationAngle) Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth) public: explicit Rectangle_Loading(QWidget *parent = nullptr); ~Rectangle_Loading(); qreal rotationAngle() const { return m_rotationAngle; } void setRotationAngle(qreal angle); int borderWidth() const { return m_borderWidth; } void setBorderWidth(int width); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void updateLoadingDots(); private: qreal m_rotationAngle; int m_borderWidth; QPropertyAnimation *m_rotationAnimation; QPropertyAnimation *m_borderAnimation; QTimer *m_dotTimer; int m_currentDotCount; QColor m_drawColor; void setupAnimations(); }; #endif // RECTANGLE_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/Square_Pseudo_Bounce_Loading.cpp ================================================ #include "Square_Pseudo_Bounce_Loading.h" #include Square_Pseudo_Bounce_Loading::Square_Pseudo_Bounce_Loading(QWidget *parent) : QWidget{parent}, m_squareSize(0), m_initialRotation(45.0), m_offsetY(0), m_currentRotation(0), m_offsetAnimation1(nullptr), m_offsetAnimation2(nullptr), m_bounceGroup(nullptr), m_rotationAnimation(nullptr), m_mainAnimation(nullptr) { setAttribute(Qt::WA_TranslucentBackground); QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setupAnimations(); } Square_Pseudo_Bounce_Loading::~Square_Pseudo_Bounce_Loading() { if (m_mainAnimation) { m_mainAnimation->stop(); delete m_mainAnimation; } } void Square_Pseudo_Bounce_Loading::setOffsetY(qreal offset) { if (m_offsetY == offset) return; m_offsetY = offset; update(); } void Square_Pseudo_Bounce_Loading::setCurrentRotation(qreal rotation) { if (m_currentRotation == rotation) return; m_currentRotation = rotation; update(); } void Square_Pseudo_Bounce_Loading::setupAnimations() { m_offsetAnimation1 = new QPropertyAnimation(this, "offsetY"); m_offsetAnimation1->setStartValue(0.0); m_offsetAnimation1->setEndValue(-bounceHeight); m_offsetAnimation1->setDuration(350); m_offsetAnimation1->setEasingCurve(QEasingCurve::OutQuad); m_offsetAnimation2 = new QPropertyAnimation(this, "offsetY"); m_offsetAnimation2->setStartValue(-bounceHeight); m_offsetAnimation2->setEndValue(0.0); m_offsetAnimation2->setDuration(350); m_offsetAnimation2->setEasingCurve(QEasingCurve::InQuad); m_bounceGroup = new QSequentialAnimationGroup(this); m_bounceGroup->addAnimation(m_offsetAnimation1); m_bounceGroup->addAnimation(m_offsetAnimation2); m_bounceGroup->setLoopCount(-1); m_rotationAnimation = new QPropertyAnimation(this, "currentRotation"); m_rotationAnimation->setStartValue(0.0); m_rotationAnimation->setEndValue(360.0); m_rotationAnimation->setDuration(2800); m_rotationAnimation->setLoopCount(-1); m_rotationAnimation->setEasingCurve(QEasingCurve::Linear); m_mainAnimation = new QParallelAnimationGroup(this); m_mainAnimation->addAnimation(m_bounceGroup); m_mainAnimation->addAnimation(m_rotationAnimation); m_mainAnimation->setLoopCount(-1); m_mainAnimation->start(); } void Square_Pseudo_Bounce_Loading::updateSquareGeometry() { qreal minDim = qMin(width(), height()); m_squareSize = minDim / 7.0; update(); } void Square_Pseudo_Bounce_Loading::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(96, 151, 226, 255)); painter.setPen(Qt::NoPen); painter.drawRect(rect()); QImage blur_image1(this->size(), QImage::Format_ARGB32); blur_image1.fill(QColor(0, 0, 0, 0)); QPainter image_painter1(&blur_image1); image_painter1.setRenderHint(QPainter::Antialiasing); image_painter1.setPen(Qt::NoPen); image_painter1.setBrush(QColor(117, 117, 117, 133)); image_painter1.drawEllipse(width() / 2.0 -m_squareSize/2,height() / 2.0 + m_squareSize * 0.36,m_squareSize,4); image_painter1.end(); painter.drawImage(QRect(0, 0, width(), height()),blur_image1); painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); qreal centerX = width() / 2.0; qreal centerY = height() / 2.0 + m_offsetY; QPainterPath path; path.addEllipse(width() / 2.0 - height() / 4.3, 0 + m_squareSize / 4.0 + -m_offsetY * 0.8, height() / 2.0, height() / 2.0); painter.setClipPath(path); painter.translate(centerX, centerY); painter.rotate(m_initialRotation + m_currentRotation); painter.drawRoundedRect(QRectF(-m_squareSize / 2.0, -m_squareSize / 2.0, m_squareSize, m_squareSize),m_squareSize / 10,m_squareSize / 10); painter.restore(); } void Square_Pseudo_Bounce_Loading::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); updateSquareGeometry(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/Square_Pseudo_Bounce_Loading.h ================================================ #ifndef SQUARE_PSEUDO_BOUNCE_LOADING_H #define SQUARE_PSEUDO_BOUNCE_LOADING_H #include #include #include #include #include #include #include class Square_Pseudo_Bounce_Loading : public QWidget { Q_OBJECT Q_PROPERTY(qreal offsetY READ offsetY WRITE setOffsetY) Q_PROPERTY(qreal currentRotation READ currentRotation WRITE setCurrentRotation) public: explicit Square_Pseudo_Bounce_Loading(QWidget *parent = nullptr); ~Square_Pseudo_Bounce_Loading(); qreal offsetY() const { return m_offsetY; } qreal currentRotation() const { return m_currentRotation; } void setOffsetY(qreal offset); void setCurrentRotation(qreal rotation); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: qreal m_squareSize; qreal m_initialRotation; qreal m_offsetY; qreal m_currentRotation; qreal bounceHeight{20}; QPropertyAnimation *m_offsetAnimation1; QPropertyAnimation *m_offsetAnimation2; QSequentialAnimationGroup *m_bounceGroup; QPropertyAnimation *m_rotationAnimation; QParallelAnimationGroup *m_mainAnimation; void setupAnimations(); void updateSquareGeometry(); }; #endif // SQUARE_PSEUDO_BOUNCE_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/black_white_ball_loading.cpp ================================================ #include "black_white_ball_loading.h" #include #include #include #include Black_White_Ball_Loading::Black_White_Ball_Loading(QWidget *parent) : QWidget{parent} , m_timer(new QTimer(this)) , m_ballPairCount(1) , m_startPosRatio(0.6) , m_endPosRatio(0.8) , m_ballRadiusRatio(0.03) , m_animationStep(0.02) , m_minSide(0) , m_groupCount(1) { connect(m_timer, &QTimer::timeout, this, &Black_White_Ball_Loading::updateAnimation); m_timer->start(16); updateAngles(); initializeGroupData(); resize(200, 200); } Black_White_Ball_Loading::~Black_White_Ball_Loading() { if (m_timer->isActive()) { m_timer->stop(); } } void Black_White_Ball_Loading::setBallPairCount(int count) { if (count > 0 && count != m_ballPairCount) { m_ballPairCount = count; updateAngles(); initializeGroupData(); update(); } } void Black_White_Ball_Loading::setAnimationRatios(qreal startRatio, qreal endRatio) { if (startRatio >= 0.0 && startRatio < endRatio && endRatio <= 1.0) { m_startPosRatio = startRatio; m_endPosRatio = endRatio; update(); } else { qWarning() << "动画比例无效。起始比例必须小于结束比例,且两者均在0到1之间。"; } } void Black_White_Ball_Loading::setBallRadiusRatio(qreal ratio) { if (ratio > 0.0 && ratio < 0.2) { m_ballRadiusRatio = ratio; update(); } else { qWarning() << "球体半径比例无效。比例必须大于0且小于0.2。"; } } void Black_White_Ball_Loading::setAnimationSpeed(qreal speed) { if (speed > 0.0 && speed <= 0.1) { m_animationStep = speed; } else { qWarning() << "动画速度无效。速度必须在0到0.1之间。"; } } void Black_White_Ball_Loading::setGroupCount(int count) { if (count > 0 && count != m_groupCount) { m_groupCount = count; initializeGroupData(); update(); } else if (count <= 0) { qWarning() << "分组数量必须大于0。"; } } void Black_White_Ball_Loading::initializeGroupData() { m_groupProgresses.clear(); m_groupForwards.clear(); m_groupDrawWhiteFirst.clear(); for (int i = 0; i < m_groupCount; ++i) { m_groupProgresses.append(static_cast(i) / m_groupCount); m_groupForwards.append(true); m_groupDrawWhiteFirst.append(true); } } void Black_White_Ball_Loading::updateAngles() { m_angles.clear(); if (m_ballPairCount > 0) { qreal angleStep = 360.0 / m_ballPairCount; for (int i = 0; i < m_ballPairCount; ++i) { m_angles.append(qDegreesToRadians(i * angleStep)); } } } void Black_White_Ball_Loading::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); m_center = rect().center(); m_minSide = qMin(width(), height()); update(); } QPointF Black_White_Ball_Loading::getPointOnLine(qreal progress, qreal distance, qreal angle) { qreal actualLineLength = m_minSide * (m_endPosRatio - m_startPosRatio); qreal startDistance = m_minSide * m_startPosRatio; qreal currentDistance = startDistance + (progress * actualLineLength); qreal dx = currentDistance * qCos(angle); qreal dy = currentDistance * qSin(angle); return QPointF(m_center.x() + dx, m_center.y() + dy); } void Black_White_Ball_Loading::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor("#34495E")); painter.setPen(Qt::NoPen); painter.drawRect(rect()); qreal ballRadius = m_minSide * m_ballRadiusRatio; for (int i = 0; i < m_ballPairCount; ++i) { qreal currentAngle = m_angles.at(i); int groupIndex = i % m_groupCount; qreal currentGroupProgress = m_groupProgresses.at(groupIndex); bool currentGroupDrawWhiteFirst = m_groupDrawWhiteFirst.at(groupIndex); QPointF whiteBallPos = getPointOnLine(currentGroupProgress, 0, currentAngle); QPointF blackBallPos = getPointOnLine(1.0 - currentGroupProgress, 0, currentAngle); if (currentGroupDrawWhiteFirst) { painter.setBrush(Qt::white); painter.setPen(Qt::NoPen); painter.drawEllipse(whiteBallPos, ballRadius, ballRadius); painter.setBrush(Qt::black); painter.setPen(Qt::NoPen); painter.drawEllipse(blackBallPos, ballRadius, ballRadius); } else { painter.setBrush(Qt::black); painter.setPen(Qt::NoPen); painter.drawEllipse(blackBallPos, ballRadius, ballRadius); painter.setBrush(Qt::white); painter.setPen(Qt::NoPen); painter.drawEllipse(whiteBallPos, ballRadius, ballRadius); } } } void Black_White_Ball_Loading::updateAnimation() { for (int i = 0; i < m_groupCount; ++i) { if (m_groupForwards[i]) { m_groupProgresses[i] += m_animationStep; if (m_groupProgresses[i] >= 1.0) { m_groupProgresses[i] = 1.0; m_groupForwards[i] = false; } m_groupDrawWhiteFirst[i] = true; } else { m_groupProgresses[i] -= m_animationStep; if (m_groupProgresses[i] <= 0.0) { m_groupProgresses[i] = 0.0; m_groupForwards[i] = true; } m_groupDrawWhiteFirst[i] = false; } } update(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/black_white_ball_loading.h ================================================ #ifndef BLACK_WHITE_BALL_LOADING_H #define BLACK_WHITE_BALL_LOADING_H #include #include #include #include class Black_White_Ball_Loading : public QWidget { Q_OBJECT public: explicit Black_White_Ball_Loading(QWidget *parent = nullptr); ~Black_White_Ball_Loading(); void setBallPairCount(int count); void setAnimationRatios(qreal startRatio, qreal endRatio); void setBallRadiusRatio(qreal ratio); void setAnimationSpeed(qreal speed); void setGroupCount(int count); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void updateAnimation(); private: QTimer *m_timer; int m_ballPairCount; qreal m_startPosRatio; qreal m_endPosRatio; qreal m_ballRadiusRatio; qreal m_animationStep; QPointF m_center; qreal m_minSide; QVector m_angles; int m_groupCount; QVector m_groupProgresses; QVector m_groupForwards; QVector m_groupDrawWhiteFirst; QPointF getPointOnLine(qreal progress, qreal distance, qreal angle); void updateAngles(); void initializeGroupData(); }; #endif // BLACK_WHITE_BALL_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/bouncing_ball_loading.cpp ================================================ #include "Bouncing_Ball_Loading.h" #include #include #include Bouncing_Ball_Loading::Bouncing_Ball_Loading(QWidget *parent) : QWidget(parent) { this->updateBallPositions(m_ballCount); m_timer = new QTimer(this); m_updateTimer = new QTimer(this); connect(m_updateTimer, &QTimer::timeout, this, [=] { update(); }); m_updateTimer->setTimerType(Qt::PreciseTimer); m_updateTimer->start(15); connect(m_timer, &QTimer::timeout, this, [=] { if (m_balls.size() - 1 < m_timerCount) { m_timer->stop(); m_timerCount = 0; return; } qDebug() << "当前球号:" << m_timerCount; m_balls[m_timerCount]->startAnimation(); m_timerCount++; }); m_timer->setTimerType(Qt::PreciseTimer); m_timer->start(200); } void Bouncing_Ball_Loading::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(239, 239, 239, 255)); painter.setPen(Qt::NoPen); painter.drawRect(rect()); for (auto sphere : m_balls) { painter.setBrush(QColor(117,117,117,106)); qreal endshadowY = sphere->fallEndPosition().ry(); qreal positionY = sphere->position().ry(); qreal shadowWidth = (positionY / endshadowY) * 30; painter.drawEllipse(sphere->fallEndPosition() + QPointF(0.0,25.0), 2 + shadowWidth, 5); painter.setBrush(sphere->ballColor()); painter.setPen(Qt::NoPen); painter.drawEllipse(sphere->position(), sphere->widthRadius(), sphere->heightRadius()); } } void Bouncing_Ball_Loading::updateBallPositions(int ballCount) { if (!m_balls.empty()) { for (Sphere *sphere : m_balls) { delete sphere; } m_balls.clear(); } if (ballCount <= 0) { qDebug() << "球的数量小于等于0"; return; } int _x = width() / (ballCount + 3); int _y = height() / 5; for (int i = 1; i < ballCount + 1; ++i) { Sphere *sphere1 = new Sphere(this); sphere1->setStartAndEndPosition(QPointF(_x * (i * 2 -1) , _y), QPointF(_x * (i * 2 -1), height() - _y)); sphere1->configureAnimation(); m_balls.push_back(sphere1); }; } void Bouncing_Ball_Loading::setSize(QSize size) { this->resize(size); this->updateBallPositions(m_ballCount); m_timer->start(200); } Sphere::Sphere(QObject *parent) : QObject(parent) { } void Sphere::startAnimation() { m_animationFinished = false; animation->start(); } void Sphere::configureAnimation() { QPointF L_position = m_position; QPointF L_fallEndPosition = m_fallEndPosition; animation = new QPropertyAnimation(this, "position"); animation->setDuration(400); animation->setStartValue(L_position); animation->setEndValue(L_fallEndPosition); animation->setEasingCurve(QEasingCurve::Linear); connect(animation, &QPropertyAnimation::finished, this, [=] { animation2 = new QPropertyAnimation(this, "widthRadius"); animation2->setDuration(100); animation2->setKeyValueAt(0.0,30); animation2->setKeyValueAt(0.4,40); animation2->setKeyValueAt(0.6,40); animation2->setKeyValueAt(1.0,30); animation2->setEasingCurve(QEasingCurve::Linear); animation3 = new QPropertyAnimation(this, "heightRadius"); animation3->setDuration(100); animation3->setKeyValueAt(0.0,30); animation3->setKeyValueAt(0.4,10); animation3->setKeyValueAt(0.6,10); animation3->setKeyValueAt(1.0,30); animation3->setEasingCurve(QEasingCurve::Linear); animation2->start(); animation3->start(); connect(animation2, &QPropertyAnimation::finished, this, [=] { animation4 = new QPropertyAnimation(this, "position"); animation4->setDuration(400); animation4->setStartValue(L_fallEndPosition); animation4->setEndValue(L_position); animation4->setEasingCurve(QEasingCurve::Linear); animation4->start(); connect(animation4, &QPropertyAnimation::finished, this, [=] { startAnimation(); m_animationFinished = true; }); }); }); } void Sphere::setStartAndEndPosition(QPointF startPosition, QPointF endPosition) { if (m_position == startPosition && m_fallEndPosition == endPosition) return; m_position = startPosition; m_fallEndPosition = endPosition; emit positionChanged(); emit fallEndPositionChanged(); } QPointF Sphere::position() const { return m_position; } void Sphere::setPosition(QPointF newPosition) { if (m_position == newPosition) return; m_position = newPosition; emit positionChanged(); } int Sphere::widthRadius() const { return m_widthRadius; } void Sphere::setWidthRadius(int newWidthRadius) { if (m_widthRadius == newWidthRadius) return; m_widthRadius = newWidthRadius; emit widthRadiusChanged(); } int Sphere::heightRadius() const { return m_heightRadius; } void Sphere::setHeightRadius(int newHeightRadius) { if (m_heightRadius == newHeightRadius) return; m_heightRadius = newHeightRadius; emit heightRadiusChanged(); } QPointF Sphere::fallEndPosition() const { return m_fallEndPosition; } void Sphere::setFallEndPosition(QPointF newFallEndPosition) { if (m_fallEndPosition == newFallEndPosition) return; m_fallEndPosition = newFallEndPosition; emit fallEndPositionChanged(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/hexagonloaderwidget.cpp ================================================ #include "HexagonLoaderWidget.h" #include #include #include #include #include HexBrick::HexBrick(QColor color, qreal rotation, QObject* parent) : QObject(parent), m_color(color), m_rotation(rotation) { } void HexBrick::setColor(const QColor& color) { if (m_color != color) { m_color = color; emit colorChanged(); } } Gel::Gel(const QPointF& offset, qreal delay) : m_offset(offset), m_scale(1.0), m_animationDelay(delay) { m_hexBricks.push_back(new HexBrick(Qt::black, 0.0, nullptr)); m_hexBricks.push_back(new HexBrick(Qt::black, 60.0, nullptr)); m_hexBricks.push_back(new HexBrick(Qt::black, -60.0, nullptr)); } HexagonLoaderWidget::HexagonLoaderWidget(QWidget *parent) : QWidget(parent), m_centerGelCurrentScale(1.0), m_r1CurrentScale(1.0), m_r2CurrentScale(1.0), m_r3CurrentScale(1.0), m_currentGlobalScale(1.0) { setupGels(); setupAnimations(); } HexagonLoaderWidget::~HexagonLoaderWidget() { qDeleteAll(m_pulseAnimations); qDeleteAll(m_fadeAnimations); for (Gel& gel : m_gels) { qDeleteAll(gel.hexBricks()); gel.hexBricks().clear(); } } qreal HexagonLoaderWidget::centerGelScale() const { return m_centerGelCurrentScale; } void HexagonLoaderWidget::setCenterGelScale(qreal scale) { if (!qFuzzyCompare(m_centerGelCurrentScale, scale)) { m_centerGelCurrentScale = scale; if (!m_gels.empty()) { m_gels[0].setScale(scale); } emit centerGelScaleChanged(); update(); } } qreal HexagonLoaderWidget::r1Scale() const { return m_r1CurrentScale; } void HexagonLoaderWidget::setR1Scale(qreal scale) { if (!qFuzzyCompare(m_r1CurrentScale, scale)) { m_r1CurrentScale = scale; for (int i = 1; i <= 6; ++i) { m_gels[i].setScale(scale); } emit r1ScaleChanged(); update(); } } qreal HexagonLoaderWidget::r2Scale() const { return m_r2CurrentScale; } void HexagonLoaderWidget::setR2Scale(qreal scale) { if (!qFuzzyCompare(m_r2CurrentScale, scale)) { m_r2CurrentScale = scale; for (int i = 7; i <= 18; ++i) { m_gels[i].setScale(scale); } emit r2ScaleChanged(); update(); } } qreal HexagonLoaderWidget::r3Scale() const { return m_r3CurrentScale; } void HexagonLoaderWidget::setR3Scale(qreal scale) { if (!qFuzzyCompare(m_r3CurrentScale, scale)) { m_r3CurrentScale = scale; for (int i = 19; i < m_gels.size(); ++i) { m_gels[i].setScale(scale); } emit r3ScaleChanged(); update(); } } void HexagonLoaderWidget::setupGels() { m_gels.push_back(Gel(QPointF(0, 0), 0.0)); m_gels.push_back(Gel(QPointF(-32, 0), 0.2)); m_gels.push_back(Gel(QPointF(-16, -28), 0.2)); m_gels.push_back(Gel(QPointF(16, -28), 0.2)); m_gels.push_back(Gel(QPointF(32, 0), 0.2)); m_gels.push_back(Gel(QPointF(-16, 28), 0.2)); m_gels.push_back(Gel(QPointF(16, 28), 0.2)); m_gels.push_back(Gel(QPointF(-48, -28), 0.4)); m_gels.push_back(Gel(QPointF(48, -28), 0.4)); m_gels.push_back(Gel(QPointF(0, 56), 0.4)); m_gels.push_back(Gel(QPointF(-48, 28), 0.4)); m_gels.push_back(Gel(QPointF(48, 28), 0.4)); m_gels.push_back(Gel(QPointF(0, -56), 0.4)); m_gels.push_back(Gel(QPointF(-32, -56), 0.4)); m_gels.push_back(Gel(QPointF(32, -56), 0.4)); m_gels.push_back(Gel(QPointF(-32, 56), 0.4)); m_gels.push_back(Gel(QPointF(32, 56), 0.4)); m_gels.push_back(Gel(QPointF(-64, 0), 0.4)); m_gels.push_back(Gel(QPointF(64, 0), 0.4)); m_gels.push_back(Gel(QPointF(-48, -84), 0.6)); m_gels.push_back(Gel(QPointF(48, -84), 0.6)); m_gels.push_back(Gel(QPointF(16, -84), 0.6)); m_gels.push_back(Gel(QPointF(-16, -84), 0.6)); m_gels.push_back(Gel(QPointF(-48, 84), 0.6)); m_gels.push_back(Gel(QPointF(-48, 84), 0.6)); m_gels.push_back(Gel(QPointF(48, 84), 0.6)); m_gels.push_back(Gel(QPointF(16, 84), 0.6)); m_gels.push_back(Gel(QPointF(-16, 84), 0.6)); m_gels.push_back(Gel(QPointF(-80, -28), 0.6)); m_gels.push_back(Gel(QPointF(-80, 28), 0.6)); m_gels.push_back(Gel(QPointF(64, 56), 0.6)); m_gels.push_back(Gel(QPointF(-64, -56), 0.6)); m_gels.push_back(Gel(QPointF(-96, 0), 0.6)); m_gels.push_back(Gel(QPointF(80, -28), 0.6)); m_gels.push_back(Gel(QPointF(80, 28), 0.6)); m_gels.push_back(Gel(QPointF(-64, 56), 0.6)); m_gels.push_back(Gel(QPointF(64, -56), 0.6)); m_gels.push_back(Gel(QPointF(96, 0), 0.6)); } void HexagonLoaderWidget::setupAnimations() { QPropertyAnimation* centerPulse = new QPropertyAnimation(this, "centerGelScale", this); centerPulse->setDuration(2000); centerPulse->setLoopCount(-1); centerPulse->setKeyValueAt(0, 1.0); centerPulse->setKeyValueAt(0.5, 0.01); centerPulse->setKeyValueAt(1, 1.0); m_pulseAnimations.append(centerPulse); QPropertyAnimation* r1Pulse = new QPropertyAnimation(this, "r1Scale", this); r1Pulse->setDuration(2000); r1Pulse->setLoopCount(-1); r1Pulse->setKeyValueAt(0, 1.0); r1Pulse->setKeyValueAt(0.5, 0.01); r1Pulse->setKeyValueAt(1, 1.0); m_pulseAnimations.append(r1Pulse); QPropertyAnimation* r2Pulse = new QPropertyAnimation(this, "r2Scale", this); r2Pulse->setDuration(2000); r2Pulse->setLoopCount(-1); r2Pulse->setKeyValueAt(0, 1.0); r2Pulse->setKeyValueAt(0.5, 0.01); r2Pulse->setKeyValueAt(1, 1.0); m_pulseAnimations.append(r2Pulse); QPropertyAnimation* r3Pulse = new QPropertyAnimation(this, "r3Scale", this); r3Pulse->setDuration(2000); r3Pulse->setLoopCount(-1); r3Pulse->setKeyValueAt(0, 1.0); r3Pulse->setKeyValueAt(0.5, 0.01); r3Pulse->setKeyValueAt(1, 1.0); m_pulseAnimations.append(r3Pulse); for (Gel& gel : m_gels) { for (HexBrick* brick : gel.hexBricks()) { QPropertyAnimation* brickFade = new QPropertyAnimation(brick, "color", this); brickFade->setDuration(2000); brickFade->setLoopCount(-1); brickFade->setKeyValueAt(0, QColor("#252525")); brickFade->setKeyValueAt(0.5, QColor("#000000")); brickFade->setKeyValueAt(1, QColor("#353535")); m_fadeAnimations.append(brickFade); } } QTimer::singleShot(0, this, [centerPulse]() { centerPulse->start(); }); QTimer::singleShot(200, this, [r1Pulse]() { r1Pulse->start(); }); QTimer::singleShot(400, this, [r2Pulse]() { r2Pulse->start(); }); QTimer::singleShot(600, this, [r3Pulse]() { r3Pulse->start(); }); int fadeAnimIndex = 0; for (const Gel& gel : m_gels) { for (int j = 0; j < gel.hexBricks().size(); ++j) { if (fadeAnimIndex < m_fadeAnimations.size()) { QPropertyAnimation* brickFade = m_fadeAnimations[fadeAnimIndex]; QTimer::singleShot(static_cast(gel.animationDelay() * 1000), this, [brickFade]() { brickFade->start(); }); fadeAnimIndex++; } } } } void HexagonLoaderWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(rect(), QColor("#282A50")); painter.translate(width() / 2, height() / 2); painter.scale(m_currentGlobalScale, m_currentGlobalScale); const qreal HEX_BRICK_WIDTH = 30.0; const qreal HEX_BRICK_HEIGHT = 17.0; for (const Gel& gel : m_gels) { painter.save(); painter.translate(gel.offset()); painter.scale(gel.scale(), gel.scale()); for (const HexBrick* brick : gel.hexBricks()) { drawHexagon(painter, QPointF(0, 0), HEX_BRICK_WIDTH / 2.0, brick->color(), brick->rotation()); } painter.restore(); } } void HexagonLoaderWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); int newWidth = width(); int newHeight = height(); const qreal originalDesignDimension = 200.0; qreal scaleFactor = qMin(newWidth, newHeight) / originalDesignDimension; m_currentGlobalScale = qMax(0.5, qMin(scaleFactor, 2.0)); update(); QWidget::resizeEvent(event); } QPointF HexagonLoaderWidget::calculateHexagonPoint(qreal size, qreal angleDegrees) { qreal angleRad = qDegreesToRadians(angleDegrees); return QPointF(size * qCos(angleRad), size * qSin(angleRad)); } void HexagonLoaderWidget::drawHexagon(QPainter& painter, const QPointF& center, qreal size, const QColor& color, qreal rotation) { qreal rectWidth = 30.0; qreal rectHeight = 17.0; painter.save(); painter.translate(center); painter.rotate(rotation); QPointF brickCenterOffset(0, -1.5); painter.translate(brickCenterOffset); painter.setBrush(color); painter.setPen(Qt::NoPen); painter.drawRect(-rectWidth / 2, -rectHeight / 2, rectWidth, rectHeight); painter.restore(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/hexagonloaderwidget.h ================================================ #ifndef HEXAGONWIDGET_H #define HEXAGONWIDGET_H #include #include #include #include #include #include #include #include class HexBrick : public QObject { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) public: explicit HexBrick(QColor color = Qt::black, qreal rotation = 0.0, QObject* parent = nullptr); QColor color() const { return m_color; } void setColor(const QColor& color); qreal rotation() const { return m_rotation; } void setRotation(qreal rotation) { m_rotation = rotation; } signals: void colorChanged(); private: QColor m_color; qreal m_rotation; }; class Gel { public: Gel(const QPointF& offset, qreal delay = 0.0); QPointF offset() const { return m_offset; } void setOffset(const QPointF& offset) { m_offset = offset; } qreal scale() const { return m_scale; } void setScale(qreal scale) { m_scale = scale; } std::vector& hexBricks() { return m_hexBricks; } const std::vector& hexBricks() const { return m_hexBricks; } qreal animationDelay() const { return m_animationDelay; } private: QPointF m_offset; qreal m_scale; std::vector m_hexBricks; qreal m_animationDelay; }; class HexagonLoaderWidget : public QWidget { Q_OBJECT Q_PROPERTY(qreal centerGelScale READ centerGelScale WRITE setCenterGelScale NOTIFY centerGelScaleChanged) Q_PROPERTY(qreal r1Scale READ r1Scale WRITE setR1Scale NOTIFY r1ScaleChanged) Q_PROPERTY(qreal r2Scale READ r2Scale WRITE setR2Scale NOTIFY r2ScaleChanged) Q_PROPERTY(qreal r3Scale READ r3Scale WRITE setR3Scale NOTIFY r3ScaleChanged) public: explicit HexagonLoaderWidget(QWidget *parent = nullptr); ~HexagonLoaderWidget(); qreal centerGelScale() const; void setCenterGelScale(qreal scale); qreal r1Scale() const; void setR1Scale(qreal scale); qreal r2Scale() const; void setR2Scale(qreal scale); qreal r3Scale() const; void setR3Scale(qreal scale); signals: void centerGelScaleChanged(); void r1ScaleChanged(); void r2ScaleChanged(); void r3ScaleChanged(); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; void drawHexagon(QPainter& painter, const QPointF& center, qreal size, const QColor& color, qreal rotation = 0.0); private: void setupGels(); void setupAnimations(); QPointF calculateHexagonPoint(qreal size, qreal angleDegrees); std::vector m_gels; QList m_pulseAnimations; QList m_fadeAnimations; qreal m_centerGelCurrentScale; qreal m_r1CurrentScale; qreal m_r2CurrentScale; qreal m_r3CurrentScale; qreal m_currentGlobalScale = 1.0; }; #endif // HEXAGONWIDGET_H ================================================ FILE: Anime_Template_Project/LoadingAnime/line_loading.cpp ================================================ // line_loading.cpp #include "line_loading.h" #include #include #include #include #include M_Line::M_Line(QObject *parent) : QObject(parent) { m_animation = new QPropertyAnimation(this, "angle"); m_animation->setDuration(1100); m_animation->setStartValue(0.0); m_animation->setEndValue(90.0); m_animation->setEasingCurve(QEasingCurve::Linear); connect(m_animation, &QPropertyAnimation::finished, this,[=]{ m_isFinished = true; }); } qreal M_Line::angle() const { return m_angle; } void M_Line::setAngle(qreal newAngle) { if (m_angle != newAngle) { m_angle = newAngle; } } void M_Line::startAnimation() { m_isFinished = false; m_animation->setDirection(QAbstractAnimation::Forward); m_animation->start(); } void M_Line::resetAnimation() { m_isFinished = false; m_animation->setDirection(QAbstractAnimation::Backward); m_animation->start(); } bool M_Line::isFinished() const { return m_isFinished; } Line_Loading::Line_Loading(QWidget *parent) : QWidget{parent} { generateLines(); animation = new QPropertyAnimation(this, "Gradient_Position"); animation->setDuration(5000); animation->setStartValue(0); animation->setEndValue(360); animation->setLoopCount(-1); animation->setEasingCurve(QEasingCurve::Linear); animation->start(); m_timer = new QTimer(this); m_timer2 = new QTimer(this); connect(m_timer, &QTimer::timeout, this, &Line_Loading::onTimeout); connect(m_timer2, &QTimer::timeout, this, [=]{ if (m_elapsedTimer.elapsed() > m_overTime ) { if (!m_lines[0]->isFinished()) { qDebug() << "最前面的动画没有执行完"; return; } if (!m_lines[m_lines.size()-1]->isFinished()) { qDebug() << "最后面的动画没有执行完"; return; } qDebug() << "执行了计时器"; m_elapsedTimer.invalidate(); m_timer->start(80); } this->update(); }); m_timer->start(80); m_timer2->start(16); } Line_Loading::~Line_Loading() { if (m_timer->isActive()) { m_timer->stop(); } } void Line_Loading::generateLines() { for (int i = 0; i < 20; ++i) { M_Line *line = new M_Line(this); m_lines.push_back(line); } } void Line_Loading::onTimeout() { if (m_count >= m_lines.size()) { m_count = 0; m_count2 = m_lines.size() - 1; m_timer->stop(); m_direction = !m_direction; m_elapsedTimer.start(); return; } if (m_count2 < 0) { m_count2 = m_lines.size() - 1; m_direction = !m_direction; m_elapsedTimer.start(); m_timer->stop(); return; } if (m_direction) { m_lines[m_count]->startAnimation(); m_count++; } else { m_lines[m_count2]->resetAnimation(); m_count2--; } } void Line_Loading::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setBrush(QColor(239, 239, 239, 255)); painter.setPen(Qt::NoPen); painter.drawRect(rect()); const qreal LINE_WIDTH_RATIO_TO_HEIGHT = 0.2; const int total_lines = m_lines.size(); if (total_lines == 0) { return; } QConicalGradient gradient(width() / 2, height(), -m_Gradient_Position); gradient.setColorAt(0, QColor( 255, 167, 69, 255)); gradient.setColorAt(0.125, QColor(254, 134, 159, 255)); gradient.setColorAt(0.25, QColor(239, 122, 200, 255)); gradient.setColorAt(0.375, QColor(160, 131, 237, 255)); gradient.setColorAt(0.5, QColor(67, 174, 255, 255)); gradient.setColorAt(0.625, QColor(160, 131, 237, 255)); gradient.setColorAt(0.75, QColor(239, 122, 200, 255)); gradient.setColorAt(0.875, QColor(254, 134, 159, 255)); gradient.setColorAt(1, QColor(255, 167, 69, 255)); const qreal ideal_line_height_by_height = height() * 0.7; const qreal target_total_width_at_90_deg = width() * 0.8; qreal max_line_height_by_width; if (total_lines > 1) { max_line_height_by_width = target_total_width_at_90_deg / (total_lines - 1.0 + LINE_WIDTH_RATIO_TO_HEIGHT); } else { max_line_height_by_width = target_total_width_at_90_deg; } const qreal line_height = qMin(ideal_line_height_by_height, max_line_height_by_width); qreal line_width = line_height * LINE_WIDTH_RATIO_TO_HEIGHT; if (line_width < 1.0) { line_width = 1.0; } const qreal widget_center_y = height() / 2.0; const qreal fixed_bottom_y = widget_center_y + (line_height / 2.0); const qreal fixed_horizontal_pitch = line_height; const qreal total_bottom_points_span = (total_lines - 1) * fixed_horizontal_pitch + line_width; const qreal start_pivot_x = (width() - total_bottom_points_span) / 2.0; QPainterPath combinedClipPath; for (int i = 0; i < total_lines; ++i) { qreal lineAngle = m_lines[i]->angle(); qreal current_pivot_x = start_pivot_x + i * fixed_horizontal_pitch; QTransform transform; transform.translate(current_pivot_x, fixed_bottom_y); transform.rotate(lineAngle); QRectF rectInLocalCoords(-line_width / 2.0, -line_height, line_width, line_height /*+ 1.0*/); QPainterPath singleLinePath; singleLinePath.addRect(rectInLocalCoords); QPainterPath transformedSingleLinePath = transform.map(singleLinePath); combinedClipPath.addPath(transformedSingleLinePath); } painter.setClipPath(combinedClipPath); painter.setBrush(gradient); painter.setPen(Qt::NoPen); painter.drawRect(rect()); } int Line_Loading::Gradient_Position() const { return m_Gradient_Position; } void Line_Loading::setGradient_Position(int newGradient_Position) { m_Gradient_Position = newGradient_Position; } ================================================ FILE: Anime_Template_Project/LoadingAnime/line_loading.h ================================================ #ifndef LINE_LOADING_H #define LINE_LOADING_H #include #include #include #include #include #include #include #include #include #include class M_Line : public QObject { Q_OBJECT Q_PROPERTY(qreal angle READ angle WRITE setAngle) public: explicit M_Line(QObject *parent = nullptr); void startAnimation(); void resetAnimation(); qreal angle() const; void setAngle(qreal newAngle); bool isFinished() const; private: qreal m_angle{0.0}; bool m_isFinished{true}; QPropertyAnimation *m_animation; }; class Line_Loading : public QWidget { Q_OBJECT Q_PROPERTY(int Gradient_Position READ Gradient_Position WRITE setGradient_Position) public: explicit Line_Loading(QWidget *parent = nullptr); ~Line_Loading(); int Gradient_Position() const; void setGradient_Position(int newGradient_Position); protected: void paintEvent(QPaintEvent *event) override; private slots: void onTimeout(); private: std::vector m_lines; void generateLines(); QTimer *m_timer; QTimer *m_timer2; int m_count{0}; int m_count2{0}; bool m_direction{true}; bool m_isFinished{false}; QElapsedTimer m_elapsedTimer; qint64 m_overTime{3500}; QPropertyAnimation *animation; int m_Gradient_Position; signals: }; #endif // LINE_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/line_rotating_along_rectangular_border.cpp ================================================ // Line_Rotating_Along_Rectangular_Border.cpp #include "line_rotating_along_rectangular_border.h" #include #include #include Line_Rotating_Along_Rectangular_Border::Line_Rotating_Along_Rectangular_Border(QWidget *parent) : QWidget{parent}, m_rotationAngle(0.0) { setAttribute(Qt::WA_TranslucentBackground); m_animation = new QPropertyAnimation(this, "rotationAngle"); m_animation->setDuration(2000); m_animation->setStartValue(0.0); m_animation->setEndValue(360.0); m_animation->setLoopCount(-1); m_animation->setEasingCurve(QEasingCurve::InOutSine); m_animation->start(); } qreal Line_Rotating_Along_Rectangular_Border::getRotationAngle() const { return m_rotationAngle; } void Line_Rotating_Along_Rectangular_Border::setRotationAngle(qreal angle) { if (m_rotationAngle == angle) return; m_rotationAngle = angle; emit rotationAngleChanged(m_rotationAngle); update(); } void Line_Rotating_Along_Rectangular_Border::calculateSquareGeometry() { int windowWidth = width(); int windowHeight = height(); int minSide = qMin(windowWidth, windowHeight); int squareSide = minSide / 2; int x = (windowWidth - squareSide) / 2; int y = (windowHeight - squareSide) / 2; m_squareRect = QRect(x, y, squareSide, squareSide); m_borderThickness = qMax(1, minSide / 50); } void Line_Rotating_Along_Rectangular_Border::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(239, 239, 239, 255)); painter.setPen(Qt::NoPen); painter.drawRect(rect()); QPainterPath outerPath; outerPath.addRect(m_squareRect); QRect innerRect = m_squareRect.adjusted(m_borderThickness, m_borderThickness, -m_borderThickness, -m_borderThickness); QPainterPath innerPath; innerPath.addRect(innerRect); QPainterPath borderClipPath = outerPath.subtracted(innerPath); painter.setClipPath(borderClipPath); painter.save(); qreal rotatingRectLength = width(); qreal rotatingRectWidth = m_squareRect.width() / 2.0; painter.translate(m_squareRect.center()); painter.rotate(m_rotationAngle); painter.setBrush(Qt::black); painter.setPen(Qt::NoPen); QRectF rotatingRect(-rotatingRectLength / 2.0, -rotatingRectWidth / 2.0, rotatingRectLength, rotatingRectWidth); painter.drawRect(rotatingRect); painter.restore(); } void Line_Rotating_Along_Rectangular_Border::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); calculateSquareGeometry(); update(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/line_rotating_along_rectangular_border.h ================================================ #ifndef LINE_ROTATING_ALONG_RECTANGULAR_BORDER_H #define LINE_ROTATING_ALONG_RECTANGULAR_BORDER_H #include #include #include #include #include class Line_Rotating_Along_Rectangular_Border : public QWidget { Q_OBJECT Q_PROPERTY(qreal rotationAngle READ getRotationAngle WRITE setRotationAngle NOTIFY rotationAngleChanged) public: explicit Line_Rotating_Along_Rectangular_Border(QWidget *parent = nullptr); qreal getRotationAngle() const; void setRotationAngle(qreal angle); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: QRect m_squareRect; int m_borderThickness; qreal m_rotationAngle; QPropertyAnimation *m_animation; void calculateSquareGeometry(); signals: void rotationAngleChanged(qreal angle); }; #endif // LINE_ROTATING_ALONG_RECTANGULAR_BORDER_H ================================================ FILE: Anime_Template_Project/LoadingAnime/main.cpp ================================================ #include "widget.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/rectangle_loading.cpp ================================================ #include "Rectangle_Loading.h" #include #include #include Rectangle_Loading::Rectangle_Loading(QWidget *parent) : QWidget(parent), m_rotationAngle(0), m_borderWidth(5), m_rotationAnimation(nullptr), m_borderAnimation(nullptr), m_dotTimer(nullptr), m_currentDotCount(0), m_drawColor(QColor(255,64,91,255)) { setupAnimations(); m_dotTimer = new QTimer(this); connect(m_dotTimer, &QTimer::timeout, this, &Rectangle_Loading::updateLoadingDots); m_dotTimer->start(500); } Rectangle_Loading::~Rectangle_Loading() { if (m_rotationAnimation) { m_rotationAnimation->stop(); } if (m_borderAnimation) { m_borderAnimation->stop(); } if (m_dotTimer) { m_dotTimer->stop(); } } void Rectangle_Loading::setRotationAngle(qreal angle) { if (m_rotationAngle != angle) { m_rotationAngle = angle; update(); } } void Rectangle_Loading::setBorderWidth(int width) { if (m_borderWidth != width) { m_borderWidth = width; update(); } } void Rectangle_Loading::setupAnimations() { m_rotationAnimation = new QPropertyAnimation(this, "rotationAngle"); m_rotationAnimation->setStartValue(0); m_rotationAnimation->setEndValue(360); m_rotationAnimation->setDuration(3000); m_rotationAnimation->setLoopCount(-1); m_rotationAnimation->setEasingCurve(QEasingCurve::Linear); m_rotationAnimation->start(); m_borderAnimation = new QPropertyAnimation(this, "borderWidth"); m_borderAnimation->setStartValue(5); m_borderAnimation->setEndValue(20); m_borderAnimation->setDuration(1500); m_borderAnimation->setLoopCount(-1); m_borderAnimation->setDirection(QAbstractAnimation::Forward); m_borderAnimation->setEasingCurve(QEasingCurve::OutCubic); m_borderAnimation->setKeyValueAt(0, 5); m_borderAnimation->setKeyValueAt(0.5, 20); m_borderAnimation->setKeyValueAt(1, 5); m_borderAnimation->start(); } void Rectangle_Loading::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(239, 239, 239, 255)); painter.setPen(Qt::NoPen); painter.drawRect(rect()); int minDimension = qMin(width(), height()); int squareSide = minDimension / 2; int minBorderWidth = qMax(2, minDimension / 40); int maxBorderWidth = qMax(minBorderWidth + 5, minDimension / 10); if (m_borderAnimation->state() == QAbstractAnimation::Running) { m_borderAnimation->setKeyValueAt(0, minBorderWidth); m_borderAnimation->setKeyValueAt(0.5, maxBorderWidth); m_borderAnimation->setKeyValueAt(1, minBorderWidth); } m_borderWidth = qBound(minBorderWidth, m_borderWidth, maxBorderWidth); if (squareSide <= m_borderWidth * 2) { squareSide = m_borderWidth * 2 + 1; } QPen pen; pen.setColor(m_drawColor); pen.setWidth(m_borderWidth); pen.setJoinStyle(Qt::MiterJoin); painter.setPen(pen); painter.save(); painter.translate(width() / 2.0, height() / 2.0); painter.rotate(m_rotationAngle); painter.drawRect(-squareSide / 2.0, -squareSide / 2.0, squareSide, squareSide); painter.restore(); painter.setPen(m_drawColor); QFont font; font.setPointSize(qMax(10, minDimension / 22)); font.setBold(true); painter.setFont(font); QString baseText = "Loading"; QString dots = ""; for (int i = 0; i < m_currentDotCount; ++i) { dots += "."; } QString displayText = baseText + dots; QFontMetrics fm(font); int textWidth = fm.horizontalAdvance(displayText); int textHeight = fm.height(); int x = (width() - textWidth) / 2; int y = (height() - textHeight) / 2 + fm.ascent(); painter.drawText(x, y, displayText); } void Rectangle_Loading::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); update(); } void Rectangle_Loading::updateLoadingDots() { m_currentDotCount = (m_currentDotCount + 1) % 4; update(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/regular_loading.cpp ================================================ // regular_loading.cpp #include "regular_loading.h" #include Regular_Loading::Regular_Loading(QWidget *parent) : QWidget{parent} , m_rotationAngle(0) { setMinimumSize(100, 100); m_animation = new QPropertyAnimation(this, "rotationAngle"); m_animation->setStartValue(0); m_animation->setEndValue(360); m_animation->setDuration(2000); m_animation->setLoopCount(-1); m_animation->setEasingCurve(QEasingCurve::Linear); connect(this, &Regular_Loading::rotationAngleChanged, this, QOverload<>::of(&QWidget::update)); m_animation->start(); } void Regular_Loading::setRotationAngle(int angle) { if (m_rotationAngle != angle) { m_rotationAngle = angle % 360; emit rotationAngleChanged(m_rotationAngle); } } void Regular_Loading::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(239, 239, 239, 255)); painter.setPen(Qt::NoPen); painter.drawRect(rect()); int side = qMin(width(), height()); int circleDiameter = side * 0.8; int x = (width() - circleDiameter) / 2; int y = (height() - circleDiameter) / 2; QRectF circleRect(x, y, circleDiameter, circleDiameter); qreal borderWidth = qMax(2.0, side / 20.0); QPen circlePen(QColor(252,204,214)); circlePen.setWidthF(borderWidth); circlePen.setCapStyle(Qt::RoundCap); painter.setPen(circlePen); painter.drawEllipse(circleRect); QPen arcPen(QColor(250,66,101)); arcPen.setWidthF(borderWidth); arcPen.setCapStyle(Qt::RoundCap); painter.setPen(arcPen); int startAngle = -m_rotationAngle * 16; int spanAngle = 40 * 16; painter.drawArc(circleRect, startAngle, spanAngle); } ================================================ FILE: Anime_Template_Project/LoadingAnime/regular_loading.h ================================================ // regular_loading.h #ifndef REGULAR_LOADING_H #define REGULAR_LOADING_H #include #include #include #include #include class Regular_Loading : public QWidget { Q_OBJECT Q_PROPERTY(int rotationAngle READ rotationAngle WRITE setRotationAngle NOTIFY rotationAngleChanged) public: explicit Regular_Loading(QWidget *parent = nullptr); int rotationAngle() const { return m_rotationAngle; } public slots: void setRotationAngle(int angle); signals: void rotationAngleChanged(int angle); protected: void paintEvent(QPaintEvent *event) override; private: int m_rotationAngle; QPropertyAnimation *m_animation; }; #endif // REGULAR_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/ring_loading.cpp ================================================ #include "ring_loading.h" #include #include #include #include Ring_Loading::Ring_Loading(QWidget *parent) : QWidget(parent) { m_timer = new QTimer(this); setRingWidth(10); setMargin(5); connect(m_timer, &QTimer::timeout, this, [=]{ Speed += 0.5; m_Inner_Arc_Start_Angle -= 16 * Speed; if (m_Inner_Arc_Start_Angle >= 360 * 16) { m_Inner_Arc_Start_Angle = 0; } update(); }); setInner_Arc_End_Angle(40); } void Ring_Loading::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); int side = qMin(width(), height()); m_centerX = side / 2; qreal actualMargin = m_margin + m_ringWidth / 2.0; QRectF rect( (width() - side) / 2.0, (height() - side) / 2.0, side, side); rect.adjust(actualMargin, actualMargin, -actualMargin, -actualMargin); if (rect.isEmpty()) { return; } QPen pen; pen.setColor(m_ringColor); pen.setWidth(m_ringWidth); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.drawArc(rect, m_startAngle, m_spanAngle); painter.drawArc(rect, m_startAngle + 120 * 16 , m_spanAngle); painter.drawArc(rect, m_startAngle + 240 * 16, m_spanAngle); painter.setPen(m_ringColor); painter.setFont(QFont("Arial", side / 20)); painter.drawText(rect, Qt::AlignCenter, "Loading"); painter.setFont(QFont("Arial", side / 40)); QString text = QString::number(qreal(m_Progress_Ring) / 16.0 / 360.0 * 100.0, 'f', 0) + "%"; painter.drawText(QRectF(rect.x() ,rect.y() + side / 20,rect.width(),rect.height()), Qt::AlignCenter, text); QPen innerPen; innerPen.setColor(QColor(255, 255, 255, m_Inner_Arc_Alpha)); innerPen.setWidth(m_ringWidth); innerPen.setCapStyle(Qt::RoundCap); painter.setPen(innerPen); int innerMargin = m_ringWidth * 2.0; rect.adjust(innerMargin , innerMargin , - innerMargin, - innerMargin); painter.drawArc(rect, m_startAngle + m_Inner_Arc_Start_Angle, m_Inner_Arc_End_Angle); painter.drawArc(rect, m_startAngle + m_Inner_Arc_Start_Angle + 120 * 16 , m_Inner_Arc_End_Angle); painter.drawArc(rect, m_startAngle + m_Inner_Arc_Start_Angle + 240 * 16, m_Inner_Arc_End_Angle); QPen outerPen; outerPen.setColor(QColor(255, 255, 255, 255)); outerPen.setWidth(m_ringWidth * 2); outerPen.setCapStyle(Qt::RoundCap); painter.setPen(outerPen); int outerMargin = m_ringWidth * 3.0; rect.adjust(-outerMargin, -outerMargin , outerMargin, outerMargin); painter.drawArc(rect, -m_Arc_Position_Value, -m_Progress_Ring); } void Ring_Loading::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); update(); QWidget::resizeEvent(event); } void Ring_Loading::setMargin(int margin) { margin = qBound(0, margin, qMin(width(), height()) / 2 - m_ringWidth); if (m_margin != margin) { m_margin = margin; emit marginChanged(m_margin); update(); } } void Ring_Loading::setRingColor(const QColor &color) { if (m_ringColor != color) { m_ringColor = color; emit ringColorChanged(m_ringColor); update(); } } void Ring_Loading::setStartAngle(int angle) { int newStart = angle * 16; if (m_startAngle != newStart) { m_startAngle = newStart; emit startAngleChanged(angle); update(); } } void Ring_Loading::setEndAngle(int angle) { int newSpan = (angle - (m_startAngle / 16)) * 16; if (newSpan < 0) { newSpan += 360 * 16; } if (m_spanAngle != newSpan) { m_spanAngle = newSpan; emit endAngleChanged(angle); update(); } } void Ring_Loading::setRingWidth(int width) { width = qMax(1, width); if (m_ringWidth != width) { m_ringWidth = width; emit ringWidthChanged(m_ringWidth); update(); } } void Ring_Loading::startAnimation() { QPropertyAnimation *animation = new QPropertyAnimation(this, "startAngle"); animation->setDuration(2000); animation->setStartValue(0); animation->setEndValue(360); QPropertyAnimation *animation2 = new QPropertyAnimation(this, "endAngle"); animation2->setDuration(2000); animation2->setStartValue(0); animation2->setEndValue(489); QPropertyAnimation *animation3 = new QPropertyAnimation(this, "margin"); animation3->setDuration(2000); animation3->setStartValue(m_centerX); animation3->setEndValue(50); QPropertyAnimation *animation4 = new QPropertyAnimation(this, "ringColor"); animation4->setDuration(1500); animation4->setStartValue(QColor(255,255,255,0)); animation4->setEndValue(QColor(255,255,255,255)); animation->start(QAbstractAnimation::DeleteWhenStopped); animation2->start(QAbstractAnimation::DeleteWhenStopped); animation3->start(QAbstractAnimation::DeleteWhenStopped); animation4->start(QAbstractAnimation::DeleteWhenStopped); connect(animation2, &QPropertyAnimation::finished, this, &Ring_Loading::startAnimation2); connect(animation4, &QPropertyAnimation::finished, this,[=]{ QPropertyAnimation *animation5 = new QPropertyAnimation(this, "ringColor"); animation5->setDuration(200); animation5->setStartValue(QColor(255,255,255,0)); animation5->setEndValue(QColor(255,255,255,255)); animation5->start(QAbstractAnimation::DeleteWhenStopped); connect(animation5, &QPropertyAnimation::stateChanged, this, [=]{ QPropertyAnimation *animation6 = new QPropertyAnimation(this, "Inner_Arc_Alpha"); animation6->setDuration(300); animation6->setStartValue(0); animation6->setEndValue(255); animation6->start(QAbstractAnimation::DeleteWhenStopped); }); }); } void Ring_Loading::startAnimation2() { QPropertyAnimation *animation = new QPropertyAnimation(this, "Progress_Ring"); animation->setDuration(2000); animation->setStartValue(0); animation->setEndValue(360); QPropertyAnimation *animation2 = new QPropertyAnimation(this, "Arc_Position_Value"); animation2->setDuration(2000); animation2->setStartValue(0); animation2->setEndValue(180); connect(animation, &QPropertyAnimation::stateChanged, this,[=](QAbstractAnimation::State newState){ if (newState == QAbstractAnimation::Running) { m_timer->start(15); } }); connect(animation, &QPropertyAnimation::finished, this,[=]{ m_timer->stop(); m_Inner_Arc_Alpha = 0; m_Progress_Ring = 0; update(); startAnimation(); }); animation->start(QAbstractAnimation::DeleteWhenStopped); animation2->start(QAbstractAnimation::DeleteWhenStopped); } int Ring_Loading::name() const { return m_Progress_Ring / 16; } void Ring_Loading::setname(int newProgress_Ring) { if (m_Progress_Ring == newProgress_Ring * 16) return; m_Progress_Ring = newProgress_Ring * 16; update(); emit nameChanged(); } int Ring_Loading::Inner_Arc_Start_Angle() const { return m_Inner_Arc_Start_Angle / 16; } void Ring_Loading::setInner_Arc_Start_Angle(int newInner_Arc_Start_Angle) { if (m_Inner_Arc_Start_Angle == newInner_Arc_Start_Angle * 16) return; m_Inner_Arc_Start_Angle = newInner_Arc_Start_Angle * 16; emit Inner_Arc_Start_AngleChanged(); } int Ring_Loading::Inner_Arc_End_Angle() const { return m_Inner_Arc_End_Angle / 16; } void Ring_Loading::setInner_Arc_End_Angle(int newInner_Arc_End_Angle) { if (m_Inner_Arc_End_Angle == newInner_Arc_End_Angle * 16) return; m_Inner_Arc_End_Angle = newInner_Arc_End_Angle * 16; update(); emit Inner_Arc_End_AngleChanged(); } int Ring_Loading::Inner_Arc_Alpha() const { return m_Inner_Arc_Alpha; } void Ring_Loading::setInner_Arc_Alpha(int newInner_Arc_Alpha) { if (m_Inner_Arc_Alpha == newInner_Arc_Alpha) return; m_Inner_Arc_Alpha = newInner_Arc_Alpha; update(); emit Inner_Arc_AlphaChanged(); } int Ring_Loading::Arc_Position_Value() const { return m_Arc_Position_Value / 16; } void Ring_Loading::setArc_Position_Value(int newArc_Position_Value) { if (m_Arc_Position_Value == newArc_Position_Value * 16) return; m_Arc_Position_Value = newArc_Position_Value * 16; update(); } ================================================ FILE: Anime_Template_Project/LoadingAnime/ring_loading.h ================================================ #ifndef RING_LOADING_H #define RING_LOADING_H #include #include #include #include #include class Ring_Loading : public QWidget { Q_OBJECT Q_PROPERTY(int margin READ getMargin WRITE setMargin NOTIFY marginChanged) Q_PROPERTY(QColor ringColor READ getRingColor WRITE setRingColor NOTIFY ringColorChanged) Q_PROPERTY(int startAngle READ getStartAngle WRITE setStartAngle NOTIFY startAngleChanged) Q_PROPERTY(int endAngle READ getEndAngle WRITE setEndAngle NOTIFY endAngleChanged) Q_PROPERTY(int ringWidth READ getRingWidth WRITE setRingWidth NOTIFY ringWidthChanged) Q_PROPERTY(int Progress_Ring READ name WRITE setname NOTIFY nameChanged FINAL) Q_PROPERTY(int Inner_Arc_Start_Angle READ Inner_Arc_Start_Angle WRITE setInner_Arc_Start_Angle NOTIFY Inner_Arc_Start_AngleChanged FINAL) Q_PROPERTY(int Inner_Arc_End_Angle READ Inner_Arc_End_Angle WRITE setInner_Arc_End_Angle NOTIFY Inner_Arc_End_AngleChanged FINAL) Q_PROPERTY(int Inner_Arc_Alpha READ Inner_Arc_Alpha WRITE setInner_Arc_Alpha NOTIFY Inner_Arc_AlphaChanged FINAL) Q_PROPERTY(int Arc_Position_Value READ Arc_Position_Value WRITE setArc_Position_Value FINAL) public: explicit Ring_Loading(QWidget *parent = nullptr); int getMargin() const { return m_margin; } QColor getRingColor() const { return m_ringColor; } int getStartAngle() const { return m_startAngle / 16; } int getEndAngle() const { return (m_startAngle + m_spanAngle) / 16; } void setMargin(int margin); void setRingColor(const QColor &color); void setStartAngle(int angle); void setEndAngle(int angle); int getRingWidth() const { return m_ringWidth; } void setRingWidth(int width); int name() const; void setname(int newProgress_Ring); void initCenterX() { m_centerX = qMin(width(), height()) / 2; } int Inner_Arc_Start_Angle() const; void setInner_Arc_Start_Angle(int newInner_Arc_Start_Angle); int Inner_Arc_End_Angle() const; void setInner_Arc_End_Angle(int newInner_Arc_End_Angle); int Inner_Arc_Alpha() const; void setInner_Arc_Alpha(int newInner_Arc_Alpha); int Arc_Position_Value() const; void setArc_Position_Value(int newArc_Position_Value); public slots: void startAnimation(); void startAnimation2(); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: QColor m_ringColor; int m_startAngle; int m_spanAngle; int m_ringWidth; int m_margin; int m_Progress_Ring{0}; int m_Arc_Position_Value{0}; int m_centerX{0}; int m_Inner_Arc_Start_Angle{0}; int m_Inner_Arc_End_Angle{0}; int m_Inner_Arc_Alpha{0}; QTimer *m_timer; qreal Speed{0}; signals: void marginChanged(int margin); void ringColorChanged(const QColor &color); void startAngleChanged(int angle); void endAngleChanged(int angle); void ringWidthChanged(int width); void nameChanged(); void Inner_Arc_Start_AngleChanged(); void Inner_Arc_End_AngleChanged(); void Inner_Arc_AlphaChanged(); }; #endif // RING_LOADING_H ================================================ FILE: Anime_Template_Project/LoadingAnime/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { this->resize(1200,860); m_ringLoading = new Ring_Loading(this); m_ringLoading->resize(this->size() / 2 ); m_ringLoading->initCenterX(); m_ringLoading->startAnimation(); m_bouncingBallLoading = new Bouncing_Ball_Loading(this); m_rectangleLoading = new Rectangle_Loading(this); m_lineRotating = new Line_Rotating_Along_Rectangular_Border(this); m_infinitySymbolLoading = new Square_Pseudo_Bounce_Loading(this); m_regularLoading = new Regular_Loading(this); m_lineLoading = new Line_Loading(this); loadingWidget = new Black_White_Ball_Loading(this); loadingWidget->setBallPairCount(36); loadingWidget->setAnimationRatios(0.3, 0.4); loadingWidget->setBallRadiusRatio(0.025); loadingWidget->setAnimationSpeed(0.025); loadingWidget->setGroupCount(4); loader = new HexagonLoaderWidget(this); } Widget::~Widget() {} void Widget::resizeEvent(QResizeEvent *event) { m_ringLoading->resize(event->size() / 3); m_ringLoading->move(0, 0); m_bouncingBallLoading->move(event->size().width() / 3, 0); m_bouncingBallLoading->setSize(event->size() / 3 ); m_rectangleLoading->move(event->size().width() / 3 * 2, 0); m_rectangleLoading->resize(event->size() / 3 ); m_lineRotating->move(0, event->size().height() / 3); m_lineRotating->resize(event->size() / 3 ); m_infinitySymbolLoading->move(event->size().width() / 3, event->size().height() / 3); m_infinitySymbolLoading->resize(event->size() / 3 ); m_regularLoading->move(event->size().width() / 3 * 2, event->size().height() / 3); m_regularLoading->resize(event->size() / 3 ); m_lineLoading->move(0, event->size().height() / 3 * 2); m_lineLoading->resize(event->size() / 3 ); loadingWidget->move(event->size().width() / 3, event->size().height() / 3 * 2); loadingWidget->resize(event->size() / 3 ); loader->move(event->size().width() / 3 * 2, event->size().height() / 3 * 2); loader->resize(event->size() / 3 ); QWidget::resizeEvent(event); } ================================================ FILE: Anime_Template_Project/LoadingAnime/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include #include "ring_loading.h" #include "bouncing_ball_loading.h" #include "Rectangle_Loading.h" #include "line_rotating_along_rectangular_border.h" #include "Square_Pseudo_Bounce_Loading.h" #include "regular_loading.h" #include "line_loading.h" #include "black_white_ball_loading.h" #include "HexagonLoaderWidget.h" class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); protected: void resizeEvent(QResizeEvent *event) override; private: Ring_Loading *m_ringLoading; Bouncing_Ball_Loading *m_bouncingBallLoading; Rectangle_Loading *m_rectangleLoading; Line_Rotating_Along_Rectangular_Border *m_lineRotating; Square_Pseudo_Bounce_Loading *m_infinitySymbolLoading; Regular_Loading *m_regularLoading; Line_Loading *m_lineLoading; Black_White_Ball_Loading *loadingWidget; HexagonLoaderWidget *loader; }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/Login_interface/hollow_button.cpp ================================================ #include "hollow_button.h" Hollow_button::Hollow_button(QWidget* parent) : QPushButton{ parent } { this->setCursor(Qt::PointingHandCursor); animation3 = new QPropertyAnimation(this, "radius"); animation3->setDuration(400); animation3->setStartValue(m_radius); animation3->setEndValue(this->width()); animation3->setEasingCurve(QEasingCurve::Linear); animation3->setDirection(QAbstractAnimation::Forward); animation1 = new QPropertyAnimation(this, "opacity"); animation1->setDuration(400); animation1->setStartValue(m_opacity); animation1->setEndValue(0); animation1->setEasingCurve(QEasingCurve::Linear); animation1->setDirection(QAbstractAnimation::Forward); connect(animation3, &QPropertyAnimation::finished, this, &Hollow_button::reset_animation); connect(animation1, &QPropertyAnimation::finished, this, &Hollow_button::reset_animation); } void Hollow_button::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); this->draw_border(); this->draw_text(); this->draw_disappearing_circle(); } void Hollow_button::draw_border() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), height() / 4, height() / 4); painter.setClipPath(path); QPen pen; pen.setWidth(5); pen.setColor(QColor(255, 255, 255, 255)); painter.setPen(pen); painter.drawRoundedRect(0, 0, width(), height(), height() / 4, height() / 4); } void Hollow_button::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, 0, 0); QFont font1; font1.setPointSize(13); font1.setBold(true); font1.setWordSpacing(1); painter.setFont(font1); QColor semiTransparent(255, 255, 255, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, m_center_text); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() / 2)); painter.drawText(rect1, m_center_text); } void Hollow_button::draw_disappearing_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), height() / 4, height() / 4); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush brush(QColor(255, 255, 255, m_opacity)); painter.setBrush(brush); painter.drawEllipse(mouse_coordinates, m_radius, m_radius); } void Hollow_button::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { mouse_coordinates = event->pos(); update(); execute_animation(); } QWidget::mousePressEvent(event); } void Hollow_button::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { if (this->status == true) emit page_changed(Hollow_button::ANIMATION_STATE_EXECUTING); else emit page_changed(Hollow_button::ANIMATION_STATE_RESET); } QWidget::mouseReleaseEvent(event); } void Hollow_button::animation_status(bool status) { this->status = status; } void Hollow_button::execute_animation() { animation3->start(); animation1->start(); } void Hollow_button::reset_animation() { m_radius = 0; m_opacity = 255; } QString Hollow_button::center_text() const { return m_center_text; } void Hollow_button::setCenter_text(const QString &newCenter_text) { m_center_text = newCenter_text; update(); } int Hollow_button::radius() const { return m_radius; } void Hollow_button::setRadius(int newRadius) { if (m_radius == newRadius) return; m_radius = newRadius; update(); } int Hollow_button::opacity() const { return m_opacity; } void Hollow_button::setOpacity(int newOpacity) { if (m_opacity == newOpacity) return; m_opacity = newOpacity; update(); } ================================================ FILE: Anime_Template_Project/Login_interface/hollow_button.h ================================================ #ifndef HOLLOW_BUTTON_H #define HOLLOW_BUTTON_H #include #include #include #include #include class Hollow_button : public QPushButton { Q_OBJECT Q_PROPERTY(QString center_text READ center_text WRITE setCenter_text FINAL) Q_PROPERTY(int radius READ radius WRITE setRadius FINAL) Q_PROPERTY(int opacity READ opacity WRITE setOpacity FINAL) public: explicit Hollow_button(QWidget* parent = nullptr); enum AnimationState { ANIMATION_STATE_EXECUTING, ANIMATION_STATE_RESET }; void draw_disappearing_circle(); void draw_border(); void draw_text(); QPoint mouse_coordinates; QPropertyAnimation* animation1; QPropertyAnimation* animation3; QString center_text() const; void setCenter_text(const QString &newCenter_text); int radius() const; void setRadius(int newRadius); int opacity() const; void setOpacity(int newOpacity); void reset_animation(); void execute_animation(); void animation_status(bool status); signals: void page_changed(AnimationState status); protected: void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); private: QString m_center_text; int m_radius = 0; int m_opacity = 255; bool status = true; }; #endif // HOLLOW_BUTTON_H ================================================ FILE: Anime_Template_Project/Login_interface/input_box.cpp ================================================ #include "input_box.h" Input_box::Input_box(QString icon, QWidget* parent) : QLineEdit{ parent } { this->icon = QPixmap(icon); this->resize(388, 58); setTextMargins(25, 0, 25, 0); QFont font = this->font(); font.setPointSize(15); setFont(font); QPalette palette = this->palette(); palette.setColor(QPalette::PlaceholderText, Qt::gray); this->setPalette(palette); palette.setColor(QPalette::Text, Qt::black); this->setPalette(palette); palette.setColor(QPalette::Base, Qt::transparent); this->setPalette(palette); this->setFrame(QFrame::NoFrame); } void Input_box::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); crop_corner(); draw_ico_image(); QLineEdit::paintEvent(event); } void Input_box::crop_corner() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 10, 10); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush Brush(QColor(238, 238, 238, 255)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); } void Input_box::draw_ico_image() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); QRect rect(width() * 0.85, height() / 4, height() / 2, height() / 2); painter.drawPixmap(rect, icon); } ================================================ FILE: Anime_Template_Project/Login_interface/input_box.h ================================================ #ifndef INPUT_BOX_H #define INPUT_BOX_H #include #include #include #include #include class Input_box : public QLineEdit { Q_OBJECT public: explicit Input_box(QString icon, QWidget* parent = nullptr); protected: void paintEvent(QPaintEvent *event); public: void crop_corner(); void draw_ico_image(); private: QPixmap icon; }; #endif // INPUT_BOX_H ================================================ FILE: Anime_Template_Project/Login_interface/login_button.cpp ================================================ #include "login_button.h" Login_button::Login_button(QWidget* parent) : QPushButton{ parent } { this->resize(392, 57); this->setCursor(Qt::PointingHandCursor); setMouseTracking(true); this->start_animation(); } void Login_button::start_animation() { animation = new QPropertyAnimation(this, "color_opacity"); animation->setDuration(200); animation->setStartValue(this->color_opacity()); animation->setEndValue(188); animation->setEasingCurve(QEasingCurve::Linear); } void Login_button::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 10, 10); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush Brush(QColor(123, 150, 228, m_color_opacity)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); this->draw_text(); } void Login_button::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, 0, 0); QFont font1; font1.setPointSize(13); font1.setBold(true); font1.setWordSpacing(1); painter.setFont(font1); QColor semiTransparent(255, 255, 255, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, m_center_text); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() / 2)); painter.drawText(rect1, m_center_text); } bool Login_button::event(QEvent* e) { if (e->type() == QEvent::Enter) { animation->setDirection(QPropertyAnimation::Forward); animation->start(); qDebug() << "鼠标进入"; } else if (e->type() == QEvent::Leave) { animation->setDirection(QPropertyAnimation::Backward); animation->start(); qDebug() << "鼠标离开"; update(); } return QPushButton::event(e); } void Login_button::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) emit execute_animation_signal(AnimationState::Execute); QWidget::mousePressEvent(event); } void Login_button::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) emit execute_animation_signal(AnimationState::Restore); QWidget::mouseReleaseEvent(event); } int Login_button::color_opacity() const { return m_color_opacity; } void Login_button::setColor_opacity(int newColor_opacity) { m_color_opacity = newColor_opacity; update(); } QString Login_button::center_text() const { return m_center_text; } void Login_button::setCenter_text(const QString &newCenter_text) { m_center_text = newCenter_text; } ================================================ FILE: Anime_Template_Project/Login_interface/login_button.h ================================================ #ifndef LOGIN_BUTTON_H #define LOGIN_BUTTON_H #include #include #include #include #include class Login_button : public QPushButton { Q_OBJECT Q_PROPERTY(int color_opacity READ color_opacity WRITE setColor_opacity FINAL) Q_PROPERTY(QString center_text READ center_text WRITE setCenter_text FINAL) public: explicit Login_button(QWidget* parent = nullptr); QPropertyAnimation *animation; enum AnimationState { Execute, Restore }; void draw_text(); void start_animation(); int color_opacity() const; void setColor_opacity(int newColor_opacity); QString center_text() const; void setCenter_text(const QString &newCenter_text); protected: void paintEvent(QPaintEvent *event); bool event(QEvent* e); void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); signals: void execute_animation_signal(Login_button::AnimationState state); private: int m_color_opacity = 255; QString m_center_text; }; #endif // LOGIN_BUTTON_H ================================================ FILE: Anime_Template_Project/Login_interface/login_form.cpp ================================================ #include "login_form.h" Login_form::Login_form(QWidget *parent) : QWidget{parent} { this->resize(477, 620); username = new Input_box("://img/account.png", this); username->move(46, 161); username->setPlaceholderText("Username"); username->setMaxLength(11); username->setValidator(new QIntValidator(0, 9999999999, this)); password = new Input_box("://img/password.png", this); password->move(46, 253); password->setPlaceholderText("Password"); password->setEchoMode(QLineEdit::Password); password->setMaxLength(16); password->setValidator(new QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9]+$"), this)); login_button = new Login_button(this); login_button->setCenter_text("Login"); login_button->move(46, 371); other_login_buttons1 = new Other_login_buttons("://img/logo_google.png", this); other_login_buttons1->move(110, 480); other_login_buttons2 = new Other_login_buttons("://img/facebook.png", this); other_login_buttons2->move(178, 480); other_login_buttons3 = new Other_login_buttons("://img/github-fill.png", this); other_login_buttons3->move(250, 480); other_login_buttons4 = new Other_login_buttons("://img/instagram.png", this); other_login_buttons4->move(320, 480); this->animations(); connect(login_button, &Login_button::execute_animation_signal, this, &Login_form::execute_animation); } void Login_form::animations() { animation = new QPropertyAnimation(this->login_button, "geometry"); animation->setDuration(250); animation->setStartValue(this->login_button->geometry()); animation->setEndValue(QRect(this->login_button->pos().x() + zoom_rate, this->login_button->pos().y() + zoom_rate / 2, login_button->width() - zoom_rate * 2, login_button->height() - zoom_rate)); animation->setEasingCurve(QEasingCurve::Linear); } void Login_form::execute_animation(Login_button::AnimationState State) { if (State == Login_button::Execute) { animation->setDirection(QAbstractAnimation::Forward); animation->start(); } else if (State == Login_button::Restore) { animation->setDirection(QAbstractAnimation::Backward); animation->start(); } } void Login_form::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); painter.setViewport(0, 0, 477, 620); painter.setWindow(0, 0, 477, 620); this->crop_corner(); this->draw_text(); } void Login_form::crop_corner() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); QBrush Brush(QColor(255, 255, 255, 255)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); } void Login_form::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, 0, 0); QFont font1; font1.setPointSize(30); font1.setBold(true); font1.setWordSpacing(1); painter.setFont(font1); QColor semiTransparent(0, 0, 0, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, "Login"); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() / 6)); painter.drawText(rect1, "Login"); } ================================================ FILE: Anime_Template_Project/Login_interface/login_form.h ================================================ #ifndef LOGIN_FORM_H #define LOGIN_FORM_H #include #include #include #include #include #include "input_box.h" #include "login_button.h" #include "other_login_buttons.h" class Login_form : public QWidget { Q_OBJECT public: explicit Login_form(QWidget *parent = nullptr); void crop_corner(); void draw_text(); void animations(); QPropertyAnimation* animation; int zoom_rate = 20; public slots: void execute_animation(Login_button::AnimationState State); protected: void paintEvent(QPaintEvent *event); public: Input_box* username; Input_box* password; Login_button* login_button; Other_login_buttons* other_login_buttons1; Other_login_buttons* other_login_buttons2; Other_login_buttons* other_login_buttons3; Other_login_buttons* other_login_buttons4; }; #endif // LOGIN_FORM_H ================================================ FILE: Anime_Template_Project/Login_interface/other_login_buttons.cpp ================================================ #include "other_login_buttons.h" Other_login_buttons::Other_login_buttons(QString icon, QWidget* parent) : QPushButton{ parent } { this->icon = QPixmap(icon); this->resize(54, 54); this->setCursor(Qt::PointingHandCursor); } void Other_login_buttons::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), height() / 4, height() / 4); painter.setClipPath(path); painter.setPen(Qt::NoPen); QPen pen; pen.setWidth(8); pen.setColor(QColor(220, 220, 220, 255)); painter.setPen(pen); painter.drawRoundedRect(0, 0, width(), height(), height() / 4, height() / 4); QRect rect1(0, 0, width() / 2, width() / 2); rect1.moveCenter(QPoint(width() / 2, height() / 2)); painter.drawPixmap(rect1, icon); } ================================================ FILE: Anime_Template_Project/Login_interface/other_login_buttons.h ================================================ #ifndef OTHER_LOGIN_BUTTONS_H #define OTHER_LOGIN_BUTTONS_H #include #include #include #include class Other_login_buttons : public QPushButton { Q_OBJECT public: explicit Other_login_buttons(QString icon, QWidget* parent = nullptr); protected: void paintEvent(QPaintEvent *event); private: QPixmap icon; }; #endif // OTHER_LOGIN_BUTTONS_H ================================================ FILE: Anime_Template_Project/Login_interface/registration_form.cpp ================================================ #include "registration_form.h" Registration_form::Registration_form(QWidget *parent) : QWidget{parent} { this->resize(477, 620); username = new Input_box("://img/account.png", this); username->move(46, 130); username->setPlaceholderText("Username"); username->setMaxLength(11); username->setValidator(new QIntValidator(0, 9999999999, this)); email = new Input_box("://img/email.png", this); email->move(46, 220); email->setPlaceholderText("Email"); email->setMaxLength(16); email->setValidator(new QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"), this)); password = new Input_box("://img/password.png", this); password->move(46, 310); password->setPlaceholderText("Password"); password->setEchoMode(QLineEdit::Password); password->setMaxLength(16); password->setValidator(new QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9]+$"), this)); login_button = new Login_button(this); login_button->setCenter_text("Registration"); login_button->move(46, 400); other_login_buttons1 = new Other_login_buttons("://img/logo_google.png", this); other_login_buttons1->move(110, 510); other_login_buttons2 = new Other_login_buttons("://img/facebook.png", this); other_login_buttons2->move(178, 510); other_login_buttons3 = new Other_login_buttons("://img/github-fill.png", this); other_login_buttons3->move(250, 510); other_login_buttons4 = new Other_login_buttons("://img/instagram.png", this); other_login_buttons4->move(320, 510); this->animations(); connect(login_button, &Login_button::execute_animation_signal, this, &Registration_form::execute_animation); } void Registration_form::animations() { animation = new QPropertyAnimation(this->login_button, "geometry"); animation->setDuration(250); animation->setStartValue(this->login_button->geometry()); animation->setEndValue(QRect(this->login_button->pos().x() + zoom_rate, this->login_button->pos().y() + zoom_rate / 2, login_button->width() - zoom_rate * 2, login_button->height() - zoom_rate)); animation->setEasingCurve(QEasingCurve::Linear); } void Registration_form::execute_animation(Login_button::AnimationState State) { if (State == Login_button::Execute) { animation->setDirection(QAbstractAnimation::Forward); animation->start(); } else if (State == Login_button::Restore) { animation->setDirection(QAbstractAnimation::Backward); animation->start(); } } void Registration_form::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); painter.setViewport(0, 0, 477, 620); painter.setWindow(0, 0, 477, 620); this->crop_corner(); this->draw_text(); } void Registration_form::crop_corner() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); QBrush Brush(QColor(255, 255, 255, 255)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); } void Registration_form::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, 0, 0); QFont font1; font1.setPointSize(30); font1.setBold(true); font1.setWordSpacing(1); painter.setFont(font1); QColor semiTransparent(0, 0, 0, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, "Registration"); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() / 8)); painter.drawText(rect1, "Registration"); } ================================================ FILE: Anime_Template_Project/Login_interface/registration_form.h ================================================ #ifndef REGISTRATION_FORM_H #define REGISTRATION_FORM_H #include #include #include #include #include #include "input_box.h" #include "login_button.h" #include "other_login_buttons.h" class Registration_form : public QWidget { Q_OBJECT public: explicit Registration_form(QWidget *parent = nullptr); void animations(); QPropertyAnimation* animation; int zoom_rate = 20; public: Input_box* username; Input_box* email; Input_box* password; Login_button* login_button; Other_login_buttons* other_login_buttons1; Other_login_buttons* other_login_buttons2; Other_login_buttons* other_login_buttons3; Other_login_buttons* other_login_buttons4; void crop_corner(); void draw_text(); protected: void paintEvent(QPaintEvent* event); public slots: void execute_animation(Login_button::AnimationState State); }; #endif // REGISTRATION_FORM_H ================================================ FILE: Anime_Template_Project/Login_interface/responsive_form.cpp ================================================ #include "responsive_form.h" Responsive_form::Responsive_form(QWidget *parent) : QWidget{parent} { this->setFixedSize(955, 620); this->setWindowFlags(Qt::FramelessWindowHint); transparent_transition_interface2 = new Transparent_transition_interface("Welcome Back!", "Already have an account?", "Login", this); transparent_transition_interface2->button->animation_status(false); transparent_transition_interface2->move(this->width(), 0); registration_form = new Registration_form(this); registration_form->move(width(), 0); login_form = new Login_form(this); login_form->move(this->width() / 2, 0); scroll_bar = new Scroll_bar(this); scroll_bar->move(-width() * 1.5, 0); transparent_transition_interface = new Transparent_transition_interface("Hello, Welcome!", "Don't have an account?", "Register", this); transparent_transition_interface->move(0, 0); this->setRightShow(); this->build_animation(); connect(transparent_transition_interface->button, &Hollow_button::page_changed, this, &Responsive_form::execute_animation); connect(transparent_transition_interface2->button, &Hollow_button::page_changed, this, &Responsive_form::execute_animation); } void Responsive_form::setRightShow() { transparent_transition_interface2->raise(); registration_form->lower(); transparent_transition_interface->raise(); login_form->lower(); } void Responsive_form::build_animation() { animation2 = new QPropertyAnimation(this->scroll_bar, "pos"); animation2->setDuration(m_animation_duration); animation2->setStartValue(this->scroll_bar->pos()); animation2->setEndValue(QPoint(this->width() / 2, 0)); animation2->setEasingCurve(QEasingCurve::Linear); animation3 = new QPropertyAnimation(this->transparent_transition_interface, "pos"); animation3->setDuration(m_animation_duration); animation3->setStartValue(this->transparent_transition_interface->pos()); animation3->setEndValue(QPoint(-this->width() / 2, 0)); animation3->setEasingCurve(QEasingCurve::Linear); animation4 = new QPropertyAnimation(this->transparent_transition_interface2, "pos"); animation4->setDuration(m_animation_duration); animation4->setStartValue(this->transparent_transition_interface2->pos()); animation4->setEndValue(QPoint(this->width() / 2, 0)); animation4->setEasingCurve(QEasingCurve::Linear); animation5 = new QPropertyAnimation(this->registration_form, "pos"); animation5->setDuration(m_animation_duration); animation5->setStartValue(this->registration_form->pos()); animation5->setEndValue(QPoint(0, 0)); animation5->setEasingCurve(QEasingCurve::Linear); animation6 = new QPropertyAnimation(this->login_form, "pos"); animation6->setDuration(m_animation_duration); animation6->setStartValue(this->login_form->pos()); animation6->setEndValue(QPoint(-this->width() / 2, 0)); animation6->setEasingCurve(QEasingCurve::Linear); connect(animation2, &QPropertyAnimation::valueChanged, this, [this] { if (scroll_bar->pos().x() > -this->width() / 2 && animation4->state() == !QAbstractAnimation::Running && animation_execute_duration) { animation2->pause(); animation3->setDirection(QAbstractAnimation::Forward); animation3->start(); animation_execute_duration = false; } else if (scroll_bar->pos().x() < -this->width() / 10 && animation3->state() == !QAbstractAnimation::Running && animation_restore_duration) { animation2->pause(); animation4->setDirection(QAbstractAnimation::Backward); animation4->start(); animation_restore_duration = false; } }); connect(animation3, &QPropertyAnimation::finished, this, [this] { animation2->resume(); }); connect(animation4, &QPropertyAnimation::finished, this, [this] { animation2->resume(); }); connect(animation3, &QPropertyAnimation::finished, this, &Responsive_form::onAnimation3Finished); connect(animation4, &QPropertyAnimation::finished, this, &Responsive_form::onAnimation4Finished); } void Responsive_form::onAnimation3Finished() { if (currentSequence == 1) { animation4->setDirection(QAbstractAnimation::Forward); animation4->start(); animation5->setDirection(QAbstractAnimation::Forward); animation5->start(); } } void Responsive_form::onAnimation4Finished() { if (currentSequence == 1) return; else if (currentSequence == 2) { animation3->setDirection(QAbstractAnimation::Backward); animation3->start(); animation6->setDirection(QAbstractAnimation::Backward); animation6->start(); } } void Responsive_form::execute_animation(Hollow_button::AnimationState status) { if (status == Hollow_button::AnimationState::ANIMATION_STATE_EXECUTING) { animation_execute_duration = true; currentSequence = 1; animation2->setDirection(QAbstractAnimation::Forward); animation2->start(); animation6->setDirection(QAbstractAnimation::Forward); animation6->start(); } else if (status == Hollow_button::AnimationState::ANIMATION_STATE_RESET) { animation_restore_duration = true; currentSequence = 2; animation2->setDirection(QAbstractAnimation::Backward); animation2->start(); } } void Responsive_form::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); this->crop_corner(); } void Responsive_form::crop_corner() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 34, 34); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush Brush(QColor(255, 255, 255, 255)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); } void Responsive_form::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); updateMask(); } void Responsive_form::updateMask() { QPainterPath path; createRoundPath(path); QRegion region = QRegion(path.toFillPolygon().toPolygon()); setMask(region); } void Responsive_form::createRoundPath(QPainterPath& path) { path.addRoundedRect(0, 0, width(), height(), 35, 35); path.closeSubpath(); } void Responsive_form::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { m_dragStartPosition = event->globalPos(); m_startWindowPosition = this->pos(); event->accept(); } else { QWidget::mousePressEvent(event); } } void Responsive_form::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { QPoint delta = event->globalPos() - m_dragStartPosition; this->move(m_startWindowPosition + delta); event->accept(); } else { QWidget::mouseMoveEvent(event); } } int Responsive_form::animation_duration() const { return m_animation_duration; } void Responsive_form::setAnimation_duration(int newAnimation_duration) { m_animation_duration = newAnimation_duration; } ================================================ FILE: Anime_Template_Project/Login_interface/responsive_form.h ================================================ #ifndef RESPONSIVE_FORM_H #define RESPONSIVE_FORM_H #include #include #include #include #include #include "scroll_bar.h" #include "transparent_transition_interface.h" #include "registration_form.h" #include "login_form.h" class Responsive_form : public QWidget { Q_OBJECT Q_PROPERTY(int animation_duration READ animation_duration WRITE setAnimation_duration FINAL) public: explicit Responsive_form(QWidget *parent = nullptr); Scroll_bar* scroll_bar; Transparent_transition_interface *transparent_transition_interface; Transparent_transition_interface* transparent_transition_interface2; Registration_form* registration_form; Login_form* login_form; void build_animation(); QPropertyAnimation* animation2; QPropertyAnimation* animation3; QPropertyAnimation* animation4; QPropertyAnimation* animation5; QPropertyAnimation* animation6; int animation_duration() const; void setAnimation_duration(int newAnimation_duration); void updateMask(); void createRoundPath(QPainterPath& path); void crop_corner(); protected: void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void resizeEvent(QResizeEvent* event); public slots: void setRightShow(); void execute_animation(Hollow_button::AnimationState status); void onAnimation3Finished(); void onAnimation4Finished(); private: QPoint m_dragStartPosition; QPoint m_startWindowPosition; int currentSequence = 1; bool animation_execute_duration = false; bool animation_restore_duration = false; int m_animation_duration = 600; }; #endif // RESPONSIVE_FORM_H ================================================ FILE: Anime_Template_Project/Login_interface/scroll_bar.cpp ================================================ #include "scroll_bar.h" Scroll_bar::Scroll_bar(QWidget *parent) : QWidget{parent} { this->resize(1910, 620); this->setWindowFlags(Qt::FramelessWindowHint); this->setAttribute(Qt::WA_TranslucentBackground); } void Scroll_bar::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); this->crop_corner(); } void Scroll_bar::crop_corner() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), height() / 4, height() / 4); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush Brush(QColor(123, 150, 228, 255)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); } ================================================ FILE: Anime_Template_Project/Login_interface/scroll_bar.h ================================================ #ifndef SCROLL_BAR_H #define SCROLL_BAR_H #include #include #include #include class Scroll_bar : public QWidget { Q_OBJECT public: explicit Scroll_bar(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event); public: void crop_corner(); }; #endif // SCROLL_BAR_H ================================================ FILE: Anime_Template_Project/Login_interface/transparent_transition_interface.cpp ================================================ #include "transparent_transition_interface.h" Transparent_transition_interface::Transparent_transition_interface(QString large_text, QString small_text, QString btn_text, QWidget *parent) : QWidget{parent} { this->large_text = large_text; this->small_text = small_text; this->resize(477, 620); button = new Hollow_button(this); button->setCenter_text(btn_text); QRect rect1(0, 0, 180, 55); rect1.moveCenter(QPoint(width() / 2, height() * 0.59)); button->setGeometry(rect1); } void Transparent_transition_interface::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); painter.setViewport(0, 0, 477, 620); painter.setWindow(0, 0, 477, 620); this->draw_text(); this->draw_text2(); } void Transparent_transition_interface::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, 0, 0); QFont font1; font1.setPointSize(29); font1.setBold(true); font1.setWordSpacing(1); painter.setFont(font1); QColor semiTransparent(255, 255, 255, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, large_text); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() * 0.41)); painter.drawText(rect1, large_text); } void Transparent_transition_interface::draw_text2() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); QRect rect1(0, 0, 0, 0); QFont font1; font1.setPointSize(11); font1.setBold(true); font1.setWordSpacing(1); painter.setFont(font1); QColor semiTransparent(255, 255, 255, 255); painter.setPen(semiTransparent); QRect actualRect = painter.boundingRect(rect1, Qt::AlignCenter, small_text); rect1.setHeight(actualRect.height()); rect1.setWidth(actualRect.width()); rect1.moveCenter(QPoint(width() / 2, height() * 0.49)); painter.drawText(rect1, small_text); } ================================================ FILE: Anime_Template_Project/Login_interface/transparent_transition_interface.h ================================================ #ifndef TRANSPARENT_TRANSITION_INTERFACE_H #define TRANSPARENT_TRANSITION_INTERFACE_H #include #include #include #include #include #include "hollow_button.h" class Transparent_transition_interface : public QWidget { Q_OBJECT public: explicit Transparent_transition_interface(QString large_text, QString small_text, QString btn_text, QWidget *parent = nullptr); void draw_text(); void draw_text2(); Hollow_button *button; protected: void paintEvent(QPaintEvent* event); private: QString large_text; QString small_text; }; #endif // TRANSPARENT_TRANSITION_INTERFACE_H ================================================ FILE: Anime_Template_Project/Magnet_Lines/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.19) project(untitled3 LANGUAGES CXX) find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets) qt_standard_project_setup() qt_add_executable(untitled3 WIN32 MACOSX_BUNDLE main.cpp RotatableLinesWidget.cpp RotatableLinesWidget.h ) target_link_libraries(untitled3 PRIVATE Qt::Core Qt::Widgets ) include(GNUInstallDirs) install(TARGETS untitled3 BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) qt_generate_deploy_app_script( TARGET untitled3 OUTPUT_SCRIPT deploy_script NO_UNSUPPORTED_PLATFORM_ERROR ) install(SCRIPT ${deploy_script}) ================================================ FILE: Anime_Template_Project/Magnet_Lines/RotatableLinesWidget.cpp ================================================ #include "RotatableLinesWidget.h" #include #include #include #include RotatableLinesWidget::RotatableLinesWidget(QWidget *parent) : QWidget(parent), m_linesPerRow(10), m_linesPerCol(10), m_baseRotationAngle(0), m_globalMousePos(-1, -1), m_padding(20) { QPalette pal = this->palette(); pal.setColor(QPalette::Window, QColor(0,0,0,255)); this->setPalette(pal); setMinimumSize(100, 100); m_mousePollTimer = new QTimer(this); connect(m_mousePollTimer, &QTimer::timeout, this, &RotatableLinesWidget::updateMousePosition); m_mousePollTimer->start(5); } RotatableLinesWidget::~RotatableLinesWidget() { if (m_mousePollTimer->isActive()) { m_mousePollTimer->stop(); } } void RotatableLinesWidget::setLinesPerRow(int count) { if (m_linesPerRow != count && count > 0) { m_linesPerRow = count; update(); } } void RotatableLinesWidget::setLinesPerCol(int count) { if (m_linesPerCol != count && count > 0) { m_linesPerCol = count; update(); } } void RotatableLinesWidget::setRotationAngle(int angle) { if (m_baseRotationAngle != angle) { m_baseRotationAngle = angle; } } void RotatableLinesWidget::updateMousePosition() { QPoint globalPos = QCursor::pos(); if (globalPos != m_globalMousePos) { m_globalMousePos = globalPos; update(); } } void RotatableLinesWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); update(); QWidget::resizeEvent(event); } void RotatableLinesWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); QPen pen(QColor("#00bcd4")); pen.setWidth(2); painter.setPen(pen); qreal usableWidth = width() - 2 * m_padding; qreal usableHeight = height() - 2 * m_padding; if (usableWidth <= 0 || usableHeight <= 0) { return; } qreal cellHeight = usableHeight / m_linesPerCol; qreal actualLineLength = cellHeight * 0.7; if (actualLineLength < 5) actualLineLength = 5; qreal hSpacing = (m_linesPerRow > 1) ? usableWidth / (m_linesPerRow - 1) : 0; if (m_linesPerRow == 1) hSpacing = 0; for (int col = 0; col < m_linesPerRow; ++col) { for (int row = 0; row < m_linesPerCol; ++row) { qreal centerX = m_padding + col * hSpacing; qreal centerY = m_padding + row * cellHeight + (actualLineLength / 2.0); QPointF lineCenterLocal(centerX, centerY); qreal currentRotationAngle = m_baseRotationAngle; if (m_globalMousePos != QPoint(-1, -1)) { QPoint lineCenterGlobal = mapToGlobal(QPoint(lineCenterLocal.x(), lineCenterLocal.y())); qreal dx = m_globalMousePos.x() - lineCenterGlobal.x(); qreal dy = m_globalMousePos.y() - lineCenterGlobal.y(); qreal angleRad = std::atan2(dy, dx); currentRotationAngle = qRadiansToDegrees(angleRad) - 180; } painter.save(); painter.translate(lineCenterLocal); painter.rotate(currentRotationAngle); painter.drawLine(0, -actualLineLength / 2, 0, actualLineLength / 2); painter.restore(); } } } ================================================ FILE: Anime_Template_Project/Magnet_Lines/RotatableLinesWidget.h ================================================ #ifndef ROTATABLELINESWIDGET_H #define ROTATABLELINESWIDGET_H #include #include #include #include #include class RotatableLinesWidget : public QWidget { Q_OBJECT public: explicit RotatableLinesWidget(QWidget *parent = nullptr); ~RotatableLinesWidget(); void setLinesPerRow(int count); void setLinesPerCol(int count); void setRotationAngle(int angle); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void updateMousePosition(); private: int m_linesPerRow; int m_linesPerCol; int m_baseRotationAngle; QPoint m_globalMousePos; QTimer *m_mousePollTimer; qreal m_padding; }; #endif ================================================ FILE: Anime_Template_Project/Magnet_Lines/main.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "RotatableLinesWidget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; window.resize(800, 600); QPalette pal = window.palette(); pal.setColor(QPalette::Window, QColor(0,0,0,255)); window.setPalette(pal); QWidget *centralWidget = new QWidget(&window); window.setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); mainLayout->setContentsMargins(10, 10, 10, 10); RotatableLinesWidget *linesWidget = new RotatableLinesWidget(centralWidget); mainLayout->addWidget(linesWidget); QGroupBox *controlGroup = new QGroupBox("控制面板", centralWidget); controlGroup->setMaximumHeight(200); QHBoxLayout *controlLayout = new QHBoxLayout(controlGroup); controlLayout->addWidget(new QLabel("行数:", controlGroup)); QSpinBox *rowsSpinBox = new QSpinBox(controlGroup); rowsSpinBox->setRange(1, 99); rowsSpinBox->setValue(10); QObject::connect(rowsSpinBox, QOverload::of(&QSpinBox::valueChanged), linesWidget, &RotatableLinesWidget::setLinesPerCol); controlLayout->addWidget(rowsSpinBox); controlLayout->addWidget(new QLabel("列数:", controlGroup)); QSpinBox *colsSpinBox = new QSpinBox(controlGroup); colsSpinBox->setRange(1, 99); colsSpinBox->setValue(10); QObject::connect(colsSpinBox, QOverload::of(&QSpinBox::valueChanged), linesWidget, &RotatableLinesWidget::setLinesPerRow); controlLayout->addWidget(colsSpinBox); controlLayout->addStretch(1); mainLayout->addWidget(controlGroup); window.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/Mimic_Button/depressed_button.cpp ================================================ #include "depressed_button.h" Depressed_Button::Depressed_Button(QString m_Function_Button_pixmap, QWidget* parent) : QPushButton{ parent } { this->setCursor(Qt::PointingHandCursor); this->resize(150, 150); this->m_Function_Button_pixmap = QPixmap(m_Function_Button_pixmap); animation = new QPropertyAnimation(this, "clor"); animation->setDuration(200); animation->setStartValue(m_clor); animation->setEndValue(QColor(135, 135, 135, 115)); animation1 = new QPropertyAnimation(this, "SF"); animation1->setDuration(200); animation1->setStartValue(m_SF); animation1->setEndValue(0.55); } void Depressed_Button::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); QPainterPath path; path.addRoundedRect(this->rect(), 35, 35); painter.setClipPath(path); painter.setPen(Qt::NoPen); painter.setBrush(QColor(255, 255, 255, 255)); painter.drawRect(this->rect()); QLinearGradient gradient(QPointF(-width() * 0.1, -height() * 0.1), QPointF(width() * 1.0, height() * 1.0)); gradient.setColorAt(0, m_clor); gradient.setColorAt(0.86, QColor(255, 255, 255, 255)); gradient.setColorAt(1.0, QColor(255, 255, 255, 255)); QPen pen; pen.setWidth(74); pen.setBrush(gradient); QImage blur_image(this->size(), QImage::Format_ARGB32); blur_image.fill(QColor(0, 0, 0, 0)); QPainter image_painter(&blur_image); image_painter.setRenderHint(QPainter::Antialiasing); image_painter.setBrush(gradient); image_painter.setPen(Qt::NoPen); image_painter.drawRoundedRect(this->rect(), 10, 10); image_painter.end(); painter.drawImage(this->rect(), blur_image); painter.setPen(Qt::NoPen); painter.setBrush(Qt::NoBrush); QRect rect; rect.setHeight(this->height() * m_SF); rect.setWidth(this->height() * m_SF); rect.moveCenter(QPoint(this->rect().center().x(), this->rect().center().y())); painter.drawPixmap(rect, m_Function_Button_pixmap); } bool Depressed_Button::event(QEvent *event) { if (event->type() == QEvent::Enter) { emit Animation_Trigger_State(true); animation->setDirection(QAbstractAnimation::Forward); animation->start(); animation1->setDirection(QAbstractAnimation::Forward); animation1->start(); } else if (event->type() == QEvent::Leave) { emit Animation_Trigger_State(false); animation->setDirection(QAbstractAnimation::Backward); animation->start(); animation1->setDirection(QAbstractAnimation::Backward); animation1->start(); } return QPushButton::event(event); } qreal Depressed_Button::JB() const { return m_JB; } void Depressed_Button::setJB(qreal newJB) { if (qFuzzyCompare(m_JB, newJB)) return; m_JB = newJB; emit JBChanged(); update(); } QColor Depressed_Button::clor() const { return m_clor; } void Depressed_Button::setClor(const QColor &newClor) { if (m_clor == newClor) return; m_clor = newClor; emit clorChanged(); update(); } qreal Depressed_Button::SF() const { return m_SF; } void Depressed_Button::setSF(qreal newSF) { if (qFuzzyCompare(m_SF, newSF)) return; m_SF = newSF; emit SFChanged(); update(); } ================================================ FILE: Anime_Template_Project/Mimic_Button/depressed_button.h ================================================ #ifndef DEPRESSED_BUTTON_H #define DEPRESSED_BUTTON_H #include #include #include #include #include #include class Depressed_Button : public QPushButton { Q_OBJECT Q_PROPERTY(qreal JB READ JB WRITE setJB NOTIFY JBChanged FINAL) Q_PROPERTY(QColor clor READ clor WRITE setClor NOTIFY clorChanged FINAL) Q_PROPERTY(qreal SF READ SF WRITE setSF NOTIFY SFChanged FINAL) public: explicit Depressed_Button(QString m_Function_Button_pixmap, QWidget* parent = nullptr); QPropertyAnimation* animation; QPropertyAnimation* animation1; qreal JB() const; void setJB(qreal newJB); QColor clor() const; void setClor(const QColor &newClor); qreal SF() const; void setSF(qreal newSF); signals: void JBChanged(); void clorChanged(); void Animation_Trigger_State(bool x); void SFChanged(); protected: void paintEvent(QPaintEvent* event); bool event(QEvent* event); private: QPixmap m_Function_Button_pixmap; QColor m_Bottom_Edge_Color = QColor(241, 245, 249, 255); qreal m_JB = 0.0; QColor m_clor = QColor(235, 235, 235, 235); qreal m_SF = 0.60; }; #endif // DEPRESSED_BUTTON_H ================================================ FILE: Anime_Template_Project/Mimic_Button/img.qrc ================================================ img/twitter.png img/哔哩哔哩.png img/电话.png img/短信.png img/浏览器.png img/录音.png img/日历.png img/通讯录.png img/微信.png ================================================ FILE: Anime_Template_Project/Mimic_Button/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { this->resize(1111, 1111); xxxx1 = new Depressed_Button(":/new/prefix1/img/twitter.png", this); connect(xxxx1, &Depressed_Button::Animation_Trigger_State, this, &Widget::Animation_1); xxxx1->move(255, 455); xxxx1->show(); xxxx2 = new Depressed_Button(":/new/prefix1/img/微信.png", this); connect(xxxx2, &Depressed_Button::Animation_Trigger_State, this, &Widget::Animation_2); xxxx2->move(655, 455); xxxx2->show(); animation1 = new QPropertyAnimation(this, "MH"); animation1->setDuration(200); animation1->setStartValue(m_MH); animation1->setEndValue(0); animation2 = new QPropertyAnimation(this, "KJ"); animation2->setDuration(200); animation2->setStartValue(m_KJ); animation2->setEndValue(0); animation3 = new QPropertyAnimation(this, "MH1"); animation3->setDuration(200); animation3->setStartValue(m_MH1); animation3->setEndValue(0); animation4 = new QPropertyAnimation(this, "KJ1"); animation4->setDuration(200); animation4->setStartValue(m_KJ1); animation4->setEndValue(0); } Widget::~Widget() {} void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(239, 239, 239, 255)); painter.setPen(Qt::NoPen); painter.drawRoundedRect(0, 0, width(), height(), 30, 30); QImage blur_image(QSize(xxxx1->width() * 3, xxxx1->height() * 3), QImage::Format_ARGB32); blur_image.fill(QColor(0, 0, 0, 0)); QPainter image_painter(&blur_image); image_painter.setRenderHint(QPainter::Antialiasing); image_painter.setPen(Qt::NoPen); image_painter.setBrush(QColor(255, 255, 255, 255)); image_painter.drawRoundedRect(QRect(QPoint(xxxx1->width() - m_KJ, xxxx1->height() - m_KJ), QSize(xxxx1->width(), xxxx1->height())), 35, 35); image_painter.setBrush(QColor(114, 114, 114, 85)); image_painter.drawRoundedRect(QRect(QPoint(xxxx1->width() + m_KJ, xxxx1->height() + m_KJ), QSize(xxxx1->width(), xxxx1->height())), 35, 35); image_painter.end(); painter.drawImage(QRect(xxxx1->geometry().x() - xxxx1->width(), xxxx1->geometry().y() - xxxx1->height(), xxxx1->width() * 3, xxxx1->height() * 3), blur_image); QImage blur_image1(QSize(xxxx2->width() * 3, xxxx2->height() * 3), QImage::Format_ARGB32); blur_image1.fill(QColor(0, 0, 0, 0)); QPainter image_painter1(&blur_image1); image_painter1.setRenderHint(QPainter::Antialiasing); image_painter1.setPen(Qt::NoPen); image_painter1.setBrush(QColor(255, 255, 255, 255)); image_painter1.drawRoundedRect(QRect(QPoint(xxxx2->width() - m_KJ1, xxxx2->height() - m_KJ1), QSize(xxxx2->width(), xxxx2->height())), 35, 35); image_painter1.setBrush(QColor(114, 114, 114, 85)); image_painter1.drawRoundedRect(QRect(QPoint(xxxx2->width() + m_KJ1, xxxx2->height() + m_KJ1), QSize(xxxx2->width(), xxxx2->height())), 35, 35); image_painter1.end(); painter.drawImage(QRect(xxxx2->geometry().x() - xxxx2->width(), xxxx2->geometry().y() - xxxx2->height(), xxxx2->width() * 3, xxxx2->height() * 3), blur_image1); } void Widget::Animation_1(bool x) { if (x) { animation1->setDirection(QAbstractAnimation::Forward); animation1->start(); animation2->setDirection(QAbstractAnimation::Forward); animation2->start(); } else { animation1->setDirection(QAbstractAnimation::Backward); animation1->start(); animation2->setDirection(QAbstractAnimation::Backward); animation2->start(); } } void Widget::Animation_2(bool x) { if (x) { animation3->setDirection(QAbstractAnimation::Forward); animation3->start(); animation4->setDirection(QAbstractAnimation::Forward); animation4->start(); } else { animation3->setDirection(QAbstractAnimation::Backward); animation3->start(); animation4->setDirection(QAbstractAnimation::Backward); animation4->start(); } } int Widget::MH() const { return m_MH; } void Widget::setMH(int newMH) { if (m_MH == newMH) return; m_MH = newMH; emit MHChanged(); update(); } int Widget::KJ() const { return m_KJ; } void Widget::setKJ(int newKJ) { if (m_KJ == newKJ) return; m_KJ = newKJ; emit KJChanged(); update(); } int Widget::MH1() const { return m_MH1; } void Widget::setMH1(int newMH1) { m_MH1 = newMH1; update(); } int Widget::KJ1() const { return m_KJ1; } void Widget::setKJ1(int newKJ1) { m_KJ1 = newKJ1; update(); } ================================================ FILE: Anime_Template_Project/Mimic_Button/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include #include #include #include "depressed_button.h" class Widget : public QWidget { Q_OBJECT Q_PROPERTY(int MH READ MH WRITE setMH NOTIFY MHChanged FINAL) Q_PROPERTY(int KJ READ KJ WRITE setKJ NOTIFY KJChanged FINAL) Q_PROPERTY(int MH1 READ MH1 WRITE setMH1 FINAL) Q_PROPERTY(int KJ1 READ KJ1 WRITE setKJ1 FINAL) public: Widget(QWidget *parent = nullptr); ~Widget(); int MH() const; void setMH(int newMH); int KJ() const; void setKJ(int newKJ); int MH1() const; void setMH1(int newMH1); int KJ1() const; void setKJ1(int newKJ1); public slots: void Animation_1(bool x); void Animation_2(bool x); signals: void MHChanged(); void KJChanged(); protected: void paintEvent(QPaintEvent* event) override; private: Depressed_Button* xxxx1; Depressed_Button* xxxx2; QPropertyAnimation* animation1; QPropertyAnimation* animation2; QPropertyAnimation* animation3; QPropertyAnimation* animation4; int m_MH = 155; int m_KJ = 30; int m_MH1 = 155; int m_KJ1 = 30; }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/Particle_Generation/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.19) project(Particle_Generation LANGUAGES CXX) find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets) find_package(Qt6 REQUIRED COMPONENTS Widgets) qt_standard_project_setup() include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/include ) link_directories( ${CMAKE_CURRENT_SOURCE_DIR}/lib ) qt_add_executable(Particle_Generation WIN32 MACOSX_BUNDLE main.cpp mainwindow.cpp mainwindow.h particlewidget.h particlewidget.cpp ) target_link_libraries(Particle_Generation PRIVATE Qt::Core Qt::Widgets ) target_link_libraries(Particle_Generation PRIVATE # opencv_world470d.lib opencv_world470.lib ) target_link_libraries(Particle_Generation PRIVATE Qt6::Widgets) include(GNUInstallDirs) install(TARGETS Particle_Generation BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) qt_generate_deploy_app_script( TARGET Particle_Generation OUTPUT_SCRIPT deploy_script NO_UNSUPPORTED_PLATFORM_ERROR ) install(SCRIPT ${deploy_script}) ================================================ FILE: Anime_Template_Project/Particle_Generation/ParticleWidget.h ================================================ #ifndef PARTICLEWIDGET_H #define PARTICLEWIDGET_H #include #include #include #include #include #include #include #include #include #include #include struct MovingDot { QPoint currentPos; cv::Point originalImgPos; QPoint startPos; qreal progress; bool reached; bool isRepulsed; QPoint repulsionStartPos; QElapsedTimer repulsionElapsedTimer; }; class ParticleWidget : public QWidget { Q_OBJECT public: explicit ParticleWidget(QWidget *parent = nullptr); ~ParticleWidget(); bool loadImage(const std::string &imagePath); void processAndResizeImage(int targetHeight); void setDotColor(const QColor& color); void setDotSize(int size); void setRepulsionRadius(int radius); void setRepulsionStrength(qreal strength); void setRepositioningDuration(int duration); bool hasOriginalImage() const { return !originalImage.empty(); } QColor getDotColor() const { return m_dotColor; } bool isImageLoaded() const { return !originalImage.empty(); } protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; private slots: void updateAnimation(); private: QImage cvMatToQImage(const cv::Mat &mat); void startDotAnimation(); QPoint mapProcessedToWidget(const cv::Point& p) const; private: std::vector whitePixels; cv::Mat processedImage; cv::Mat originalImage; QImage displayImage; QColor m_dotColor; int m_dotSize; QTimer *m_animationTimer; std::vector m_movingDots; int m_animationDuration; QElapsedTimer m_elapsedTimer; bool m_animationRunning; QPoint m_mousePos; int m_repulsionRadius; qreal m_repulsionStrength; int m_repositioningDuration; }; #endif // PARTICLEWIDGET_H ================================================ FILE: Anime_Template_Project/Particle_Generation/main.cpp ================================================ #include #include "MainWindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/Particle_Generation/mainwindow.cpp ================================================ #include "mainwindow.h" #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); imageProcessorWidget = new ParticleWidget(this); imageProcessorWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mainLayout->addWidget(imageProcessorWidget); QWidget *controlPanelWidget = new QWidget(this); QVBoxLayout *controlPanelLayout = new QVBoxLayout(controlPanelWidget); controlPanelLayout->setContentsMargins(0, 0, 0, 0); QHBoxLayout *controlLayoutTop = new QHBoxLayout(); controlPanelLayout->addLayout(controlLayoutTop); loadImageButton = new QPushButton("加载图片", this); controlLayoutTop->addWidget(loadImageButton); connect(loadImageButton, &QPushButton::clicked, this, &MainWindow::onLoadImageClicked); processButton = new QPushButton("处理并压缩", this); controlLayoutTop->addWidget(processButton); processButton->setEnabled(false); connect(processButton, &QPushButton::clicked, [this]() { imageProcessorWidget->processAndResizeImage(heightSlider->value()); }); heightSlider = new QSlider(Qt::Horizontal, this); heightSlider->setRange(100, 1080); heightSlider->setValue(480); heightSlider->setSingleStep(10); heightSlider->setPageStep(50); heightSlider->setTickPosition(QSlider::TicksBelow); heightSlider->setTickInterval(100); controlLayoutTop->addWidget(heightSlider); heightLabel = new QLabel(QString("高度: %1px").arg(heightSlider->value()), this); controlLayoutTop->addWidget(heightLabel); connect(heightSlider, &QSlider::valueChanged, this, &MainWindow::onHeightSliderValueChanged); QHBoxLayout *controlLayoutMiddle = new QHBoxLayout(); controlPanelLayout->addLayout(controlLayoutMiddle); chooseColorButton = new QPushButton("选择点颜色", this); controlLayoutMiddle->addWidget(chooseColorButton); connect(chooseColorButton, &QPushButton::clicked, this, &MainWindow::onChooseColorClicked); dotSizeSlider = new QSlider(Qt::Horizontal, this); dotSizeSlider->setRange(1, 10); dotSizeSlider->setValue(2); dotSizeSlider->setSingleStep(1); dotSizeSlider->setTickPosition(QSlider::TicksBelow); dotSizeSlider->setTickInterval(1); controlLayoutMiddle->addWidget(dotSizeSlider); dotSizeLabel = new QLabel(QString("点大小: %1px").arg(dotSizeSlider->value()), this); controlLayoutMiddle->addWidget(dotSizeLabel); connect(dotSizeSlider, &QSlider::valueChanged, this, &MainWindow::onDotSizeSliderValueChanged); QHBoxLayout *controlLayoutBottom = new QHBoxLayout(); controlPanelLayout->addLayout(controlLayoutBottom); repulsionRadiusSlider = new QSlider(Qt::Horizontal, this); repulsionRadiusSlider->setRange(10, 200); repulsionRadiusSlider->setValue(80); repulsionRadiusSlider->setSingleStep(5); repulsionRadiusSlider->setTickPosition(QSlider::TicksBelow); repulsionRadiusSlider->setTickInterval(20); controlLayoutBottom->addWidget(repulsionRadiusSlider); repulsionRadiusLabel = new QLabel(QString("排斥半径: %1px").arg(repulsionRadiusSlider->value()), this); controlLayoutBottom->addWidget(repulsionRadiusLabel); connect(repulsionRadiusSlider, &QSlider::valueChanged, this, &MainWindow::onRepulsionRadiusSliderValueChanged); repulsionStrengthSlider = new QSlider(Qt::Horizontal, this); repulsionStrengthSlider->setRange(0, 100); repulsionStrengthSlider->setValue(20); repulsionStrengthSlider->setSingleStep(1); repulsionStrengthSlider->setTickPosition(QSlider::TicksBelow); repulsionStrengthSlider->setTickInterval(10); controlLayoutBottom->addWidget(repulsionStrengthSlider); repulsionStrengthLabel = new QLabel(QString("排斥强度: %1").arg(repulsionStrengthSlider->value()), this); controlLayoutBottom->addWidget(repulsionStrengthLabel); connect(repulsionStrengthSlider, &QSlider::valueChanged, this, &MainWindow::onRepulsionStrengthSliderValueChanged); mainLayout->addWidget(controlPanelWidget); resize(900, 700); imageProcessorWidget->setRepulsionRadius(repulsionRadiusSlider->value()); imageProcessorWidget->setRepulsionStrength(repulsionStrengthSlider->value()); } MainWindow::~MainWindow() {} void MainWindow::onLoadImageClicked() { QString filePath = QFileDialog::getOpenFileName( this, "选择一张图片", lastOpenedDirPath, "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif)" ); if (!filePath.isEmpty()) { lastOpenedDirPath = QFileInfo(filePath).absolutePath(); if (imageProcessorWidget->loadImage(filePath.toStdString())) { processButton->setEnabled(true); imageProcessorWidget->setDotColor(imageProcessorWidget->getDotColor()); imageProcessorWidget->setDotSize(dotSizeSlider->value()); imageProcessorWidget->setRepulsionRadius(repulsionRadiusSlider->value()); imageProcessorWidget->setRepulsionStrength(repulsionStrengthSlider->value()); imageProcessorWidget->processAndResizeImage(heightSlider->value()); } else { processButton->setEnabled(false); } } } void MainWindow::onHeightSliderValueChanged(int value) { heightLabel->setText(QString("高度: %1px").arg(value)); if (imageProcessorWidget->hasOriginalImage()) { imageProcessorWidget->processAndResizeImage(value); } } void MainWindow::onChooseColorClicked() { QColor initialColor = imageProcessorWidget->getDotColor(); QColor color = QColorDialog::getColor(initialColor, this, "选择点颜色"); if (color.isValid()) { imageProcessorWidget->setDotColor(color); } } void MainWindow::onDotSizeSliderValueChanged(int value) { dotSizeLabel->setText(QString("点大小: %1px").arg(value)); imageProcessorWidget->setDotSize(value); } void MainWindow::onRepulsionRadiusSliderValueChanged(int value) { repulsionRadiusLabel->setText(QString("排斥半径: %1px").arg(value)); imageProcessorWidget->setRepulsionRadius(value); } void MainWindow::onRepulsionStrengthSliderValueChanged(int value) { repulsionStrengthLabel->setText(QString("排斥强度: %1").arg(value)); imageProcessorWidget->setRepulsionStrength(static_cast(value)); } ================================================ FILE: Anime_Template_Project/Particle_Generation/mainwindow.h ================================================ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include #include #include #include #include #include #include "ParticleWidget.h" class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onLoadImageClicked(); void onHeightSliderValueChanged(int value); void onChooseColorClicked(); void onDotSizeSliderValueChanged(int value); void onRepulsionRadiusSliderValueChanged(int value); void onRepulsionStrengthSliderValueChanged(int value); private: ParticleWidget *imageProcessorWidget; QPushButton *loadImageButton; QSlider *heightSlider; QLabel *heightLabel; QPushButton *processButton; QPushButton *chooseColorButton; QSlider *dotSizeSlider; QLabel *dotSizeLabel; QSlider *repulsionRadiusSlider; QLabel *repulsionRadiusLabel; QSlider *repulsionStrengthSlider; QLabel *repulsionStrengthLabel; QString lastOpenedDirPath; }; #endif // MAINWINDOW_H ================================================ FILE: Anime_Template_Project/Particle_Generation/particlewidget.cpp ================================================ #include "ParticleWidget.h" #include #include #include #include #include #include #include #include ParticleWidget::ParticleWidget(QWidget *parent) : QWidget(parent), m_dotColor(Qt::red), m_dotSize(2), m_animationTimer(new QTimer(this)), m_animationDuration(1000), m_animationRunning(false), m_repulsionRadius(80), m_repulsionStrength(20.0), m_repositioningDuration(333) { setMinimumSize(640, 480); connect(m_animationTimer, &QTimer::timeout, this, &ParticleWidget::updateAnimation); setMouseTracking(true); } ParticleWidget::~ParticleWidget() { } void ParticleWidget::setDotColor(const QColor& color) { m_dotColor = color; update(); } void ParticleWidget::setDotSize(int size) { if (size > 0) { m_dotSize = size; update(); } } void ParticleWidget::setRepulsionRadius(int radius) { if (radius >= 0) { m_repulsionRadius = radius; update(); } } void ParticleWidget::setRepulsionStrength(qreal strength) { m_repulsionStrength = strength; if (m_animationTimer->isActive()) { update(); } } void ParticleWidget::setRepositioningDuration(int duration) { if (duration >= 0) { m_repositioningDuration = duration; } } bool ParticleWidget::loadImage(const std::string& imagePath) { cv::Mat img = cv::imread(imagePath); if (img.empty()) { qWarning("无法加载图片或图片不存在: %s", imagePath.c_str()); originalImage = cv::Mat(); processedImage = cv::Mat(); displayImage = QImage(); whitePixels.clear(); m_movingDots.clear(); m_animationTimer->stop(); m_animationRunning = false; update(); return false; } originalImage = img.clone(); qDebug() << "图片加载成功:" << imagePath.c_str(); return true; } void ParticleWidget::processAndResizeImage(int targetHeight) { if (originalImage.empty()) { qWarning("未加载原始图片,无法进行处理和压缩。"); processedImage = cv::Mat(); displayImage = QImage(); whitePixels.clear(); update(); return; } double aspectRatio = (double)originalImage.cols / originalImage.rows; int targetWidth = static_cast(targetHeight * aspectRatio); cv::Mat resizedImg; cv::resize(originalImage, resizedImg, cv::Size(targetWidth, targetHeight)); cv::Mat imgGray, imgBlur, imgCanny, imgDil, imgErode; cvtColor(resizedImg, imgGray, cv::COLOR_BGR2GRAY); GaussianBlur(resizedImg, imgBlur, cv::Size(3, 3), 3, 0); Canny(imgBlur, imgCanny, 25, 75); cv::Mat kernel = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); dilate(imgCanny, imgDil, kernel); erode(imgDil, imgErode, kernel); cv::threshold(imgErode, imgErode, 127, 255, cv::THRESH_BINARY); processedImage = imgErode.clone(); whitePixels.clear(); int step = 1; if (processedImage.rows > 0) { int baseStep = 2; step = baseStep; } for (int y = 0; y < processedImage.rows; y += step) { for (int x = 0; x < processedImage.cols; x += step) { if (processedImage.at(y, x) == 255) { whitePixels.push_back(cv::Point(x, y)); } } } qDebug() << "处理并压缩后的图片尺寸: " << processedImage.cols << "x" << processedImage.rows; qDebug() << "找到白色像素数量:" << whitePixels.size(); displayImage = cvMatToQImage(processedImage); startDotAnimation(); update(); } QPoint ParticleWidget::mapProcessedToWidget(const cv::Point& p) const { if (processedImage.empty() || width() == 0 || height() == 0) { return QPoint(0, 0); } double widgetAspectRatio = static_cast(width()) / height(); double imageAspectRatio = static_cast(processedImage.cols) / processedImage.rows; int drawWidth, drawHeight; if (widgetAspectRatio > imageAspectRatio) { drawHeight = height(); drawWidth = static_cast(drawHeight * imageAspectRatio); } else { drawWidth = width(); drawHeight = static_cast(drawWidth / imageAspectRatio); } int startX = (width() - drawWidth) / 2; int startY = (height() - drawHeight) / 2; double scaleX = static_cast(drawWidth) / processedImage.cols; double scaleY = static_cast(drawHeight) / processedImage.rows; return QPoint(startX + static_cast(p.x * scaleX), startY + static_cast(p.y * scaleY)); } void ParticleWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(rect(), Qt::black); if (!displayImage.isNull()) { if (m_animationRunning || !m_movingDots.empty()) { painter.setPen(Qt::NoPen); painter.setBrush(QBrush(m_dotColor)); for (const MovingDot& dot : m_movingDots) { painter.drawEllipse(dot.currentPos.x() - m_dotSize / 2, dot.currentPos.y() - m_dotSize / 2, m_dotSize, m_dotSize); } } } } void ParticleWidget::startDotAnimation() { if (whitePixels.empty() || processedImage.empty()) { m_movingDots.clear(); m_animationTimer->stop(); m_animationRunning = false; qDebug() << "无目标像素,粒子清空。"; return; } std::set> targetPixelSet; for (const cv::Point& p : whitePixels) { targetPixelSet.insert({p.x, p.y}); } std::vector dotShouldRetain(m_movingDots.size(), false); std::set> matchedTargetPixels; for (size_t i = 0; i < m_movingDots.size(); ++i) { const auto& dot = m_movingDots[i]; std::pair currentDotOriginalPos = {dot.originalImgPos.x, dot.originalImgPos.y}; if (targetPixelSet.count(currentDotOriginalPos) && !matchedTargetPixels.count(currentDotOriginalPos)) { dotShouldRetain[i] = true; matchedTargetPixels.insert(currentDotOriginalPos); } } std::vector newMovingDots; for (size_t i = 0; i < m_movingDots.size(); ++i) { if (dotShouldRetain[i]) { newMovingDots.push_back(m_movingDots[i]); newMovingDots.back().isRepulsed = false; newMovingDots.back().repulsionElapsedTimer.invalidate(); newMovingDots.back().startPos = newMovingDots.back().currentPos; newMovingDots.back().progress = 0.0; newMovingDots.back().reached = false; } } for (const cv::Point& targetCvPoint : whitePixels) { std::pair currentTarget = {targetCvPoint.x, targetCvPoint.y}; if (!matchedTargetPixels.count(currentTarget)) { MovingDot dot; dot.originalImgPos = targetCvPoint; dot.startPos.setX(QRandomGenerator::global()->bounded(width())); dot.startPos.setY(QRandomGenerator::global()->bounded(height())); dot.currentPos = dot.startPos; dot.progress = 0.0; dot.reached = false; dot.isRepulsed = false; dot.repulsionElapsedTimer.invalidate(); newMovingDots.push_back(dot); } } m_movingDots = newMovingDots; if (!m_movingDots.empty()) { m_animationRunning = true; m_elapsedTimer.restart(); if (!m_animationTimer->isActive()) { m_animationTimer->start(16); } qDebug() << "动画调整:当前粒子数量:" << m_movingDots.size(); } else { m_animationTimer->stop(); m_animationRunning = false; qDebug() << "动画停止:无粒子需要移动。"; } update(); } void ParticleWidget::updateAnimation() { qint64 mainAnimationElapsed = m_elapsedTimer.elapsed(); bool allDotsReachedTarget = true; for (MovingDot& dot : m_movingDots) { QPoint targetPos = mapProcessedToWidget(dot.originalImgPos); QPoint idealPos; if (!dot.reached) { dot.progress = qMin(1.0, static_cast(mainAnimationElapsed) / m_animationDuration); idealPos.setX(static_cast(dot.startPos.x() + (targetPos.x() - dot.startPos.x()) * dot.progress)); idealPos.setY(static_cast(dot.startPos.y() + (targetPos.y() - dot.startPos.y()) * dot.progress)); if (dot.progress >= 1.0) { idealPos = targetPos; dot.reached = true; } else { allDotsReachedTarget = false; } } else { idealPos = targetPos; } QPoint currentEffectivePos = dot.currentPos; bool inRepulsionRange = false; if (rect().contains(m_mousePos)) { qreal dx_repel = currentEffectivePos.x() - m_mousePos.x(); qreal dy_repel = currentEffectivePos.y() - m_mousePos.y(); qreal distance_repel = std::sqrt(dx_repel*dx_repel + dy_repel*dy_repel); if (distance_repel < m_repulsionRadius && distance_repel > 0.1) { inRepulsionRange = true; dot.isRepulsed = true; dot.repulsionElapsedTimer.invalidate(); qreal repulsionFactor = (m_repulsionRadius - distance_repel) / m_repulsionRadius; qreal strength = m_repulsionStrength * repulsionFactor; qreal normalizedDx = dx_repel / distance_repel; qreal normalizedDy = dy_repel / distance_repel; QPoint displacedPos; displacedPos.setX(static_cast(idealPos.x() + normalizedDx * strength)); displacedPos.setY(static_cast(idealPos.y() + normalizedDy * strength)); dot.currentPos.setX(qBound(0, displacedPos.x(), width())); dot.currentPos.setY(qBound(0, displacedPos.y(), height())); } } if (!inRepulsionRange) { if (dot.isRepulsed) { dot.repulsionStartPos = dot.currentPos; dot.repulsionElapsedTimer.restart(); dot.isRepulsed = false; } if (dot.repulsionElapsedTimer.isValid()) { qint64 repelElapsed = dot.repulsionElapsedTimer.elapsed(); qreal repelProgress = qMin(1.0, static_cast(repelElapsed) / m_repositioningDuration); dot.currentPos.setX(static_cast(dot.repulsionStartPos.x() + (idealPos.x() - dot.repulsionStartPos.x()) * repelProgress)); dot.currentPos.setY(static_cast(dot.repulsionStartPos.y() + (idealPos.y() - dot.repulsionStartPos.y()) * repelProgress)); if (repelProgress >= 1.0) { dot.repulsionElapsedTimer.invalidate(); dot.currentPos = idealPos; } allDotsReachedTarget = false; } else { dot.currentPos = idealPos; } } } update(); } void ParticleWidget::mouseMoveEvent(QMouseEvent *event) { m_mousePos = event->pos(); QWidget::mouseMoveEvent(event); } QImage ParticleWidget::cvMatToQImage(const cv::Mat &mat) { switch (mat.type()) { case CV_8UC4: { return QImage(mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_ARGB32); } case CV_8UC3: { QImage image(mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_RGB888); return image.rgbSwapped(); } case CV_8UC1: { return QImage(mat.data, mat.cols, mat.rows, static_cast(mat.step), QImage::Format_Grayscale8); } default: qWarning("无法将CV::Mat转换为QImage: 未知或不支持的格式"); return QImage(); } } void ParticleWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); startDotAnimation(); update(); } ================================================ FILE: Anime_Template_Project/Physical_Text/DraggableElasticObject.cpp ================================================ #include "draggableelasticobject.h" #include #include #include #include DraggableElasticObject::DraggableElasticObject(QWidget *parent) : QWidget(parent) { setAttribute(Qt::WA_TranslucentBackground); if (parent) { resize(parent->size()); } else { resize(800, 600); } m_physicsTimer = new QTimer(this); connect(m_physicsTimer, &QTimer::timeout, this, &DraggableElasticObject::updatePhysics); m_physicsTimer->start(static_cast(TIME_STEP * 1000)); setMouseTracking(true); } DraggableElasticObject::~DraggableElasticObject() { delete m_physicsTimer; } void DraggableElasticObject::addBlock(const PhysicsBlockProperties& block) { m_blocks.append(block); update(); } void DraggableElasticObject::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); for (const PhysicsBlockProperties& block : m_blocks) { painter.setPen(block.color); painter.setBrush(Qt::NoBrush); painter.drawRect(block.position.x(), block.position.y(), block.size.width(), block.size.height()); if (!block.name.isEmpty()) { QFont font; font.setPointSize(15); font.setBold(true); painter.setFont(font); painter.drawText(QRectF(block.position.x(), block.position.y(), block.size.width(), block.size.height()), Qt::AlignCenter, block.name); } } } void DraggableElasticObject::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { for (int i = 0; i < m_blocks.size(); ++i) { PhysicsBlockProperties& block = m_blocks[i]; QRectF blockRect(block.position, block.size); if (blockRect.contains(event->pos())) { m_isDragging = true; m_draggedBlockIndex = i; m_dragOffset = event->pos() - block.position; m_lastMousePos = event->pos(); m_lastMousePressTime = QDateTime::currentMSecsSinceEpoch(); m_lastMouseVelocity = {0.0, 0.0}; break; } } } } void DraggableElasticObject::mouseMoveEvent(QMouseEvent *event) { if (m_isDragging && m_draggedBlockIndex != -1) { PhysicsBlockProperties& block = m_blocks[m_draggedBlockIndex]; qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); double deltaTime = (currentTime - m_lastMousePressTime) / 1000.0; if (deltaTime > 0) { QPointF currentMouseVelocity = (event->pos() - m_lastMousePos) / deltaTime; m_lastMouseVelocity = currentMouseVelocity; } m_lastMousePos = event->pos(); m_lastMousePressTime = currentTime; update(); } } void DraggableElasticObject::mouseReleaseEvent(QMouseEvent *event) { if (m_isDragging && m_draggedBlockIndex != -1) { PhysicsBlockProperties& block = m_blocks[m_draggedBlockIndex]; block.isMovable = true; double throwFactor = 1.5; block.velocity = m_lastMouseVelocity * throwFactor; m_isDragging = false; m_draggedBlockIndex = -1; update(); } } void DraggableElasticObject::updatePhysics() { for (int i = 0; i < m_blocks.size(); ++i) { PhysicsBlockProperties& block = m_blocks[i]; block.isOnSomething = false; if (m_isDragging && m_draggedBlockIndex == i) { block.acceleration = {0.0, 0.0}; } else { applyForces(block); block.velocity += block.acceleration * TIME_STEP; } } int collisionIterations = 5; for (int iter = 0; iter < collisionIterations; ++iter) { for (int i = 0; i < m_blocks.size(); ++i) { PhysicsBlockProperties& block1 = m_blocks[i]; for (int j = i + 1; j < m_blocks.size(); ++j) { PhysicsBlockProperties& block2 = m_blocks[j]; handleBlockToBlockCollision(block1, block2); } } } for (int i = 0; i < m_blocks.size(); ++i) { PhysicsBlockProperties& block = m_blocks[i]; if (m_isDragging && m_draggedBlockIndex == i) { block.position = m_lastMousePos - m_dragOffset; block.velocity = {0.0, 0.0}; block.acceleration = {0.0, 0.0}; continue; } block.position += block.velocity * TIME_STEP; handleBoundaryCollision(block); bool onGround = (block.position.y() >= (height() - block.size.height() - EPSILON)); applyFrictionAndCheckStop(block, onGround); } update(); } void DraggableElasticObject::applyForces(PhysicsBlockProperties& block) { if (!block.isOnSomething) { block.acceleration.setY(GRAVITY_ACCELERATION); } else { block.acceleration.setY(0.0); } block.acceleration.setX(0.0); } void DraggableElasticObject::updateVelocityAndPredictedPosition(PhysicsBlockProperties& block, QPointF& predictedPosition) const { predictedPosition = block.position + block.velocity * TIME_STEP; } void DraggableElasticObject::handleBlockToBlockCollision(PhysicsBlockProperties& blockA, PhysicsBlockProperties& blockB) { QRectF rectA(blockA.position, blockA.size); QRectF rectB(blockB.position, blockB.size); if (!rectA.intersects(rectB)) { return; } bool isBlockADragged = (m_isDragging && m_draggedBlockIndex != -1 && &blockA == &m_blocks[m_draggedBlockIndex]); bool isBlockBDragged = (m_isDragging && m_draggedBlockIndex != -1 && &blockB == &m_blocks[m_draggedBlockIndex]); if (!blockA.isMovable && !blockB.isMovable && !isBlockADragged && !isBlockBDragged) { return; } if (isBlockADragged && isBlockBDragged) { return; } double overlapX = 0.0; double overlapY = 0.0; double blockARight = blockA.position.x() + blockA.size.width(); double blockALeft = blockA.position.x(); double blockABottom = blockA.position.y() + blockA.size.height(); double blockATop = blockA.position.y(); double blockBRight = blockB.position.x() + blockB.size.width(); double blockBLeft = blockB.position.x(); double blockBBottom = blockB.position.y() + blockB.size.height(); double blockBTop = blockB.position.y(); if (blockARight > blockBLeft && blockALeft < blockBRight) { overlapX = qMin(blockARight - blockBLeft, blockBRight - blockALeft); } if (blockABottom > blockBTop && blockATop < blockBBottom) { overlapY = qMin(blockABottom - blockBTop, blockBBottom - blockATop); } if (qAbs(overlapX) < qAbs(overlapY)) { PhysicsBlockProperties* movableBlock = nullptr; PhysicsBlockProperties* fixedBlock = nullptr; if (isBlockADragged || !blockA.isMovable) { fixedBlock = &blockA; movableBlock = &blockB; } else if (isBlockBDragged || !blockB.isMovable) { fixedBlock = &blockB; movableBlock = &blockA; } else { double totalMovableMass = blockA.mass + blockB.mass; if (totalMovableMass < EPSILON) return; double separationAmount = overlapX; if (blockALeft < blockBLeft) { blockA.position.setX(blockA.position.x() - separationAmount * (blockB.mass / totalMovableMass)); blockB.position.setX(blockB.position.x() + separationAmount * (blockA.mass / totalMovableMass)); } else { blockA.position.setX(blockA.position.x() + separationAmount * (blockB.mass / totalMovableMass)); blockB.position.setX(blockB.position.x() - separationAmount * (blockA.mass / totalMovableMass)); } goto velocity_resolution_x; } if (fixedBlock && movableBlock && movableBlock->isMovable) { double separationAmount = overlapX; if (movableBlock->position.x() < fixedBlock->position.x()) { movableBlock->position.setX(movableBlock->position.x() - separationAmount); } else { movableBlock->position.setX(movableBlock->position.x() + separationAmount); } goto velocity_resolution_x; } else { return; } } else { PhysicsBlockProperties* movableBlock = nullptr; PhysicsBlockProperties* fixedBlock = nullptr; if (isBlockADragged || !blockA.isMovable) { fixedBlock = &blockA; movableBlock = &blockB; } else if (isBlockBDragged || !blockB.isMovable) { fixedBlock = &blockB; movableBlock = &blockA; } else { double totalMovableMass = blockA.mass + blockB.mass; if (totalMovableMass < EPSILON) return; double separationAmount = overlapY; if (blockATop < blockBTop) { blockA.position.setY(blockA.position.y() - separationAmount * (blockB.mass / totalMovableMass)); blockB.position.setY(blockB.position.y() + separationAmount * (blockA.mass / totalMovableMass)); blockA.isOnSomething = true; } else { blockA.position.setY(blockA.position.y() + separationAmount * (blockB.mass / totalMovableMass)); blockB.position.setY(blockB.position.y() - separationAmount * (blockA.mass / totalMovableMass)); blockB.isOnSomething = true; } goto velocity_resolution_y; } if (fixedBlock && movableBlock && movableBlock->isMovable) { double separationAmount = overlapY; if (movableBlock->position.y() < fixedBlock->position.y()) { movableBlock->position.setY(movableBlock->position.y() - separationAmount); movableBlock->isOnSomething = true; } else { movableBlock->position.setY(movableBlock->position.y() + separationAmount); } goto velocity_resolution_y; } else { return; } } velocity_resolution_x: if (!blockA.isMovable && !blockB.isMovable && !isBlockADragged && !isBlockBDragged) return; if (isBlockADragged && blockB.isMovable) { double vRelX = blockA.velocity.x() - blockB.velocity.x(); if ((blockALeft < blockBLeft && vRelX > 0) || (blockALeft > blockBLeft && vRelX < 0)) { double e = qMin(blockA.restitutionCoefficient, blockB.restitutionCoefficient); double j = -(1 + e) * vRelX * blockB.mass; blockB.velocity.setX(blockB.velocity.x() - j / blockB.mass); } } else if (isBlockBDragged && blockA.isMovable) { double vRelX = blockA.velocity.x() - blockB.velocity.x(); if ((blockALeft < blockBLeft && vRelX > 0) || (blockALeft > blockBLeft && vRelX < 0)) { double e = qMin(blockA.restitutionCoefficient, blockB.restitutionCoefficient); double j = -(1 + e) * vRelX * blockA.mass; blockA.velocity.setX(blockA.velocity.x() + j / blockA.mass); } } else { double vRelX = blockA.velocity.x() - blockB.velocity.x(); if ((blockALeft < blockBLeft && vRelX > 0) || (blockALeft > blockBLeft && vRelX < 0)) { double e = qMin(blockA.restitutionCoefficient, blockB.restitutionCoefficient); double j = -(1 + e) * vRelX; double invMassA = blockA.isMovable ? (1.0 / blockA.mass) : 0.0; double invMassB = blockB.isMovable ? (1.0 / blockB.mass) : 0.0; double totalInvMass = invMassA + invMassB; if (totalInvMass > EPSILON) { j /= totalInvMass; if (blockA.isMovable) { blockA.velocity.setX(blockA.velocity.x() + j * invMassA); if (qAbs(blockA.velocity.x()) < EPSILON) blockA.velocity.setX(0.0); } if (blockB.isMovable) { blockB.velocity.setX(blockB.velocity.x() - j * invMassB); if (qAbs(blockB.velocity.x()) < EPSILON) blockB.velocity.setX(0.0); } } } } return; velocity_resolution_y: if (!blockA.isMovable && !blockB.isMovable && !isBlockADragged && !isBlockBDragged) return; if (isBlockADragged && blockB.isMovable) { double vRelY = blockA.velocity.y() - blockB.velocity.y(); if ((blockATop < blockBTop && vRelY > 0) || (blockATop > blockBTop && vRelY < 0)) { double e = qMin(blockA.restitutionCoefficient, blockB.restitutionCoefficient); double j = -(1 + e) * vRelY * blockB.mass; blockB.velocity.setY(blockB.velocity.y() - j / blockB.mass); blockB.isOnSomething = true; } } else if (isBlockBDragged && blockA.isMovable) { double vRelY = blockA.velocity.y() - blockB.velocity.y(); if ((blockATop < blockBTop && vRelY > 0) || (blockATop > blockBTop && vRelY < 0)) { double e = qMin(blockA.restitutionCoefficient, blockB.restitutionCoefficient); double j = -(1 + e) * vRelY * blockA.mass; blockA.velocity.setY(blockA.velocity.y() + j / blockA.mass); blockA.isOnSomething = true; } } else { double vRelY = blockA.velocity.y() - blockB.velocity.y(); if ((blockATop < blockBTop && vRelY > 0) || (blockATop > blockBTop && vRelY < 0)) { double e = qMin(blockA.restitutionCoefficient, blockB.restitutionCoefficient); double j = -(1 + e) * vRelY; double invMassA = blockA.isMovable ? (1.0 / blockA.mass) : 0.0; double invMassB = blockB.isMovable ? (1.0 / blockB.mass) : 0.0; double totalInvMass = invMassA + invMassB; if (totalInvMass > EPSILON) { j /= totalInvMass; if (blockA.isMovable) { blockA.velocity.setY(blockA.velocity.y() + j * invMassA); if (qAbs(blockA.velocity.y()) < EPSILON) blockA.velocity.setY(0.0); } if (blockB.isMovable) { blockB.velocity.setY(blockB.velocity.y() - j * invMassB); if (qAbs(blockB.velocity.y()) < EPSILON) blockB.velocity.setY(0.0); } } } } if (!isBlockADragged && !blockA.isMovable && (qAbs(blockA.velocity.x()) > EPSILON || qAbs(blockA.velocity.y()) > EPSILON)) { blockA.isMovable = true; } if (!isBlockBDragged && !blockB.isMovable && (qAbs(blockB.velocity.x()) > EPSILON || qAbs(blockB.velocity.y()) > EPSILON)) { blockB.isMovable = true; } } void DraggableElasticObject::handleBoundaryCollision(PhysicsBlockProperties& block) { if (qAbs(block.velocity.x()) < EPSILON && qAbs(block.velocity.y()) < EPSILON && !block.isMovable && !(m_isDragging && m_draggedBlockIndex != -1 && &block == &m_blocks[m_draggedBlockIndex])) { return; } double parentWidth = width(); double parentHeight = height(); bool collided = false; double groundY = parentHeight - block.size.height(); if (block.position.y() >= groundY - EPSILON) { block.position.setY(groundY); block.velocity.setY(-block.velocity.y() * block.restitutionCoefficient); if (qAbs(block.velocity.y()) < EPSILON) { block.velocity.setY(0.0); } collided = true; block.isOnSomething = true; } double topY = 0.0; if (block.position.y() <= topY + EPSILON) { block.position.setY(topY); block.velocity.setY(-block.velocity.y() * block.restitutionCoefficient); if (qAbs(block.velocity.y()) < EPSILON) { block.velocity.setY(0.0); } collided = true; } double leftX = 0.0; if (block.position.x() <= leftX + EPSILON) { block.position.setX(leftX); block.velocity.setX(-block.velocity.x() * block.restitutionCoefficient); if (qAbs(block.velocity.x()) < EPSILON) { block.velocity.setX(0.0); } collided = true; } double rightX = parentWidth - block.size.width(); if (block.position.x() >= rightX - EPSILON) { block.position.setX(rightX); block.velocity.setX(-block.velocity.x() * block.restitutionCoefficient); if (qAbs(block.velocity.x()) < EPSILON) { block.velocity.setX(0.0); } collided = true; } if (collided && !block.isMovable && (qAbs(block.velocity.x()) > EPSILON || qAbs(block.velocity.y()) > EPSILON) && !(m_isDragging && m_draggedBlockIndex != -1 && &block == &m_blocks[m_draggedBlockIndex])) { block.isMovable = true; } } void DraggableElasticObject::applyFrictionAndCheckStop(PhysicsBlockProperties& block, bool onGround) { if (m_isDragging && m_draggedBlockIndex != -1 && &block == &m_blocks[m_draggedBlockIndex]) { return; } bool isSupported = onGround || block.isOnSomething; if (isSupported && qAbs(block.velocity.x()) > EPSILON) { double normalForce = block.mass * GRAVITY_ACCELERATION; double frictionForceMagnitude = block.frictionCoefficient * normalForce; double frictionAccelerationMagnitude = frictionForceMagnitude / block.mass; if (block.velocity.x() > 0) { block.velocity.setX(qMax(0.0, block.velocity.x() - frictionAccelerationMagnitude * TIME_STEP)); } else { block.velocity.setX(qMin(0.0, block.velocity.x() + frictionAccelerationMagnitude * TIME_STEP)); } if (qAbs(block.velocity.x()) < EPSILON) { block.velocity.setX(0.0); } } if (isSupported && qAbs(block.velocity.y()) < EPSILON && qAbs(block.acceleration.y()) < EPSILON) { block.velocity.setY(0.0); block.acceleration.setY(0.0); if (qAbs(block.velocity.x()) < EPSILON) { block.velocity.setX(0.0); block.acceleration.setX(0.0); block.isMovable = false; } } else { if (qAbs(block.velocity.x()) > EPSILON || qAbs(block.velocity.y()) > EPSILON || !isSupported) { block.isMovable = true; } } } ================================================ FILE: Anime_Template_Project/Physical_Text/draggableelasticobject.h ================================================ #ifndef DRAGGABLEELASTICOBJECT_H #define DRAGGABLEELASTICOBJECT_H #include #include #include #include #include #include #include #include #include struct PhysicsBlockProperties { QPointF position = {0.0, 0.0}; QSizeF size = {50.0, 50.0}; QString name; double mass = 1.0; QPointF velocity = {0.0, 0.0}; QPointF acceleration = {0.0, 0.0}; double frictionCoefficient = 0.5; double restitutionCoefficient = 0.7; bool isMovable = true; QColor color = Qt::blue; bool isOnSomething = false; }; class DraggableElasticObject : public QWidget { Q_OBJECT public: explicit DraggableElasticObject(QWidget *parent = nullptr); ~DraggableElasticObject() override; void addBlock(const PhysicsBlockProperties& block); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private slots: void updatePhysics(); private: void applyForces(PhysicsBlockProperties& block); void updateVelocityAndPredictedPosition(PhysicsBlockProperties& block, QPointF& predictedPosition) const; void handleBlockToBlockCollision(PhysicsBlockProperties& blockA, PhysicsBlockProperties& blockB); void handleBoundaryCollision(PhysicsBlockProperties& block); void applyFrictionAndCheckStop(PhysicsBlockProperties& block, bool onGround); private: QList m_blocks; QTimer *m_physicsTimer; const double GRAVITY_ACCELERATION = 981.0; const double TIME_STEP = 0.001; const double EPSILON = 0.1; bool m_isDragging = false; int m_draggedBlockIndex = -1; QPointF m_dragOffset; QPointF m_lastMousePos; qint64 m_lastMousePressTime; QPointF m_lastMouseVelocity; }; #endif // DRAGGABLEELASTICOBJECT_H ================================================ FILE: Anime_Template_Project/Physical_Text/main.cpp ================================================ #include #include "draggableelasticobject.h" #include #include #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; window.resize(1000, 700); DraggableElasticObject *physicsWidget = new DraggableElasticObject(&window); window.setCentralWidget(physicsWidget); QString sentence = "命运石之门 胆大党 败犬女主太多了 哭泣少女乐队 我心里危险的东西 葬送的芙莉莲 咒术回战 无职转生 为美好的世界献上祝福 夏日重现 鬼灭之刃 JOJO的奇妙冒险 86-不存在的战区"; QStringList words = sentence.split(" ", Qt::SkipEmptyParts); QFont font; font.setPointSize(15); font.setBold(true); qreal currentX = 50.0; qreal currentY = 50.0; qreal maxHeightInRow = 0.0; for (const QString& word : words) { PhysicsBlockProperties block; block.name = word; QFontMetrics fm(font); int textWidth = fm.horizontalAdvance(word) + 20; int lineHeight = fm.height() + 10; block.size = {qreal(textWidth), qreal(lineHeight)}; if (currentX + block.size.width() > window.width() - 50) { currentX = 50.0; currentY += maxHeightInRow + 20; maxHeightInRow = 0.0; } block.position = {currentX, currentY}; block.color = QColor(QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255), QRandomGenerator::global()->bounded(255)); block.mass = 1.0 + QRandomGenerator::global()->bounded(100) / 50.0; block.restitutionCoefficient = 0.6 + QRandomGenerator::global()->bounded(40) / 100.0; physicsWidget->addBlock(block); currentX += block.size.width() + 10; maxHeightInRow = qMax(maxHeightInRow, block.size.height()); } PhysicsBlockProperties groundBlock; groundBlock.position = {100.0, 500.0}; groundBlock.size = {800.0, 30.0}; groundBlock.color = Qt::gray; groundBlock.isMovable = false; groundBlock.restitutionCoefficient = 0.5; physicsWidget->addBlock(groundBlock); window.show(); return a.exec(); } ================================================ FILE: Anime_Template_Project/PixelTransition/pixel_transition.cpp ================================================ #include "pixel_transition.h" Pixel_Transition::Pixel_Transition(int count,QWidget *parent) : QWidget{parent} { PixelPixmap = QPixmap(":/new/Ggdkoima8AAFzsN.jpg"); this->updatePixelCount(count); } void Pixel_Transition::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform ); int widgetWidth = width(); int widgetHeight = height(); int imageWidth = PixelPixmap.width(); int imageHeight = PixelPixmap.height(); double widthRatio = qreal(widgetWidth) / imageWidth; double heightRatio = qreal(widgetHeight) / imageHeight; double scale = std::max(widthRatio, heightRatio); int scaledWidth = int(imageWidth * scale); int scaledHeight = int(imageHeight * scale); int x = (widgetWidth - scaledWidth) / 2; // int y = (widgetHeight - scaledHeight) / 2; QPainterPath path; path.addRoundedRect(QRectF(0, 0, widgetWidth, widgetHeight), 10, 10); painter.setClipPath(path); // 绘制图片 if (Display_Background) painter.drawPixmap(x, 0, PixelPixmap.scaled(scaledWidth, scaledHeight, Qt::KeepAspectRatio)); //绘制第二背景 if (!Display_Background) { painter.setBrush( QColor(17,17,17)); painter.drawRect(0, 0, widgetWidth, widgetWidth); QFont font; font.setFamily("微软雅黑"); font.setPixelSize(widgetWidth / 5); font.setBold(true); painter.setFont(font); QLinearGradient gradient(widgetWidth * 0.3, widgetHeight * 0.3, widgetWidth * 0.6, widgetHeight * 0.6); gradient.setColorAt(0, QColor(87, 36, 60)); gradient.setColorAt(0.5, QColor(53, 119, 177)); gradient.setColorAt(1, QColor(228, 252, 255)); painter.setPen(QPen(QBrush(gradient), 2)); painter.drawText(QRectF(0, 0, widgetWidth, widgetHeight), Qt::AlignCenter, "星见雅"); } painter.setPen(Qt::NoPen); painter.setBrush(PixelColor); for (int x = 0; x < m_Display_Progress; x++) { int xpos = White_pixel[x] % count; int ypos = White_pixel[x] / count; painter.drawRect(QRectF(qreal(xpos) * PixelSize - 1, qreal(ypos) * PixelSize - 1, PixelSize + 1, PixelSize + 1)); } QPen borderPen(Qt::white, 3); painter.setPen(borderPen); painter.setBrush(Qt::NoBrush); painter.drawRoundedRect(QRectF(0, 0, widgetWidth, widgetHeight), 10, 10); } bool Pixel_Transition::event(QEvent *event) { if (event->type() == QEvent::Enter) this->startAnimation(); else if (event->type() == QEvent::Leave) this->resetAnimation(); return QWidget::event(event); } void Pixel_Transition::resizeEvent(QResizeEvent *event) { this->PixelSize = qreal(width()) / qreal(count); } void Pixel_Transition::adjustDisplaySize(int width, int height) { if (height > width) this->resize(width, width); else this->resize(height, height); update(); } void Pixel_Transition::startAnimation() { if (Display_Background) { QPropertyAnimation* animation = new QPropertyAnimation(this, "Display_Progress"); animation->setDuration(1000 *AnimationTimeRatio); animation->setStartValue(0); animation->setEndValue(count * count); animation->start(QAbstractAnimation::DeleteWhenStopped); connect(animation, &QPropertyAnimation::finished, this,[=]{ this->Display_Background = false; QPropertyAnimation* animation2 = new QPropertyAnimation(this, "Display_Progress"); animation2->setDuration(1000 *AnimationTimeRatio); animation2->setStartValue(m_Display_Progress); animation2->setEndValue(0); animation2->start(QAbstractAnimation::DeleteWhenStopped); connect(animation2,&QPropertyAnimation::finished, this,[=]{ this->updatePixelCount(count); }); }); } else { QPropertyAnimation* animation = new QPropertyAnimation(this, "Display_Progress"); animation->setDuration(1000 *AnimationTimeRatio); animation->setStartValue(m_Display_Progress); animation->setEndValue(0); animation->start(QAbstractAnimation::DeleteWhenStopped); connect(animation,&QPropertyAnimation::finished, this,[=]{ this->updatePixelCount(count); }); } } void Pixel_Transition::resetAnimation() { if (Display_Background) { QPropertyAnimation* animation = new QPropertyAnimation(this, "Display_Progress"); animation->setDuration(1000 *AnimationTimeRatio); animation->setStartValue(m_Display_Progress); animation->setEndValue(0); animation->start(QAbstractAnimation::DeleteWhenStopped); connect(animation,&QPropertyAnimation::finished, this,[=]{ this->updatePixelCount(count); }); } else { QPropertyAnimation* animation = new QPropertyAnimation(this, "Display_Progress"); animation->setDuration(1000 *AnimationTimeRatio); animation->setStartValue(m_Display_Progress); animation->setEndValue(count * count); animation->start(QAbstractAnimation::DeleteWhenStopped); connect(animation, &QPropertyAnimation::finished, this,[=]{ this->Display_Background = true; QPropertyAnimation* animation2 = new QPropertyAnimation(this, "Display_Progress"); animation2->setDuration(1000 *AnimationTimeRatio); animation2->setStartValue(m_Display_Progress); animation2->setEndValue(0); animation2->start(QAbstractAnimation::DeleteWhenStopped); connect(animation2,&QPropertyAnimation::finished, this,[=]{ this->updatePixelCount(count); }); }); } } void Pixel_Transition::updatePixelCount(int count) { White_pixel.clear(); this->count = count; this->PixelSize = qreal(width()) / qreal(count); for (int var = 0; var < count * count; ++var) White_pixel.append(var); QRandomGenerator *rgb = QRandomGenerator::global(); std::shuffle(White_pixel.begin(), White_pixel.end(), *rgb); qDebug() << "White_pixel size:" << White_pixel.size(); } void Pixel_Transition::updateAnimationDuration(float duration) { this->AnimationTimeRatio = duration; } int Pixel_Transition::Display_Progress() const { return m_Display_Progress; } void Pixel_Transition::setDisplay_Progress(int newDisplay_Progress) { m_Display_Progress = newDisplay_Progress; qDebug() << "Display_Progress:" << m_Display_Progress; update(); } ================================================ FILE: Anime_Template_Project/PixelTransition/pixel_transition.h ================================================ #ifndef PIXEL_TRANSITION_H #define PIXEL_TRANSITION_H #include #include #include #include #include #include #include #include class Pixel_Transition : public QWidget { Q_OBJECT Q_PROPERTY(int Display_Progress READ Display_Progress WRITE setDisplay_Progress FINAL) public: explicit Pixel_Transition(int count, QWidget *parent = nullptr); int Display_Progress() const; void setDisplay_Progress(int newDisplay_Progress); void adjustDisplaySize(int width, int height); void startAnimation(); void resetAnimation(); void setPixelColor(const QColor &color) { this->PixelColor = color; } public slots: void updatePixelCount(int count); void updateAnimationDuration(float duration); protected: void paintEvent(QPaintEvent *event) override; bool event(QEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: QList White_pixel; int count; QColor PixelColor = QColor(255,255,255); qreal PixelSize; qreal AnimationTimeRatio = 0.4; bool Display_Background = true; QPixmap PixelPixmap; int m_Display_Progress = 0; }; #endif // PIXEL_TRANSITION_H ================================================ FILE: Anime_Template_Project/PixelTransition/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { this->resize(1200,500); pixel_transition = new Pixel_Transition(18,this); slider = new QSlider(Qt::Horizontal, this); slider->setRange(2, 150); slider->setValue(18); slider->resize(200, 30); timerslider = new QSlider(Qt::Horizontal, this); timerslider->setRange(1, 200); timerslider->setValue(40); timerslider->resize(200, 30); label1 = new QLabel("网格尺寸:", this); label1->resize(100,30); label1->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); label2 = new QLabel("动画持续时间:", this); label2->resize(100,30); label2->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); label3 = new QLabel(this); label3->resize(40,30); label3->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); label4 = new QLabel(this); label4->resize(40,30); label4->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); label5 = new QPushButton(this); label5->resize(100,30); label3->setText(QString::number(slider->value())); label4->setText(QString::number(float(timerslider->value()) / 100.00)); colorButton = new QPushButton("选择颜色", this); colorButton->resize(100, 30); QPalette palette = label5->palette(); palette.setColor(QPalette::Button, Qt::white); label5->setPalette(palette); connect(colorButton, &QPushButton::clicked, this, &Widget::chooseColor); connect(slider, &QSlider::valueChanged, label3, [this](int value) { label3->setText(QString::number(value)); pixel_transition->updatePixelCount(value); }); connect(timerslider, &QSlider::valueChanged, label4, [this](int value) { label4->setText(QString::number(float(value) / 100.00)); pixel_transition->updateAnimationDuration(float(value) / 100.00); }); } Widget::~Widget() { } void Widget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); int width = event->size().width(); int height = event->size().height(); pixel_transition->adjustDisplaySize(width, height -100); pixel_transition->move((width - pixel_transition->width()) / 2, 0); label1->move(width /2 - slider->width() - label1->width(), height - 100); slider->move(width /2 - slider->width(),height -100); label3->move(width /2,height -100); label2->move(width /2 - timerslider->width() - label2->width(), height - 65); timerslider->move(width /2 - slider->width(),height -65); label4->move(width /2,height -65); colorButton->move(width /2 - slider->width(), height - 35); label5->move(width /2 - slider->width() - label1->width(),height -35); } void Widget::chooseColor() { QColor color = QColorDialog::getColor(Qt::white, this, "选择颜色"); if (color.isValid()) { QPalette palette = label5->palette(); palette.setColor(QPalette::Button, color); label5->setPalette(palette); pixel_transition->setPixelColor(color); } } ================================================ FILE: Anime_Template_Project/PixelTransition/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include #include #include #include #include #include #include "pixel_transition.h" class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); protected: void resizeEvent(QResizeEvent *event) override; private slots: void chooseColor(); private: Pixel_Transition* pixel_transition; QSlider *slider; QSlider *timerslider; QLabel *label1; QLabel *label2; QLabel *label3; QLabel *label4; QPushButton *label5; QPushButton *colorButton; }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/Point_Wave/widget.cpp ================================================ #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { resize(w,h); timer = new QTimer(this); connect(timer,&QTimer::timeout,this,&Widget::Radius_Processing); timer->setTimerType(Qt::PreciseTimer); timer->start(3); } Widget::~Widget() {} void Widget::addDot() { this->w = width(); this->h = height(); m_dots.clear(); qreal startX = w / 15; qreal startY = h / 15;; qreal spacing = h / 14; r = spacing; r2 = spacing; Wcenter = QPointF(w/2,w/2); int numRows = 13; int numCols = 13; int x = 4; int j = numRows; int i = 8; for (int row = 0; row < numRows; ++row) { for (int col = 0; col < numCols; ++col) { if (col < x) continue; if (col > i) continue; m_dots.append({QPointF(startX + col * spacing, startY + row * spacing),qreal(3)}); } if (row < numRows *0.3) { qDebug() << numRows *0.3; x -=1; i +=1; } else if (row > numRows *0.61) { qDebug() << numRows *0.61; x +=1; i -=1; } } } void Widget::Radius_Processing() { for (DotData& Data : m_dots) { if (this->isPointInAnnulus(Data.point, Wcenter, r1, r2)) { qDebug() << "存在: " << Data.point; Data.dotRadius += 0.75; } else { if (Data.dotRadius < 3) continue; Data.dotRadius -= 0.3; } } if ( r1>w && r2 >2) { r1 = 0; r2 = r; } else { r1 += 1.0; r2 += 1.0; } update(); } void Widget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(rect(), QColor(33,33,33)); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(QColor(146,252,193))); for (const DotData& Data : m_dots) { painter.drawEllipse(Data.point.x() - Data.dotRadius, Data.point.y() - Data.dotRadius, 2 * Data.dotRadius, 2 * Data.dotRadius); } painter.setPen(QColor(146,252,193)); painter.setBrush(Qt::NoBrush); painter.drawEllipse(Wcenter,r2,r2); painter.drawEllipse(Wcenter,r1,r1); } void Widget::resizeEvent(QResizeEvent *event) { resize(event->size().width(),event->size().width()); addDot(); } bool Widget::isPointInAnnulus(const QPointF& testPoint, const QPointF& centerPoint, qreal innerRadius, qreal outerRadius) { qreal dx = testPoint.x() - centerPoint.x(); qreal dy = testPoint.y() - centerPoint.y(); qreal distanceSquared = qPow(dx, 2) + qPow(dy, 2); qreal distance = qSqrt(distanceSquared); return (distance >= innerRadius && distance <= outerRadius); } ================================================ FILE: Anime_Template_Project/Point_Wave/widget.h ================================================ #ifndef WIDGET_H #define WIDGET_H #include #include #include #include struct DotData { QPointF point; qreal dotRadius; }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); void addDot(); bool isPointInAnnulus(const QPointF &testPoint, const QPointF ¢erPoint, qreal innerRadius, qreal outerRadius); public slots: void Radius_Processing(); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: QList m_dots; qreal m_dotRadius = 3.0; int w = 600; int h = 600; QTimer * timer; QPointF Wcenter; qreal r; qreal r1 = 0; qreal r2; }; #endif // WIDGET_H ================================================ FILE: Anime_Template_Project/Pressure_Block/squarepressurewidget.cpp ================================================ // squarepressurewidget.cpp #include "squarepressurewidget.h" #include #include SquarePressureWidget::SquarePressureWidget(QWidget *parent) : QWidget(parent), m_text("COMPRESSA"), m_minSquareSize(24), m_mousePos(0, 0) { m_timer = new QTimer(this); connect(m_timer, &QTimer::timeout, this, &SquarePressureWidget::updateAnimation); m_timer->start(5); } void SquarePressureWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); for (const auto &prop : m_squareProps) { painter.setOpacity(prop.currentAlpha); painter.fillRect(prop.rect, prop.currentColor); } } void SquarePressureWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); calculateSquareProperties(); update(); } void SquarePressureWidget::updateAnimation() { m_cursorPos = mapFromGlobal(QCursor::pos()); m_mousePos.setX(m_mousePos.x() + (m_cursorPos.x() - m_mousePos.x()) / 5.0); m_mousePos.setY(m_mousePos.y() + (m_cursorPos.y() - m_mousePos.y()) / 5.0); double maxDist = std::max(rect().width() / 2.0, rect().height() / 2.0) * 1.5; for (auto &prop : m_squareProps) { double d = dist(m_mousePos, prop.initialCenter); auto getAttr = [&](double distance, double minVal, double maxVal) { double normalizedDistance = std::min(distance, maxDist) / maxDist; double val = maxVal - (normalizedDistance * (maxVal - minVal)); return std::max(minVal, val); }; double maxDynamicSquareSize = std::max(rect().width(), rect().height()) * 0.15; maxDynamicSquareSize = std::max(m_minSquareSize, maxDynamicSquareSize); double currentSquareSize = getAttr(d, m_minSquareSize, maxDynamicSquareSize); double maxDisplacement = maxDynamicSquareSize * 0.5; double displacementStrength = getAttr(d, 0.0, maxDisplacement); QPointF direction = prop.initialCenter - m_mousePos; if (direction.isNull()) { direction = QPointF(1.0, 0.0); } double angle = std::atan2(direction.y(), direction.x()); QPointF displacementVector(std::cos(angle) * displacementStrength, std::sin(angle) * displacementStrength); QPointF finalCenter = prop.initialCenter + displacementVector; prop.rect.setSize(QSizeF(currentSquareSize, currentSquareSize)); prop.rect.moveCenter(finalCenter); prop.currentAlpha = getAttr(d, 0.2, 1.0); int colorVal = static_cast(getAttr(d, 0.0, 255.0)); prop.currentColor = QColor(colorVal, colorVal, colorVal); } update(); } void SquarePressureWidget::calculateSquareProperties() { m_squareProps.clear(); if (m_text.isEmpty()) { return; } double charCount = m_text.length(); double idealSquareSize = std::min(width() / charCount, (double)height()); double initialSquareSize = std::max(m_minSquareSize, idealSquareSize); double currentX = (width() - (charCount * initialSquareSize)) / 2.0; for (int i = 0; i < charCount; ++i) { SquareProperties prop; prop.character = m_text.at(i); prop.rect = QRectF(currentX, (height() - initialSquareSize) / 2.0, initialSquareSize, initialSquareSize); prop.initialCenter = prop.rect.center(); prop.currentWeight = 100; prop.currentAlpha = 1.0; prop.currentColor = Qt::white; m_squareProps.append(prop); currentX += initialSquareSize; } } double SquarePressureWidget::dist(const QPointF &p1, const QPointF &p2) { return std::sqrt(std::pow(p2.x() - p1.x(), 2) + std::pow(p2.y() - p1.y(), 2)); } ================================================ FILE: Anime_Template_Project/Pressure_Block/squarepressurewidget.h ================================================ #ifndef SQUAREPRESSUREWIDGET_H #define SQUAREPRESSUREWIDGET_H #include #include #include #include #include #include #include struct SquareProperties { QChar character; QRectF rect; double currentWeight; double currentAlpha; QColor currentColor; QPointF initialCenter; }; class SquarePressureWidget : public QWidget { Q_OBJECT public: explicit SquarePressureWidget(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void updateAnimation(); private: void calculateSquareProperties(); double dist(const QPointF &p1, const QPointF &p2); QString m_text; double m_minSquareSize; QPointF m_mousePos; QPointF m_cursorPos; QTimer *m_timer; QVector m_squareProps; }; #endif // SQUAREPRESSUREWIDGET_H ================================================ FILE: Anime_Template_Project/SplitText/Single_Text.cpp ================================================ #include "Single_Text.h" Single_Text::Single_Text(QString text,QWidget *parent) : QWidget(parent) { this->setFixedSize(160, 160); this->m_text = text; QPalette palette = this->palette(); palette.setColor(QPalette::Base, QColor(5, 0, 15)); this->setPalette(palette); centerX = this->width() / 2.0; centerY = this->height() / 3.0; m_color_animation = new QPropertyAnimation(this, "color_progress"); m_color_animation->setDuration(200); m_color_animation->setStartValue(0); m_color_animation->setEndValue(255); m_rotate_animation = new QPropertyAnimation(this, "rotate_degree"); m_rotate_animation->setDuration(400); m_rotate_animation->setStartValue(0); m_rotate_animation->setEndValue(360); m_rotate_animation->setEasingCurve(QEasingCurve::OutBack); connect(m_rotate_animation, &QPropertyAnimation::finished, this, &Single_Text::rotate_finished); } void Single_Text::start_color_animation() { m_color_animation->setDirection(QAbstractAnimation::Forward); m_color_animation->start(); } void Single_Text::reset_color_animation() { m_color_animation->setDirection(QAbstractAnimation::Backward); m_color_animation->start(); } void Single_Text::start_rotate_animation() { m_rotate_animation->setDirection(QAbstractAnimation::Forward); m_rotate_animation->start(); } void Single_Text::reset_rotate_animation() { m_rotate_animation->setDirection(QAbstractAnimation::Backward); m_rotate_animation->start(); } void Single_Text::paintEvent(QPaintEvent *event) { QPainter painter(this); QConicalGradient gradient(0, 0, this->pos().x() * 1.5); gradient.setColorAt(0, QColor( 255, 167, 69, 255)); gradient.setColorAt(0.25, QColor(254, 134, 159, 255)); gradient.setColorAt(0.5, QColor(239, 122, 200, 255)); gradient.setColorAt(0.75, QColor(160, 131, 237, 255)); gradient.setColorAt(1.0, QColor(67, 174, 255, 255)); painter.setBrush(gradient); QFont font; font.setPixelSize(this->height() / 2); font.setFamily("Arial"); font.setBold(true); painter.setFont(font); painter.setPen(QPen(QBrush(gradient), 2)); QTransform transform; transform.translate(centerX, centerY); transform.rotate(m_rotate_degree); transform.translate(-centerX, -centerY); painter.setTransform(transform); painter.drawText(0, 0, this->width() / 2, this->height() / 2, Qt::AlignCenter, m_text); } int Single_Text::get_color_progress() const { return m_color_progress; } void Single_Text::set_color_progress(int value) { m_color_progress = value; update(); } int Single_Text::get_rotate_degree() const { return m_rotate_degree; } void Single_Text::set_rotate_degree(int value) { m_rotate_degree = value; update(); } ================================================ FILE: Anime_Template_Project/SplitText/Single_Text.h ================================================ #pragma once #include #include #include class Single_Text : public QWidget { Q_OBJECT Q_PROPERTY(int color_progress READ get_color_progress WRITE set_color_progress) Q_PROPERTY(int rotate_degree READ get_rotate_degree WRITE set_rotate_degree) public: explicit Single_Text(QString text,QWidget *parent = nullptr); int get_color_progress() const; void set_color_progress(int value); int get_rotate_degree() const; void set_rotate_degree(int value); void start_color_animation(); void reset_color_animation(); void start_rotate_animation(); void reset_rotate_animation(); Q_SIGNALS: void rotate_finished(); protected: void paintEvent(QPaintEvent *event) override; private: QString m_text; QColor m_color = QColor(255, 255, 255, 255); int m_color_progress = 255; int m_rotate_degree = 0; QPropertyAnimation *m_color_animation = nullptr; QPropertyAnimation *m_rotate_animation = nullptr; qreal centerX ; qreal centerY ; }; ================================================ FILE: Anime_Template_Project/SplitText/SplitText.cpp ================================================ #include "SplitText.h" SplitText::SplitText(QWidget* parent) : QMainWindow(parent) { this->resize(700, 700); run_animation_button = new QPushButton(this); run_animation_button->resize(100, 30); run_animation_button->setText("Run Animation"); input_text_edit = new QLineEdit(this); input_text_edit->resize(200, 30); connect(input_text_edit,&QLineEdit::textChanged,this,&SplitText::add_single_text); connect(run_animation_button, &QPushButton::clicked, this, &SplitText::run_animation); } SplitText::~SplitText() { } void SplitText::resizeEvent(QResizeEvent* event) { int mw = event->size().width() / 2; int mh = event->size().height(); start_y = mh - 200; end_y = mh - 700; run_animation_button->move(mw - run_animation_button->width() * 2, mh - run_animation_button->height() - 10); input_text_edit->move(mw - input_text_edit->width() / 2, mh - input_text_edit->height() - 10); int start_x = (this->width() - single_text_list.size() * 80) / 2; for (int i = 0; i < single_text_list.size(); i++) { single_text_list[i]->move(start_x + i * 80, height() - 200); } } void SplitText::add_single_text(QString text) { for (Single_Text* single_text : single_text_list) { delete single_text; } single_text_list.clear(); int text_length = text.length() * 80; int start_x = (this->width() - text_length) / 2; for (int i = 0; i < text.length(); i++) { QString single_text = text.mid(i, 1); Single_Text* single_text_obj = new Single_Text(single_text, this); single_text_obj->move(start_x + i * 80, height() - 200); single_text_obj->show(); single_text_list.push_back(single_text_obj); } } void SplitText::run_animation() { for (int i = 0; i < single_text_list.size(); i++) { QPropertyAnimation* animation = new QPropertyAnimation(single_text_list[i], "pos"); animation->setDuration(350 + i * 20); animation->setStartValue(QPoint(single_text_list[i]->pos().x(), start_y)); animation->setEndValue(QPoint(single_text_list[i]->pos().x(), end_y)); animation->setEasingCurve(QEasingCurve::InBack); animation->start(QAbstractAnimation::DeleteWhenStopped); ///////////////////////// connect(animation, &QPropertyAnimation::finished, this,[=]{ single_text_list[i]->start_rotate_animation(); ///////////////////////////// connect(single_text_list[i], &Single_Text::rotate_finished, this, [=]{ qDebug() << "动画结束"; QPropertyAnimation* animation2 = new QPropertyAnimation(single_text_list[i], "pos"); animation2->setDuration(350 + i * 20); animation2->setStartValue(QPoint(single_text_list[i]->pos().x(), end_y)); animation2->setEndValue(QPoint(single_text_list[i]->pos().x(), start_y)); animation2->setEasingCurve(QEasingCurve::OutElastic); animation2->start(QAbstractAnimation::DeleteWhenStopped); }); }); } } ================================================ FILE: Anime_Template_Project/SplitText/SplitText.h ================================================ #pragma once #include #include "Single_Text.h" //动态列表 #include //按钮 #include //输入框 #include #include //动画 #include class SplitText : public QMainWindow { Q_OBJECT public: SplitText(QWidget* parent = nullptr); ~SplitText(); protected: void resizeEvent(QResizeEvent* event) override; public slots: void add_single_text(QString text ); void run_animation(); private: std::vector single_text_list; QPushButton* run_animation_button; QLineEdit* input_text_edit; int start_y; int end_y; }; ================================================ FILE: Anime_Template_Project/Star_Sky_Connection/MovingPointsWidget.cpp ================================================ #include "MovingPointsWidget.h" MovingPoint::MovingPoint(const QPointF& pos, const QPointF& direction, double speed, const QColor& color) : m_position(pos), m_speed(speed), m_color(color) { qreal length = qSqrt(direction.x() * direction.x() + direction.y() * direction.y()); if (length > 0) { m_direction = direction / length; } else { m_direction = QPointF(0, 0); } } void MovingPoint::updatePosition(double deltaTime) { m_position += m_direction * (m_speed * deltaTime); } void MovingPoint::setDirection(const QPointF& newDirection) { qreal length = qSqrt(newDirection.x() * newDirection.x() + newDirection.y() * newDirection.y()); if (length > 0) { m_direction = newDirection / length; } else { m_direction = QPointF(0, 0); } } QPointF MovingPoint::getPosition() const { return m_position; } QColor MovingPoint::getColor() const { return m_color; } QPointF MovingPoint::getDirection() const { return m_direction; } bool MovingPoint::isOffscreen(const QSizeF& windowSize) const { return m_position.x() < -10 || m_position.x() > windowSize.width() + 10 || m_position.y() < -10 || m_position.y() > windowSize.height() + 10; } MovingPointsWidget::MovingPointsWidget(QWidget *parent) : QWidget(parent) { setMouseTracking(true); QPalette pal = this->palette(); pal.setColor(QPalette::ColorRole::Window, QColor(0, 0, 0,255)); this->setPalette(pal); resize(1200, 800); m_updateTimer = new QTimer(this); connect(m_updateTimer, &QTimer::timeout, this, &MovingPointsWidget::updatePoints); m_updateTimer->start(m_timerInterval); for (int i = 0; i < m_maxPoints / 2; ++i) { generateRandomPoint(); } } MovingPointsWidget::~MovingPointsWidget() { if (m_updateTimer) { m_updateTimer->stop(); delete m_updateTimer; m_updateTimer = nullptr; } } void MovingPointsWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); for (int i = 0; i < m_points.size(); ++i) { for (int j = i + 1; j < m_points.size(); ++j) { const QPointF& pos1 = m_points[i].getPosition(); const QPointF& pos2 = m_points[j].getPosition(); qreal distance = qSqrt(qPow(pos1.x() - pos2.x(), 2) + qPow(pos1.y() - pos2.y(), 2)); if (distance < 230) { int alpha = 0; if (distance < 100) { alpha = 255; } else { alpha = qRound(255.0 * (1.0 - (distance - 100.0) / 130.0)); alpha = qMax(0, qMin(255, alpha)); } painter.setPen(QPen(QColor(255, 255, 255, alpha))); painter.drawLine(pos1, pos2); } } } for (const auto& point : m_points) { painter.setBrush(QBrush(point.getColor())); painter.setPen(Qt::NoPen); painter.drawEllipse(point.getPosition(), 2, 2); } } void MovingPointsWidget::mouseMoveEvent(QMouseEvent *event) { m_mousePosition = event->pos(); } void MovingPointsWidget::enterEvent(QEnterEvent *event) { Q_UNUSED(event); m_mouseInWidget = true; } void MovingPointsWidget::leaveEvent(QEvent *event) { Q_UNUSED(event); m_mouseInWidget = false; } void MovingPointsWidget::updatePoints() { double deltaTime = static_cast(m_timerInterval) / 1000.0; for (int i = 0; i < m_points.size(); ++i) { QPointF currentPointPos = m_points[i].getPosition(); QPointF currentPointDir = m_points[i].getDirection(); if (m_mouseInWidget) { qreal distToMouse = qSqrt(qPow(currentPointPos.x() - m_mousePosition.x(), 2) + qPow(currentPointPos.y() - m_mousePosition.y(), 2)); if (distToMouse < m_attractionRadius) { QPointF targetPoint = calculateHexagonPoint(m_mousePosition, i % 6, 80.0); QPointF attractionVector = targetPoint - currentPointPos; qreal attractionLength = qSqrt(qPow(attractionVector.x(), 2) + qPow(attractionVector.y(), 2)); if (attractionLength > 0) { attractionVector /= attractionLength; } else { attractionVector = QPointF(0,0); } double attractionFactor = 1.0 - (distToMouse / m_attractionRadius); attractionFactor = qMax(0.0, qMin(1.0, attractionFactor)); QPointF newDirection = currentPointDir * (1.0 - attractionFactor) + attractionVector * attractionFactor; qreal newDirLength = qSqrt(qPow(newDirection.x(), 2) + qPow(newDirection.y(), 2)); if (newDirLength > 0) { m_points[i].setDirection(newDirection / newDirLength); } } } m_points[i].updatePosition(deltaTime); } for (int i = 0; i < m_points.size(); ) { if (m_points[i].isOffscreen(size())) { m_points.removeAt(i); if (m_points.size() < m_maxPoints) { generateRandomPoint(); } } else { ++i; } } while (m_points.size() < m_maxPoints) { generateRandomPoint(); } update(); } void MovingPointsWidget::generateRandomPoint() { QPointF startPos; QPointF direction; int edge = QRandomGenerator::global()->bounded(4); switch (edge) { case 0: startPos = QPointF(QRandomGenerator::global()->bounded(width()), -5.0); direction = generateRandomDirection(); if (direction.y() < 0) direction.setY(-direction.y()); break; case 1: startPos = QPointF(width() + 5.0, QRandomGenerator::global()->bounded(height())); direction = generateRandomDirection(); if (direction.x() > 0) direction.setX(-direction.x()); break; case 2: startPos = QPointF(QRandomGenerator::global()->bounded(width()), height() + 5.0); direction = generateRandomDirection(); if (direction.y() > 0) direction.setY(-direction.y()); break; case 3: startPos = QPointF(-5.0, QRandomGenerator::global()->bounded(height())); direction = generateRandomDirection(); if (direction.x() < 0) direction.setX(-direction.x()); break; } QColor color(QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256)); m_points.append(MovingPoint(startPos, direction, m_pointSpeed, color)); } QPointF MovingPointsWidget::generateRandomDirection() const { double angle = QRandomGenerator::global()->bounded(360.0) * M_PI / 180.0; return QPointF(qCos(angle), qSin(angle)); } QPointF MovingPointsWidget::calculateHexagonPoint(const QPointF& center, int index, double radius) const { double angle_rad = M_PI / 3.0 * index; angle_rad += M_PI / 6.0; return QPointF(center.x() + radius * qCos(angle_rad), center.y() + radius * qSin(angle_rad)); } ================================================ FILE: Anime_Template_Project/Star_Sky_Connection/MovingPointsWidget.h ================================================ #ifndef MOVINGPOINTSWIDGET_H #define MOVINGPOINTSWIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include class MovingPoint { public: explicit MovingPoint(const QPointF& pos, const QPointF& direction, double speed, const QColor& color); void updatePosition(double deltaTime); void setDirection(const QPointF& newDirection); QPointF getPosition() const; QColor getColor() const; QPointF getDirection() const; bool isOffscreen(const QSizeF& windowSize) const; private: QPointF m_position; QPointF m_direction; double m_speed; QColor m_color; }; class MovingPointsWidget : public QWidget { Q_OBJECT public: explicit MovingPointsWidget(QWidget *parent = nullptr); ~MovingPointsWidget(); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void enterEvent(QEnterEvent *event) override; void leaveEvent(QEvent *event) override; private slots: void updatePoints(); private: QVector m_points; QTimer *m_updateTimer; int m_maxPoints = 150; double m_pointSpeed = 90; int m_timerInterval = 15; bool m_mouseInWidget = false; QPointF m_mousePosition; double m_attractionRadius = 250.0; void generateRandomPoint(); QPointF generateRandomDirection() const; QPointF calculateHexagonPoint(const QPointF& center, int index, double radius) const; }; #endif ================================================ FILE: Anime_Template_Project/TreeScene/TreeScene.cpp ================================================ #include "TreeScene.h" #include #include #include #include #define PI 3.14159265358979323846 TreeScene::TreeScene(QWidget *parent) : QWidget(parent), root(nullptr), currentGrowthIndex(0), currentSakuraGrowthIndex(0), shakeAngle(0), shakeDirection(1), maxShakeAngle(2.0), shakeSpeed(0.1), fallingSakuraSpeedFactor(0.5), currentStage(TreeGrowing) { setFixedSize(800, 600); setStyleSheet("background-color: lightblue;"); treeGrowthTimer = new QTimer(this); connect(treeGrowthTimer, &QTimer::timeout, this, &TreeScene::updateTreeGrowth); treeGrowthTimer->start(50); sakuraGrowthTimer = new QTimer(this); connect(sakuraGrowthTimer, &QTimer::timeout, this, &TreeScene::updateSakuraGrowth); treeShakeTimer = new QTimer(this); connect(treeShakeTimer, &QTimer::timeout, this, &TreeScene::updateTreeShake); fallingSakuraTimer = new QTimer(this); connect(fallingSakuraTimer, &QTimer::timeout, this, &TreeScene::updateFallingSakura); sakuraDropTriggerTimer = new QTimer(this); connect(sakuraDropTriggerTimer, &QTimer::timeout, this, &TreeScene::startSakuraDropTimer); QPointF startPoint(width() / 2, height() - 50); root = new TreeNode(); root->startPoint = startPoint; root->endPoint = startPoint; root->length = 0; root->angle = -90; root->depth = 0; root->indexInAllNodes = allNodes.size(); allNodes.append(root); generateTree(root, 0, root->startPoint, root->angle, 0); qDebug() << "Total nodes generated:" << allNodes.size(); qDebug() << "Leaf nodes generated:" << leafNodes.size(); qDebug() << "Flower-bearing nodes generated (depth >= 5):" << flowerBearingNodes.size(); growthElapsedTimer.start(); } TreeScene::~TreeScene() { delete root; } void TreeScene::generateTree(TreeNode* node, int depth, QPointF start, qreal angle, qreal length) { bool shouldBeLeaf = (depth >= 9); node->startPoint = start; node->angle = angle; node->depth = depth; qreal branchLengthFactor = QRandomGenerator::global()->generateDouble() * (1.2 - 0.8) + 0.8; qreal branchLength = (200.0 / (depth + 1.0)) * branchLengthFactor; node->length = branchLength; node->endPoint.setX(start.x() + branchLength * std::cos(angle * PI / 180.0)); node->endPoint.setY(start.y() + branchLength * std::sin(angle * PI / 180.0)); node->isLeaf = shouldBeLeaf; if (node->depth >= 5) { flowerBearingNodes.append(node); } if (node->depth < 8) { qreal angleOffset1 = QRandomGenerator::global()->bounded(0, 15); qreal branchAngle1 = angle - 25 - angleOffset1; qreal angleOffset2 = QRandomGenerator::global()->bounded(0, 15); qreal branchAngle2 = angle + 25 + angleOffset2; node->left = new TreeNode(); node->left->parent = node; node->left->indexInAllNodes = allNodes.size(); allNodes.append(node->left); generateTree(node->left, depth + 1, node->endPoint, branchAngle1, branchLength * 0.8); node->right = new TreeNode(); node->right->parent = node; node->right->indexInAllNodes = allNodes.size(); allNodes.append(node->right); generateTree(node->right, depth + 1, node->endPoint, branchAngle2, branchLength * 0.8); } else { } } void TreeScene::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); drawTree(painter, root); for (int i = 0; i < growingSakuras.size(); ++i) { if (i < flowerBearingNodes.size() && flowerBearingNodes[i]->hasSakura) { drawSakura(painter, flowerBearingNodes[i]->endPoint, growingSakuras[i]); } } for (const auto& sakura : fallingSakuras) { drawFallingSakura(painter, sakura); } } void TreeScene::drawTree(QPainter& painter, TreeNode* node) { if (!node) return; QPen pen(Qt::darkGreen); pen.setWidth(qMax(1, 10 - node->depth * 1)); painter.setPen(pen); painter.save(); if (currentStage == TreeShakingAndSakuraFalling) { qreal maxTreeDepth = 9.0; qreal depthShakeFactor = static_cast(node->depth) / maxTreeDepth; qreal currentBranchShakeAngle = shakeAngle * depthShakeFactor; painter.translate(node->startPoint); painter.rotate(currentBranchShakeAngle); painter.translate(-node->startPoint); } if (currentStage == TreeGrowing) { int nodeIndex = node->indexInAllNodes; if (nodeIndex <= currentGrowthIndex) { qreal progress = 1.0; if (nodeIndex == currentGrowthIndex) { qint64 elapsedMs = growthElapsedTimer.elapsed(); qreal growthDurationPerNode = 50.0; progress = qMin(1.0, elapsedMs / growthDurationPerNode); } QPointF actualEndPoint; actualEndPoint.setX(node->startPoint.x() + node->length * progress * std::cos(node->angle * PI / 180.0)); actualEndPoint.setY(node->startPoint.y() + node->length * progress * std::sin(node->angle * PI / 180.0)); painter.drawLine(node->startPoint, actualEndPoint); } } else { painter.drawLine(node->startPoint, node->endPoint); } painter.restore(); drawTree(painter, node->left); drawTree(painter, node->right); } void TreeScene::drawSakura(QPainter& painter, const QPointF& center, const QVector& petals) { painter.setBrush(SAKURA_COLOR); painter.setPen(Qt::NoPen); QPainterPath petalPath; qreal baseWidth = 10.0; qreal baseHeight = 15.0; petalPath.moveTo(0, baseHeight / 2.0); petalPath.cubicTo(baseWidth * 0.4, baseHeight * 0.3, baseWidth * 0.4, -baseHeight * 0.3, 0, -baseHeight / 2.0); petalPath.cubicTo(-baseWidth * 0.4, -baseHeight * 0.3, -baseWidth * 0.4, baseHeight * 0.3, 0, baseHeight / 2.0); petalPath.closeSubpath(); for (const auto& petal : petals) { painter.save(); painter.translate(center); painter.rotate(petal.rotation); painter.setOpacity(petal.opacity); painter.scale(petal.sizeFactor, petal.sizeFactor); painter.translate(petal.relativePos); painter.drawPath(petalPath); painter.restore(); } painter.setOpacity(1.0); } void TreeScene::drawFallingSakura(QPainter& painter, const FallingSakura& sakura) { painter.setBrush(SAKURA_COLOR); painter.setPen(Qt::NoPen); painter.setOpacity(sakura.opacity); QPainterPath petalPath; qreal baseWidth = 10.0; qreal baseHeight = 15.0; petalPath.moveTo(0, baseHeight / 2.0); petalPath.cubicTo(baseWidth * 0.4, baseHeight * 0.3, baseWidth * 0.4, -baseHeight * 0.3, 0, -baseHeight / 2.0); petalPath.cubicTo(-baseWidth * 0.4, -baseHeight * 0.3, -baseWidth * 0.4, baseHeight * 0.3, 0, baseHeight / 2.0); petalPath.closeSubpath(); for (const auto& petal : sakura.petals) { painter.save(); painter.translate(sakura.position); painter.rotate(sakura.rotation + petal.rotation); painter.drawPath(petalPath); painter.restore(); } painter.setOpacity(1.0); } QVector TreeScene::generateSakuraPetals() { QVector petals; int numPetals = 5; qreal petalAngleStep = 360.0 / numPetals; for (int i = 0; i < numPetals; ++i) { SakuraPetal petal; petal.relativePos = QPointF(0, 0); petal.rotation = i * petalAngleStep; petal.opacity = 1.0; petal.sizeFactor = 0.3; petals.append(petal); } return petals; } void TreeScene::updateTreeGrowth() { if (currentGrowthIndex < allNodes.size()) { TreeNode* currentNode = allNodes[currentGrowthIndex]; qint64 elapsedMs = growthElapsedTimer.elapsed(); qreal growthDurationPerNode = 50.0; if (elapsedMs >= growthDurationPerNode) { currentGrowthIndex++; growthElapsedTimer.restart(); } update(); } else { treeGrowthTimer->stop(); qDebug() << "Tree growth finished."; currentStage = SakuraGrowing; if (!flowerBearingNodes.isEmpty()) { for(int i = 0; i < flowerBearingNodes.size(); ++i) { growingSakuras.append(generateSakuraPetals()); } sakuraGrowthTimer->start(30); qDebug() << "Sakura growth started. Growing " << growingSakuras.size() << " sakuras."; } else { qDebug() << "No flower-bearing nodes found, skipping sakura growth."; currentStage = TreeShakingAndSakuraFalling; treeShakeTimer->start(20); sakuraDropTriggerTimer->start(QRandomGenerator::global()->bounded(500, 1500)); fallingSakuraTimer->start(30); qDebug() << "Tree shaking and sakura falling started (no sakuras grown)."; } } } void TreeScene::updateSakuraGrowth() { if (currentSakuraGrowthIndex < flowerBearingNodes.size()) { TreeNode* currentFlowerNode = flowerBearingNodes[currentSakuraGrowthIndex]; currentFlowerNode->hasSakura = true; QVector& petals = growingSakuras[currentSakuraGrowthIndex]; bool allPetalsGrown = true; for (auto& petal : petals) { if (petal.sizeFactor < 1.0) { petal.sizeFactor += 0.2; petal.relativePos.setX(petal.relativePos.x() + (QRandomGenerator::global()->generateDouble() - 0.5)); petal.relativePos.setY(petal.relativePos.y() + (QRandomGenerator::global()->generateDouble() - 0.5)); allPetalsGrown = false; } } update(); if (allPetalsGrown) { currentSakuraGrowthIndex++; } } else { sakuraGrowthTimer->stop(); qDebug() << "Sakura growth finished."; currentStage = TreeShakingAndSakuraFalling; treeShakeTimer->start(20); sakuraDropTriggerTimer->start(QRandomGenerator::global()->bounded(500, 1500)); fallingSakuraTimer->start(30); qDebug() << "Tree shaking and sakura falling started."; } } void TreeScene::updateTreeShake() { shakeAngle += shakeDirection * shakeSpeed; if (std::abs(shakeAngle) >= maxShakeAngle) { shakeDirection *= -1; } update(); } void TreeScene::startSakuraDropTimer() { FallingSakura newSakura; if (!flowerBearingNodes.isEmpty()) { int flowerNodeIndex = QRandomGenerator::global()->bounded(flowerBearingNodes.size()); if (flowerNodeIndex < growingSakuras.size()) { newSakura.position = flowerBearingNodes[flowerNodeIndex]->endPoint; newSakura.petals = growingSakuras[flowerNodeIndex]; } else { newSakura.position = QPointF(QRandomGenerator::global()->bounded(width()), QRandomGenerator::global()->bounded(height() / 4)); newSakura.petals = generateSakuraPetals(); for(auto& petal : newSakura.petals) petal.sizeFactor = 1.0; } } else { newSakura.position = QPointF(QRandomGenerator::global()->bounded(width()), QRandomGenerator::global()->bounded(height() / 2)); newSakura.petals = generateSakuraPetals(); for(auto& petal : newSakura.petals) petal.sizeFactor = 1.0; } qreal speedFactor = QRandomGenerator::global()->generateDouble() * (3.0 - 1.0) + 1.0; newSakura.speedY = speedFactor * fallingSakuraSpeedFactor; qreal rotationSpeedFactor = QRandomGenerator::global()->generateDouble() * (5.0 - (-5.0)) + (-5.0); newSakura.rotationSpeed = rotationSpeedFactor; newSakura.opacity = 1.0; fallingSakuras.append(newSakura); sakuraDropTriggerTimer->start(QRandomGenerator::global()->bounded(300, 1000)); } void TreeScene::updateFallingSakura() { for (int i = 0; i < fallingSakuras.size(); ++i) { FallingSakura& sakura = fallingSakuras[i]; sakura.position.setY(sakura.position.y() + sakura.speedY); qreal windOffsetX = QRandomGenerator::global()->generateDouble() - 0.5; sakura.position.setX(sakura.position.x() + windOffsetX); sakura.rotation += sakura.rotationSpeed; if (sakura.position.y() > height() * 0.75) { sakura.opacity -= 0.02; if (sakura.opacity < 0) { sakura.opacity = 0; } } } fallingSakuras.erase(std::remove_if(fallingSakuras.begin(), fallingSakuras.end(), [](const FallingSakura& s){ return s.opacity <= 0; }), fallingSakuras.end()); update(); } ================================================ FILE: Anime_Template_Project/TreeScene/TreeScene.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include const QColor SAKURA_COLOR = QColor(255, 192, 203); struct TreeNode { QPointF startPoint; QPointF endPoint; qreal length; qreal angle; TreeNode* parent; TreeNode* left; TreeNode* right; int depth; int indexInAllNodes; bool isLeaf; bool hasSakura; TreeNode() : parent(nullptr), left(nullptr), right(nullptr), depth(0), indexInAllNodes(-1), isLeaf(true), hasSakura(false) {} ~TreeNode() { delete left; delete right; } }; struct SakuraPetal { QPointF relativePos; qreal rotation; qreal opacity; qreal sizeFactor; }; struct FallingSakura { QPointF position; qreal rotation; qreal rotationSpeed; qreal speedY; qreal opacity; QVector petals; FallingSakura() : rotation(0), rotationSpeed(0), speedY(0), opacity(1.0) {} }; class TreeScene : public QWidget { Q_OBJECT public: explicit TreeScene(QWidget *parent = nullptr); ~TreeScene(); protected: void paintEvent(QPaintEvent *event) override; private slots: void updateTreeGrowth(); void updateSakuraGrowth(); void updateTreeShake(); void updateFallingSakura(); void startSakuraDropTimer(); private: TreeNode* root; QVector allNodes; QVector leafNodes; QVector flowerBearingNodes; int currentGrowthIndex; QTimer* treeGrowthTimer; QElapsedTimer growthElapsedTimer; QTimer* sakuraGrowthTimer; int currentSakuraGrowthIndex; QVector> growingSakuras; QTimer* treeShakeTimer; qreal shakeAngle; qreal shakeDirection; qreal maxShakeAngle; qreal shakeSpeed; QTimer* fallingSakuraTimer; QTimer* sakuraDropTriggerTimer; QVector fallingSakuras; qreal fallingSakuraSpeedFactor; enum AnimationStage { TreeGrowing, SakuraGrowing, TreeShakingAndSakuraFalling }; AnimationStage currentStage; void generateTree(TreeNode* node, int depth, QPointF start, qreal angle, qreal length); void drawTree(QPainter& painter, TreeNode* node); void drawSakura(QPainter& painter, const QPointF& center, const QVector& petals); void drawFallingSakura(QPainter& painter, const FallingSakura& sakura); QVector generateSakuraPetals(); }; ================================================ FILE: Anime_Template_Project/Wave/waveswidget.cpp ================================================ #include "waveswidget.h" #include #include #include WavesWidget::WavesWidget(QWidget *parent) : QWidget(parent), animationTimer(new QTimer(this)) { config.lineColor = Qt::white; config.waveSpeedX = 0.02; config.waveSpeedY = 0.01; config.waveAmpX = 40; config.waveAmpY = 20; config.xGap = 12; config.yGap = 36; config.friction = 0.90; config.tension = 0.01; config.maxCursorMove = 120; mouse.x = -10; mouse.y = 0; mouse.lx = 0; mouse.ly = 0; mouse.sx = 0; mouse.sy = 0; mouse.v = 0; mouse.vs = 0; mouse.a = 0; mouse.set = false; std::mt19937_64 rng(std::chrono::system_clock::now().time_since_epoch().count()); std::uniform_real_distribution<> dist(0.0, 1.0); noiseGenerator = new Noise(dist(rng)); connect(animationTimer, &QTimer::timeout, this, &WavesWidget::tick); animationTimer->start(500); setMouseTracking(true); timeElapsed.start(); } WavesWidget::~WavesWidget() { delete noiseGenerator; } void WavesWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(config.lineColor); for (const auto& points : lines) { if (points.empty()) continue; QPainterPath path; QPointF p1 = moved(points[0], false); path.moveTo(p1); for (size_t idx = 0; idx < points.size(); ++idx) { const Point& p = points[idx]; bool isLast = (idx == points.size() - 1); p1 = moved(p, !isLast); path.lineTo(p1); } painter.drawPath(path); } } void WavesWidget::mouseMoveEvent(QMouseEvent *event) { updateMouse(event->pos().x(), event->pos().y()); QWidget::mouseMoveEvent(event); } void WavesWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); setLines(); QWidget::resizeEvent(event); } void WavesWidget::tick() { qint64 currentTime = timeElapsed.elapsed(); mouse.sx += (mouse.x - mouse.sx) * 0.1; mouse.sy += (mouse.y - mouse.sy) * 0.1; qreal dx = mouse.x - mouse.lx; qreal dy = mouse.y - mouse.ly; qreal d = std::hypot(dx, dy); mouse.v = d; mouse.vs += (d - mouse.vs) * 0.1; mouse.vs = std::min(100.0, mouse.vs); mouse.lx = mouse.x; mouse.ly = mouse.y; mouse.a = std::atan2(dy, dx); movePoints(currentTime); update(); } void WavesWidget::setLines() { lines.clear(); qreal oWidth = width() + 200; qreal oHeight = height() + 30; int totalLines = static_cast(std::ceil(oWidth / config.xGap)); int totalPoints = static_cast(std::ceil(oHeight / config.yGap)); qreal xStart = (width() - config.xGap * totalLines) / 2.0; qreal yStart = (height() - config.yGap * totalPoints) / 2.0; for (int i = 0; i <= totalLines; ++i) { std::vector pts; for (int j = 0; j <= totalPoints; ++j) { pts.push_back({ xStart + config.xGap * i, yStart + config.yGap * j, {0, 0}, {0, 0, 0, 0} }); } lines.push_back(pts); } } void WavesWidget::movePoints(qint64 time) { for (auto& pts : lines) { for (auto& p : pts) { qreal move = noiseGenerator->perlin2( (p.x + time * config.waveSpeedX) * 0.002, (p.y + time * config.waveSpeedY) * 0.0015 ) * 12; p.wave.x = std::cos(move) * config.waveAmpX; p.wave.y = std::sin(move) * config.waveAmpY; qreal dx = p.x - mouse.sx; qreal dy = p.y - mouse.sy; qreal dist = std::hypot(dx, dy); qreal l = std::max(175.0, mouse.vs); if (dist < l) { qreal s = 1.0 - dist / l; qreal f = std::cos(dist * 0.001) * s; p.cursor.vx += std::cos(mouse.a) * f * l * mouse.vs * 0.00065; p.cursor.vy += std::sin(mouse.a) * f * l * mouse.vs * 0.00065; } p.cursor.vx += (0.0 - p.cursor.x) * config.tension; p.cursor.vy += (0.0 - p.cursor.y) * config.tension; p.cursor.vx *= config.friction; p.cursor.vy *= config.friction; p.cursor.x += p.cursor.vx * 2; p.cursor.y += p.cursor.vy * 2; p.cursor.x = std::min(config.maxCursorMove, std::max(-config.maxCursorMove, p.cursor.x)); p.cursor.y = std::min(config.maxCursorMove, std::max(-config.maxCursorMove, p.cursor.y)); } } } QPointF WavesWidget::moved(const Point& point, bool withCursor) { qreal x = point.x + point.wave.x + (withCursor ? point.cursor.x : 0); qreal y = point.y + point.wave.y + (withCursor ? point.cursor.y : 0); return QPointF(std::round(x * 10) / 10.0, std::round(y * 10) / 10.0); } void WavesWidget::updateMouse(int x, int y) { mouse.x = x; mouse.y = y; if (!mouse.set) { mouse.sx = mouse.x; mouse.sy = mouse.y; mouse.lx = mouse.x; mouse.ly = mouse.y; mouse.set = true; } } ================================================ FILE: Anime_Template_Project/Wave/waveswidget.h ================================================ #ifndef WAVESWIDGET_H #define WAVESWIDGET_H #include #include #include #include #include #include #include #include #include #include #include struct Grad { qreal x, y, z; Grad() : x(0.0), y(0.0), z(0.0) {} Grad(qreal gx, qreal gy, qreal gz) : x(gx), y(gy), gz(gz) {} qreal dot2(qreal dx, qreal dy) const { return x * dx + y * dy; } }; class Noise { public: Noise(qreal seedVal = 0) { grad3 = { Grad(1, 1, 0), Grad(-1, 1, 0), Grad(1, -1, 0), Grad(-1, -1, 0), Grad(1, 0, 1), Grad(-1, 0, 1), Grad(1, 0, -1), Grad(-1, 0, -1), Grad(0, 1, 1), Grad(0, -1, 1), Grad(0, 1, -1), Grad(0, -1, -1) }; p = {151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180}; perm.resize(512); gradP.resize(512); seed(seedVal); } void seed(qreal seedVal) { if (seedVal > 0 && seedVal < 1) seedVal *= 65536; seedVal = std::floor(seedVal); if (seedVal < 256) seedVal = static_cast(seedVal) | (static_cast(seedVal) << 8); for (int i = 0; i < 256; i++) { int v = (i & 1) ? (p[i] ^ (static_cast(seedVal) & 255)) : (p[i] ^ ((static_cast(seedVal) >> 8) & 255)); perm[i] = perm[i + 256] = v; gradP[i] = gradP[i + 256] = grad3[v % 12]; } } static qreal fade(qreal t) { return t * t * t * (t * (t * 6 - 15) + 10); } static qreal lerp(qreal a, qreal b, qreal t) { return (1 - t) * a + t * b; } qreal perlin2(qreal x, qreal y) const { int X = static_cast(std::floor(x)); int Y = static_cast(std::floor(y)); x -= X; y -= Y; X &= 255; Y &= 255; const Grad& n00 = gradP[X + perm[Y]]; const Grad& n01 = gradP[X + perm[Y + 1]]; const Grad& n10 = gradP[X + 1 + perm[Y]]; const Grad& n11 = gradP[X + 1 + perm[Y + 1]]; qreal u = fade(x); return lerp( lerp(n00.dot2(x, y), n10.dot2(x - 1, y), u), lerp(n01.dot2(x, y - 1), n11.dot2(x - 1, y - 1), u), fade(y) ); } private: std::vector grad3; std::vector p; std::vector perm; std::vector gradP; }; struct Point { qreal x, y; struct Wave { qreal x, y; } wave; struct Cursor { qreal x, y, vx, vy; } cursor; }; struct MouseState { qreal x, y; qreal lx, ly; qreal sx, sy; qreal v; qreal vs; qreal a; bool set; }; struct Config { QColor lineColor; qreal waveSpeedX; qreal waveSpeedY; qreal waveAmpX; qreal waveAmpY; qreal xGap; qreal yGap; qreal friction; qreal tension; qreal maxCursorMove; }; class WavesWidget : public QWidget { Q_OBJECT public: explicit WavesWidget(QWidget *parent = nullptr); ~WavesWidget(); void setLineColor(const QColor& color) { config.lineColor = color; update(); } void setWaveSpeedX(qreal speed) { config.waveSpeedX = speed; } void setWaveSpeedY(qreal speed) { config.waveSpeedY = speed; } void setWaveAmpX(qreal amp) { config.waveAmpX = amp; } void setWaveAmpY(qreal amp) { config.waveAmpY = amp; } void setXGap(qreal gap) { config.xGap = gap; setLines(); update(); } void setYGap(qreal gap) { config.yGap = gap; setLines(); update(); } void setFriction(qreal f) { config.friction = f; } void setTension(qreal t) { config.tension = t; } void setMaxCursorMove(qreal move) { config.maxCursorMove = move; } protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void tick(); private: QTimer *animationTimer; Noise *noiseGenerator = nullptr; std::vector> lines; MouseState mouse; Config config; QElapsedTimer timeElapsed; void setLines(); void movePoints(qint64 time); QPointF moved(const Point& point, bool withCursor = true); void updateMouse(int x, int y); }; #endif // WAVESWIDGET_H ================================================ FILE: Anime_Template_Project/Zipper_Slider/pathwidget.cpp ================================================ #include "PathWidget.h" PathWidget::PathWidget(QWidget *parent) : QWidget(parent) { QPalette palette = this->palette(); palette.setColor(QPalette::Window, QColor(224, 213, 185, 255)); this->setPalette(palette); resize(1200, 1200); } void PathWidget::setOffset(qreal offset) { yy3 -= offset; xx = (yy3 - y1) / 2; yy = yy3 - y1; update(); } void PathWidget::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); QPainterPath path; path.moveTo(xx3, yy3); path.cubicTo(xx3 + xx * 0.05, yy3 - yy * 0.1, xx3 + xx * 0.15, yy3 - yy * 0.5, xx3 + xx, y1); painter.drawPath(path); QPainterPath path1; path1.moveTo(xx3, yy3); path1.cubicTo(xx3 - xx * 0.05, yy3 - yy * 0.1, xx3 - xx * 0.15, yy3 - yy * 0.5, xx3 - xx, y1); painter.drawPath(path1); painter.drawLine(xxx3, yyy3, xx3, yy3); painter.setBrush(QColor(100, 104, 79, 255)); QPainterPath linePath; linePath.moveTo(xxx3, yyy3); linePath.lineTo(xx3, yy3); Draw_Straight_Path_Square(painter, linePath); Draw_Curve_Path_Square(painter, path); Draw_Curve_Path_Square(painter, path1); Draw_Rectangle(painter); } void PathWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_dragStartPosition = event->globalPos(); m_startWindowPosition = this->pos(); m_lastMovePosition = event->globalPos(); event->accept(); } else QWidget::mousePressEvent(event); } void PathWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { QPoint delta = event->globalPos() - m_lastMovePosition; m_totalMoveDistance += delta; if (m_totalMoveDistance.y() > 30) { this->setOffset(-10); if (yy3 > yyy3 - 10) yy3 = 600; m_totalMoveDistance = QPoint(0, 0); m_lastMovePosition = event->globalPos(); } else if (m_totalMoveDistance.y() < -30) { this->setOffset(10); if (yy3 < y1 + 10) yy3 = 310; m_totalMoveDistance = QPoint(0, 0); m_lastMovePosition = event->globalPos(); } event->accept(); } else QWidget::mouseMoveEvent(event); } void PathWidget::Draw_Straight_Path_Square(QPainter& painter, const QPainterPath& path) { const qreal step = 10.0; const qreal totalLength = path.length(); if (totalLength <= 0) return; for (qreal d = 0; d <= totalLength; d += step) { const qreal t = path.percentAtLength(d); const QPointF posx = path.pointAtPercent(t); const QPointF posy(xxx3, yyy3 - d); painter.drawRect(posx.x() - 5, posy.y() - 1, 10, 2); } } void PathWidget::Draw_Curve_Path_Square(QPainter& painter, const QPainterPath& path) { const qreal step = 10.0; const qreal totalLength = yy; if (totalLength <= 0) return; for (qreal d = 0; d <= totalLength; d += step) { const qreal t = path.percentAtLength(d); const QPointF posx = path.pointAtPercent(t); const QPointF posy(xxx3, yy3 - d); painter.drawRect(posx.x() - 5, posy.y() - 2, 10, 2); } } void PathWidget::Draw_Rectangle(QPainter &painter) { painter.setBrush(QColor(0, 0, 0, 255)); painter.drawRect(xx3 - 15, yy3, 30, 10); } ================================================ FILE: Anime_Template_Project/Zipper_Slider/pathwidget.h ================================================ #ifndef PATHWIDGET_H #define PATHWIDGET_H #include #include #include #include #include class PathWidget : public QWidget { Q_OBJECT public: explicit PathWidget(QWidget *parent = nullptr); void setOffset(qreal offset); void Draw_Straight_Path_Square(QPainter &painter, const QPainterPath &path); void Draw_Curve_Path_Square(QPainter &painter, const QPainterPath &path); void Draw_Rectangle(QPainter &painter); protected: void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent* event); // 鼠标按下事件 void mouseMoveEvent(QMouseEvent* event); // 鼠标移动事件 private: qreal y1 = 310; //终点 qreal xx = 150; //终点偏移 qreal yy = 300; qreal xx3 = 600; qreal yy3 = 600; qreal xxx3 = 600; qreal yyy3 = 600; QTimer m_timer; // 定时器 QPoint m_dragStartPosition; // 鼠标按下时的全局位置 QPoint m_startWindowPosition; // 窗口初始位置 QPoint m_lastMovePosition; QPoint m_totalMoveDistance; }; #endif // PATHWIDGET_H ================================================ FILE: Anime_Template_Project/button_class/diffusion_button.cpp ================================================ #include "diffusion_button.h" Diffusion_button::Diffusion_button(QWidget* parent) : QPushButton{ parent } { this->setCursor(Qt::PointingHandCursor); this->resize(130, 42); QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this); shadow->setOffset(0, 16); shadow->setBlurRadius(50); shadow->setColor(QColor(0, 0, 0, 166)); this->setGraphicsEffect(shadow); animation3 = new QPropertyAnimation(this, "radius"); animation3->setDuration(400); animation3->setStartValue(m_radius); animation3->setEndValue(this->width()); animation3->setEasingCurve(QEasingCurve::Linear); animation3->setDirection(QAbstractAnimation::Forward); animation1 = new QPropertyAnimation(this, "opacity"); animation1->setDuration(400); animation1->setStartValue(m_opacity); animation1->setEndValue(0); animation1->setEasingCurve(QEasingCurve::Linear); animation1->setDirection(QAbstractAnimation::Forward); connect(animation3, &QPropertyAnimation::finished, this, &Diffusion_button::reset_animation); connect(animation1, &QPropertyAnimation::finished, this, &Diffusion_button::reset_animation); } void Diffusion_button::draw_disappearing_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); QBrush brush(QColor(135, 206, 235, m_opacity)); painter.setBrush(brush); painter.drawEllipse(mouse_coordinates, m_radius, m_radius); } void Diffusion_button::execute_animation() { animation3->start(); animation1->start(); } void Diffusion_button::reset_animation() { m_radius = 0; m_opacity = 255; } void Diffusion_button::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 21, 21); painter.setClipPath(path); painter.setPen(Qt::NoPen); QBrush Brush(QColor(255, 255, 255, 255)); painter.setBrush(Brush); painter.drawRect(0, 0, width(), height()); this->draw_disappearing_circle(); } void Diffusion_button::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { mouse_coordinates = event->pos(); update(); execute_animation(); } } int Diffusion_button::radius() const { return m_radius; } void Diffusion_button::setRadius(int newRadius) { if (m_radius == newRadius) return; m_radius = newRadius; update(); emit radiusChanged(); } int Diffusion_button::opacity() const { return m_opacity; } void Diffusion_button::setOpacity(int newOpacity) { if (m_opacity == newOpacity) return; m_opacity = newOpacity; update(); emit opacityChanged(); } ================================================ FILE: Anime_Template_Project/button_class/diffusion_button.h ================================================ #ifndef DIFFUSION_BUTTON_H #define DIFFUSION_BUTTON_H #include #include #include #include #include #include class Diffusion_button : public QPushButton { Q_OBJECT Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL) Q_PROPERTY(int opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL) public: explicit Diffusion_button(QWidget* parent = nullptr); void draw_disappearing_circle(); void execute_animation(); QPoint mouse_coordinates; QPropertyAnimation* animation1; QPropertyAnimation* animation3; void reset_animation(); public: int radius() const; void setRadius(int newRadius); int opacity() const; void setOpacity(int newOpacity); signals: void radiusChanged(); void opacityChanged(); protected: void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); private: int m_radius = 0; int m_opacity = 255; }; #endif // DIFFUSION_BUTTON_H ================================================ FILE: Anime_Template_Project/button_class/wave_button.cpp ================================================ #include "wave_button.h" Wave_button::Wave_button(QWidget* parent) : QPushButton{ parent } { this->resize(147, 55); this->setCursor(Qt::PointingHandCursor); m_wave_position = sqrt(pow(width(), 2) + pow(width(), 2)) / 2; QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this); shadow->setOffset(0, 16); shadow->setBlurRadius(50); shadow->setColor(QColor(0, 0, 0, 255)); this->setGraphicsEffect(shadow); } double Wave_button::triangle_position() { return sqrt(pow(width(), 2) + pow(width(), 2)) / 2;; } void Wave_button::rotating_rounded_rectangle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 26, 26); painter.setClipPath(path); color = QColor(0, 0, 222, m_wave_transparency); QRect rect(0 - width() / 5 * 4, m_wave_position, width() * 2, width() * 2); painter.translate(rect.center()); painter.rotate(m_angle); painter.setPen(Qt::NoPen); painter.setBrush(color); painter.drawRoundedRect(-rect.width() / 2, -rect.height() / 2, rect.width(), rect.height(), width() * 0.9, width() * 0.9); } void Wave_button::rotating_rounded_rectangle1() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 26, 26); painter.setClipPath(path); color = QColor(0, 0, 222, m_wave_transparency); QRect rect1(0 - width() / 20, m_wave_position, width() * 2, width() * 2); painter.translate(rect1.center()); painter.rotate(m_right_angle); painter.setPen(Qt::NoPen); painter.setBrush(color); painter.drawRoundedRect(-rect1.width() / 2, -rect1.height() / 2, rect1.width(), rect1.height(), width() * 0.9, width() * 0.9); } void Wave_button::draw_border() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(0, 0, width(), height(), 26, 26); painter.setClipPath(path); QPen pen; pen.setWidth(8); pen.setColor(QColor(0, 0, 255, 255)); painter.setPen(pen); painter.drawRoundedRect(0, 0, width(), height(), 26, 26); } void Wave_button::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); QRect rect1(0, 0, width(), height()); QFont font1; font1.setPointSize(13); font1.setLetterSpacing(QFont::AbsoluteSpacing, 2); font1.setBold(true); painter.setFont(font1); QColor semiTransparent(255, 255, 255, 255); painter.setPen(semiTransparent); painter.drawText(rect1, Qt::AlignCenter, QString("Click")); } void Wave_button::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, width(), height()); this->rotating_rounded_rectangle(); this->rotating_rounded_rectangle1(); this->draw_border(); this->draw_text(); } void Wave_button::execute_animation() { QPropertyAnimation* animation1 = new QPropertyAnimation(this, "angle"); animation1->setDuration(execution_time); animation1->setStartValue(this->m_angle); animation1->setEndValue(1080); animation1->setEasingCurve(QEasingCurve::Linear); animation1->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation4 = new QPropertyAnimation(this, "right_angle"); animation4->setDuration(execution_time); animation4->setStartValue(this->m_right_angle); animation4->setEndValue(25); animation4->setEasingCurve(QEasingCurve::Linear); animation4->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation2 = new QPropertyAnimation(this, "wave_position"); animation2->setDuration(execution_time); animation2->setStartValue(this->m_wave_position); animation2->setEndValue(-height()); animation2->setEasingCurve(QEasingCurve::Linear); animation2->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation3 = new QPropertyAnimation(this, "wave_transparency"); animation3->setDuration(execution_time); animation3->setStartValue(this->m_wave_transparency); animation3->setEndValue(255); animation3->setEasingCurve(QEasingCurve::Linear); animation3->start(QAbstractAnimation::DeleteWhenStopped); } void Wave_button::restore_animation() { QPropertyAnimation* animation1 = new QPropertyAnimation(this, "angle"); animation1->setDuration(execution_time); animation1->setStartValue(this->m_angle); animation1->setEndValue(25); animation1->setEasingCurve(QEasingCurve::Linear); animation1->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation4 = new QPropertyAnimation(this, "right_angle"); animation4->setDuration(execution_time); animation4->setStartValue(this->m_right_angle); animation4->setEndValue(1080); animation4->setEasingCurve(QEasingCurve::Linear); animation4->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation2 = new QPropertyAnimation(this, "wave_position"); animation2->setDuration(execution_time); animation2->setStartValue(this->m_wave_position); animation2->setEndValue(this->triangle_position()); animation2->setEasingCurve(QEasingCurve::Linear); animation2->start(QAbstractAnimation::DeleteWhenStopped); QPropertyAnimation* animation3 = new QPropertyAnimation(this, "wave_transparency"); animation3->setDuration(execution_time); animation3->setStartValue(this->m_wave_transparency); animation3->setEndValue(100); animation3->setEasingCurve(QEasingCurve::Linear); animation3->start(QAbstractAnimation::DeleteWhenStopped); } void Wave_button::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { if (click_status == true) { execute_animation(); click_status = false; } else if (click_status == false) { restore_animation(); click_status = true; } } } int Wave_button::angle() const { return m_angle; } void Wave_button::setAngle(int newAngle) { if (m_angle == newAngle) return; m_angle = newAngle; update(); emit angleChanged(); } int Wave_button::wave_position() const { return m_wave_position; } void Wave_button::setWave_position(int newWave_position) { if (m_wave_position == newWave_position) return; m_wave_position = newWave_position; update(); emit wave_positionChanged(); } int Wave_button::wave_transparency() const { return m_wave_transparency; } void Wave_button::setWave_transparency(int newWave_transparency) { if (m_wave_transparency == newWave_transparency) return; m_wave_transparency = newWave_transparency; update(); emit wave_transparencyChanged(); } int Wave_button::right_angle() const { return m_right_angle; } void Wave_button::setRight_angle(int newRight_angle) { if (m_right_angle == newRight_angle) return; m_right_angle = newRight_angle; update(); emit right_angleChanged(); } ================================================ FILE: Anime_Template_Project/button_class/wave_button.h ================================================ #ifndef WAVE_BUTTON_H #define WAVE_BUTTON_H #include #include #include #include #include #include class Wave_button : public QPushButton { Q_OBJECT Q_PROPERTY(int angle READ angle WRITE setAngle NOTIFY angleChanged FINAL) Q_PROPERTY(int wave_position READ wave_position WRITE setWave_position NOTIFY wave_positionChanged FINAL) Q_PROPERTY(int wave_transparency READ wave_transparency WRITE setWave_transparency NOTIFY wave_transparencyChanged FINAL) Q_PROPERTY(int right_angle READ right_angle WRITE setRight_angle NOTIFY right_angleChanged FINAL) public: explicit Wave_button(QWidget* parent = nullptr); void rotating_rounded_rectangle(); void rotating_rounded_rectangle1(); QColor color; void draw_border(); void execute_animation(); void restore_animation(); double triangle_position(); bool click_status = true; int execution_time = 4000; void draw_text(); public: int angle() const; void setAngle(int newAngle); int wave_position() const; void setWave_position(int newWave_position); int wave_transparency() const; void setWave_transparency(int newWave_transparency); int right_angle() const; void setRight_angle(int newRight_angle); signals: void angleChanged(); void wave_positionChanged(); void wave_transparencyChanged(); void right_angleChanged(); protected: void paintEvent(QPaintEvent* event); void mousePressEvent(QMouseEvent* event); private: int m_angle = 25; int m_right_angle = 1080; int m_wave_position = 0; int m_wave_transparency = 100; }; #endif // WAVE_BUTTON_H ================================================ FILE: Anime_Template_Project/dial_class/knob_page.cpp ================================================ #include "knob_page.h" Knob_page::Knob_page(QWidget *parent) : QWidget{parent} { QPalette pal(this->palette()); pal.setColor(QPalette::Window, QColor(243, 246, 253)); this->setPalette(pal); this->resize(1000, 1000); temp_dial = new Temperature_dial(this); temp_dial->show(); QPoint Point = this->rect().center(); temp_dial->move(Point.x() - temp_dial->width() / 2, Point.y() - temp_dial->height() / 2); this->animations(); } void Knob_page::animations() { animation = new Timer_animation(this->temp_dial, "geometry"); animation->setDuration(400); animation->setStartValue(this->temp_dial->geometry()); animation->setEndValue(QRect(this->temp_dial->pos().x() - zoom_rate / 2, this->temp_dial->pos().y() - zoom_rate / 2, temp_dial->width() + zoom_rate, temp_dial->height() + zoom_rate)); connect(temp_dial, &Temperature_dial::execute_animation_signal, this, &Knob_page::execute_animation); } void Knob_page::execute_animation(Temperature_dial::AnimationState State) { if (State == Temperature_dial::Execute) { animation->setDirection(Timer_animation::Forward); animation->start(); } else if (State == Temperature_dial::Restore) { animation->setDirection(Timer_animation::Backward); animation->start(); } } ================================================ FILE: Anime_Template_Project/dial_class/knob_page.h ================================================ #ifndef KNOB_PAGE_H #define KNOB_PAGE_H #include #include "temperature_dial.h" #include "../utility_class/timer_animation.h" class Knob_page : public QWidget { Q_OBJECT public: explicit Knob_page(QWidget *parent = nullptr); Temperature_dial *temp_dial; void animations(); int zoom_rate = 40; Timer_animation *animation; public slots: void execute_animation(Temperature_dial::AnimationState State); signals: }; #endif // KNOB_PAGE_H ================================================ FILE: Anime_Template_Project/dial_class/temperature_dial.cpp ================================================ #include "temperature_dial.h" Temperature_dial::Temperature_dial(QWidget *parent) : QWidget(parent) { resize(260, 260); setMouseTracking(true); window_width = QRectF(0, 0, 260, 260); center = window_width.center(); radius = window_width.height() * 0.41; circleCenter = center + QPointF(0, radius); angle = getAngleInDegrees(); shadow = new QGraphicsDropShadowEffect(this); shadow->setOffset(0, 19); shadow->setBlurRadius(50); shadow->setColor(QColor(213, 224, 254, 255)); this->setGraphicsEffect(shadow); setMouseTracking(true); this->startAnimation(); } void Temperature_dial::startAnimation() { timer_animation = new Timer_animation(this->shadow, "blurRadius"); timer_animation->setDuration(400); timer_animation->setStartValue(50); timer_animation->setEndValue(150); connect(timer_animation, &Timer_animation::started, this, [this] { is_animation_running = false; m_previousAngle = atan2(circleCenter.y() - center.y(), circleCenter.x() - center.x()); }); } double Temperature_dial::getAngleInDegrees() const { double dx = circleCenter.x() - center.x(); double dy = circleCenter.y() - center.y(); double angle = atan2(dy, dx); double degrees = angle * (180.0 / M_PI); if (degrees < 0) { degrees += 360.0; } return degrees; } void Temperature_dial::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(0, 0, window_width.width(), window_width.height()); QPainterPath path; path.addRoundedRect(0, 0, window_width.width(), window_width.height(), window_width.width() / 10, window_width.height() / 10); painter.setClipPath(path); painter.fillRect(rect(), Qt::white); this->draw_empty_circle(); this->draw_crosshair(); this->draw_circular_gradient_shadow(); this->draw_circle(); this->draw_full_circle(); this->draw_text(); } void Temperature_dial::draw_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(window_width.x(), window_width.y(), window_width.width(), window_width.height()); painter.setPen(Qt::NoPen); painter.setBrush(Qt::black); painter.drawEllipse(circleCenter, 9, 9); } void Temperature_dial::draw_empty_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(window_width.x(), window_width.y(), window_width.width(), window_width.height()); QPen pen; pen.setWidth(3); pen.setColor(QColor(242, 242, 242, 255)); painter.setPen(pen); painter.setBrush(Qt::NoBrush); painter.drawEllipse(center, int(radius), int(radius)); } void Temperature_dial::draw_circular_gradient_shadow() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(window_width.x(), window_width.y(), window_width.width(), window_width.height()); painter.setPen(QPen(Qt::NoPen)); QColor Color = getColorForAngle(getAngleInDegrees()); QRadialGradient radialGrad(window_width.width() / 2, window_width.height() / 2, window_width.width() * 0.6, window_width.width() * 0.35, window_width.height() * 0.65); Color.setAlpha(255); radialGrad.setColorAt(0, Color); Color.setAlpha(222); radialGrad.setColorAt(0.3, Color); Color.setAlpha(111); radialGrad.setColorAt(0.5, Color); Color.setAlpha(0); radialGrad.setColorAt(0.7, Color); radialGrad.setColorAt(1, Color); painter.setBrush(radialGrad); painter.drawRect(window_width); } void Temperature_dial::draw_full_circle() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(window_width.x(), window_width.y(), window_width.width(), window_width.height()); painter.setPen(QPen(Qt::NoPen)); painter.setBrush(Qt::white); painter.drawEllipse(center, int(window_width.width() * 0.28), int(window_width.width() * 0.28)); } void Temperature_dial::draw_crosshair() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(window_width.x(), window_width.y(), window_width.width(), window_width.height()); QPen pen(Qt::black, 2); painter.setPen(pen); painter.drawLine(center, center + QPoint(0, window_width.height() * 0.31)); painter.drawLine(center, center + QPoint(0, -window_width.height() * 0.31)); painter.drawLine(center, center + QPoint(window_width.height() * 0.31, 0)); painter.drawLine(center, center + QPoint(-window_width.height() * 0.31, 0)); } void Temperature_dial::draw_text() { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setViewport(0, 0, width(), height()); painter.setWindow(window_width.x(), window_width.y(), window_width.width(), window_width.height()); if (is_animation_running == true) { integerPart = QString::number(int(angle)); decimalPart = QString::number(angle - int(angle), 'f', 1).mid(2); angle = getAngleInDegrees(); } draw_text_template(integerPart, 0.12, QPointF(0.31, 0.54), Qt::AlignRight | Qt::AlignHCenter); draw_text_template(".", 0.1, QPointF(0.34, 0.56), Qt::AlignRight | Qt::AlignHCenter); draw_text_template(decimalPart, 0.12, QPointF(0.41, 0.54), Qt::AlignRight | Qt::AlignHCenter); draw_text_template("°", 0.1, QPointF(0.45, 0.54), Qt::AlignRight | Qt::AlignHCenter); } void Temperature_dial::draw_text_template(const QString& text, float fontScale, QPointF positionRatio, const int& alignment) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); int baseSize = qMin(width(), height()); int dynamicFontSize = baseSize * fontScale; QFont font("Arial"); font.setPixelSize(dynamicFontSize); font.setWeight(QFont::DemiBold); painter.setFont(font); QRect textRect; textRect.setSize(QSize(width() * 0.4, height() * 0.2)); textRect.moveTo( width() * positionRatio.x() - textRect.width() / 2, height() * positionRatio.y() - textRect.height() / 2 ); painter.setPen(Qt::black); painter.drawText(textRect, alignment, text); } QColor Temperature_dial::getColorForAngle(int angle) { if (angle <= 120) { return interpolateColor(QColor(235, 255, 188), QColor(255, 245, 188), angle / 120.0); } else if (angle <= 240) { return interpolateColor(QColor(255, 245, 188), QColor(255, 216, 188), (angle - 120) / 120.0); } else { return interpolateColor(QColor(255, 216, 188), QColor(235, 255, 188), (angle - 240) / 120.0); } } QColor Temperature_dial::interpolateColor(const QColor& start, const QColor& end, double t) { int r = static_cast(start.red() + t * (end.red() - start.red())); int g = static_cast(start.green() + t * (end.green() - start.green())); int b = static_cast(start.blue() + t * (end.blue() - start.blue())); int a = static_cast(start.alpha() + t * (end.alpha() - start.alpha())); return QColor(r, g, b, a); } void Temperature_dial::set_size(QRect size) { this->window_width = size; } void Temperature_dial::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { double distance = sqrt(pow(event->pos().x() - circleCenter.x(), 2) + pow(event->pos().y() - circleCenter.y(), 2)); if (distance <= 20) { this->setCursor(Qt::PointingHandCursor); isDragging = true; is_animation_running = true; } } } void Temperature_dial::mouseMoveEvent(QMouseEvent *event) { if (isDragging) { QPoint newPos = event->pos(); m_previousAngle = atan2(newPos.y() - center.y(), newPos.x() - center.x()); circleCenter.setX(center.x() + radius * cos(m_previousAngle)); circleCenter.setY(center.y() + radius * sin(m_previousAngle)); update(); } } void Temperature_dial::resizeEvent(QResizeEvent* event) { window_width = QRect(0, 0, event->size().width(), event->size().height()); center = window_width.center(); radius = window_width.height() * 0.41; circleCenter.setX(center.x() + radius * cos(m_previousAngle)); circleCenter.setY(center.y() + radius * sin(m_previousAngle)); fontSize = static_cast(window_width.height() * 0.1); update(); QWidget::resizeEvent(event); } void Temperature_dial::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { isDragging = false; this->setCursor(Qt::ArrowCursor); } } bool Temperature_dial::event(QEvent* e) { if (e->type() == QEvent::Enter) { execute_animation_signal(AnimationState::Execute); timer_animation->setDirection(Timer_animation::Forward); timer_animation->start(); } else if (e->type() == QEvent::Leave) { execute_animation_signal(AnimationState::Restore); timer_animation->setDirection(Timer_animation::Backward); timer_animation->start(); } return QWidget::event(e); } int Temperature_dial::shadow_scale() const { return m_shadow_scale; } void Temperature_dial::setShadow_scale(int newShadow_scale) { if (m_shadow_scale == newShadow_scale) return; m_shadow_scale = newShadow_scale; emit shadow_scaleChanged(); } ================================================ FILE: Anime_Template_Project/dial_class/temperature_dial.h ================================================ #ifndef TEMPERATURE_DIAL_H #define TEMPERATURE_DIAL_H #include #include #include #include #include #include "../utility_class/timer_animation.h" class Temperature_dial : public QWidget { Q_OBJECT Q_PROPERTY(int shadow_scale READ shadow_scale WRITE setShadow_scale NOTIFY shadow_scaleChanged FINAL) public: explicit Temperature_dial(QWidget *parent = nullptr); enum AnimationState { Execute, Restore }; void startAnimation(); void draw_text(); void draw_text_template(const QString& text, float fontScale, QPointF positionRatio, const int& alignment); void draw_circle(); void draw_empty_circle(); void draw_full_circle(); void draw_crosshair(); void draw_circular_gradient_shadow(); QColor interpolateColor(const QColor& start, const QColor& end, double t); QColor getColorForAngle(int angle); void set_size(QRect size); public: double getAngleInDegrees() const; int shadow_scale() const; void setShadow_scale(int newShadow_scale); private slots: signals: void shadow_scaleChanged(); void execute_animation_signal(Temperature_dial::AnimationState state); protected: void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void resizeEvent(QResizeEvent *event); void mouseReleaseEvent(QMouseEvent *event); bool event(QEvent* e); private: QPointF center; QPointF circleCenter; double radius; double m_previousAngle = M_PI_2; bool isDragging = false; Timer_animation * timer_animation; bool is_animation_running = true; double angle; QString integerPart; QString decimalPart; QRectF window_width; QGraphicsDropShadowEffect* shadow; int m_shadow_scale; int zoom_rate = 40; int fontSize; }; #endif // TEMPERATURE_DIAL_H ================================================ FILE: Anime_Template_Project/json/Anime_care_attributes.json ================================================ { "Anime_img_str_list": [ { "img": "://img/card_image1.png", "str_1": "无职转生~到了异世界就拿出真本事~", "str_2": "無職転生 ~異世界行ったら本気だす~", "anime_url": "https://bangumi.tv/subject/277554" }, { "img": "://img/card_image2.png", "str_1": "Code Geass 反叛的鲁路修", "str_2": "コードギアス 反逆のルルーシュ", "anime_url": "https://bangumi.tv/subject/793" }, { "img": "://img/card_image3.png", "str_1": "凉宫春日的忧郁", "str_2": "涼宮ハルヒの憂鬱", "anime_url": "https://bangumi.tv/subject/1606" }, { "img": "://img/card_image4.png", "str_1": "CLANNAD", "str_2": "CLANNAD -クラナド", "anime_url": "https://bangumi.tv/subject/51" }, { "img": "://img/card_image5.png", "str_1": "我的青春恋爱物语果然有问题", "str_2": "やはり俺の青春ラブコメはまちがっている", "anime_url": "https://bangumi.tv/subject/54433" }, { "img": "://img/card_image6.png", "str_1": "命运石之门", "str_2": "STEINS;GATE", "anime_url": "https://bangumi.tv/subject/10380" }, { "img": "://img/card_image7.png", "str_1": "JOJO的奇妙冒险", "str_2": "ジョジョの奇妙な冒険", "anime_url": "https://bangumi.tv/subject/43558" } ] } ================================================ FILE: Anime_Template_Project/src.qrc ================================================ img/card_image1.png img/card_image2.png img/card_image3.png img/card_image4.png img/card_image5.png img/card_image6.png img/card_image7.png img/account.png img/password.png img/facebook.png img/github-fill.png img/instagram.png img/logo_google.png img/email.png img/card_image1.jpg img/card_image2.jpg img/card_image3.jpg img/card_image4.jpg img/card_image5.jpg img/card_image6.jpg img/card_image7.jpg json/Anime_care_attributes.json ================================================ FILE: Anime_Template_Project/utility_class/timer_animation.cpp ================================================ #include "timer_animation.h" #include #include Timer_animation::Timer_animation(QObject* target, const QByteArray& propertyName, QObject* parent) : QObject(parent), m_target(target), m_propertyName(propertyName), m_previousValue(QVariant()) { m_timer.setInterval(16); m_timer.setTimerType(Qt::PreciseTimer); connect(&m_timer, &QTimer::timeout, this, &Timer_animation::update); if (m_target && !m_propertyName.isEmpty()) { int propIndex = m_target->metaObject()->indexOfProperty(m_propertyName); if (propIndex < 0) { qWarning("属性 '%s'未找到", m_propertyName.constData()); } else { QMetaProperty prop = m_target->metaObject()->property(propIndex); if (!prop.isWritable()) { qWarning("属性 '%s' 是只读", m_propertyName.constData()); } } } } void Timer_animation::calculateInterval() { if (!m_target || m_propertyName.isEmpty()) return; QMetaProperty prop = m_target->metaObject()->property( m_target->metaObject()->indexOfProperty(m_propertyName) ); const int type = prop.userType(); if (type == QMetaType::Int) { const int delta = qAbs(m_endValue.toInt() - m_startValue.toInt()); if (delta == 0) return; const int idealInterval = qMax(1, m_duration / delta); m_timer.setInterval(qBound(1, idealInterval, 16)); } else if (type == QMetaType::Double) { const double delta = qAbs(m_endValue.toDouble() - m_startValue.toDouble()); const double stepSize = 0.5; const int steps = qCeil(delta / stepSize); const int idealInterval = qMax(1, m_duration / steps); m_timer.setInterval(qBound(1, idealInterval, 16)); } else { m_timer.setInterval(16); } } void Timer_animation::setStartValue(const QVariant& value) { m_startValue = value; } void Timer_animation::setEndValue(const QVariant& value) { m_endValue = value; } void Timer_animation::setDuration(int msecs) { if (msecs > 0) m_duration = msecs; } void Timer_animation::setDirection(Direction dir) { if (m_direction == dir) return; m_direction = dir; if (m_state == Running) { qint64 current = QDateTime::currentMSecsSinceEpoch(); qreal elapsed = current - m_startTime; progress = elapsed / m_duration; m_startTime = current - (m_duration * (1 - progress)); } } void Timer_animation::start(bool autoDelete) { if (m_state == Running) return; m_autoDelete = autoDelete; if (!m_target || m_propertyName.isEmpty()) return; QMetaProperty prop = m_target->metaObject()->property( m_target->metaObject()->indexOfProperty(m_propertyName) ); if (!m_startValue.canConvert(prop.userType()) || !m_endValue.canConvert(prop.userType())) return; m_previousValue = (m_direction == Forward) ? m_startValue : m_endValue; progress = 0.0; if (m_state == Paused) { qint64 totalElapsed = m_pausedTime - m_startTime; progress = qreal(totalElapsed) / m_duration; } m_startTime = QDateTime::currentMSecsSinceEpoch() - (m_duration * progress); calculateInterval(); m_timer.start(); setState(Running); emit started(); } void Timer_animation::pause() { if (m_state != Running) return; m_timer.stop(); m_pausedTime = QDateTime::currentMSecsSinceEpoch(); setState(Paused); } void Timer_animation::resume() { if (m_state != Paused) return; qint64 pauseDuration = QDateTime::currentMSecsSinceEpoch() - m_pausedTime; m_startTime += pauseDuration; m_timer.start(); setState(Running); } void Timer_animation::stop() { m_timer.stop(); if (m_autoDelete) this->deleteLater(); m_previousValue = QVariant(); setState(Stopped); } void Timer_animation::update() { qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); qreal elapsed = currentTime - m_startTime; qreal progress = elapsed / m_duration; if (m_direction == Backward) progress = 1.0 - progress; if ((m_direction == Forward && progress >= 1.0) || (m_direction == Backward && progress <= 0.0)) { progress = qBound(0.0, progress, 1.0); QVariant finalValue = (m_direction == Forward) ? m_endValue : m_startValue; m_target->setProperty(m_propertyName, finalValue); emit valueChanged(finalValue); if (m_previousValue.isValid() && m_previousValue.userType() == finalValue.userType()) { QVariant finalIncrement = calculateIncrement(m_previousValue, finalValue); emit incrementChanged(finalIncrement); } stop(); emit finished(); return; } QVariant value = interpolate(progress); if (value.isValid()) { m_target->setProperty(m_propertyName, value); emit valueChanged(value); if (m_previousValue.isValid() && m_previousValue.userType() == value.userType()) { QVariant increment = calculateIncrement(m_previousValue, value); emit incrementChanged(increment); } m_previousValue = value; } } QVariant Timer_animation::calculateIncrement(const QVariant &prev, const QVariant &curr) const { if (prev.userType() != curr.userType()) return QVariant(); switch (prev.userType()) { case QMetaType::Int: return curr.toInt() - prev.toInt(); case QMetaType::Double: return curr.toDouble() - prev.toDouble(); case QMetaType::QColor: { QColor prevColor = prev.value(); QColor currColor = curr.value(); return QColor( currColor.red() - prevColor.red(), currColor.green() - prevColor.green(), currColor.blue() - prevColor.blue(), currColor.alpha() - prevColor.alpha() ); } case QMetaType::QPoint: return curr.toPoint() - prev.toPoint(); case QMetaType::QPointF: return curr.toPointF() - prev.toPointF(); case QMetaType::QSize: { QSize prevSize = prev.toSize(); QSize currSize = curr.toSize(); return QSize( currSize.width() - prevSize.width(), currSize.height() - prevSize.height() ); } case QMetaType::QSizeF: { QSizeF prevSize = prev.toSizeF(); QSizeF currSize = curr.toSizeF(); return QSizeF( currSize.width() - prevSize.width(), currSize.height() - prevSize.height() ); } case QMetaType::QRect: { QRect prevRect = prev.toRect(); QRect currRect = curr.toRect(); return QRect( currRect.x() - prevRect.x(), currRect.y() - prevRect.y(), currRect.width() - prevRect.width(), currRect.height() - prevRect.height() ); } case QMetaType::QRectF: { QRectF prevRect = prev.toRectF(); QRectF currRect = curr.toRectF(); return QRectF( currRect.x() - prevRect.x(), currRect.y() - prevRect.y(), currRect.width() - prevRect.width(), currRect.height() - prevRect.height() ); } default: return QVariant(); } } void Timer_animation::setState(State newState) { if (m_state != newState) { m_state = newState; emit stateChanged(newState); } } QVariant Timer_animation::interpolate(qreal progress) const { qreal actualProgress = (m_direction == Forward) ? progress : (1.0 - progress); if (m_startValue.userType() != m_endValue.userType()) return QVariant(); switch (m_startValue.userType()) { case QMetaType::Int: { int start = m_startValue.toInt(); int end = m_endValue.toInt(); return start + (end - start) * progress; } case QMetaType::Double: { double start = m_startValue.toDouble(); double end = m_endValue.toDouble(); return start + (end - start) * progress; } case QMetaType::QColor: { QColor start = m_startValue.value(); QColor end = m_endValue.value(); return QColor( qBound(0, static_cast(start.red() + (end.red() - start.red()) * actualProgress), 255), qBound(0, static_cast(start.green() + (end.green() - start.green()) * progress), 255), qBound(0, static_cast(start.blue() + (end.blue() - start.blue()) * progress), 255), qBound(0, static_cast(start.alpha() + (end.alpha() - start.alpha()) * progress), 255) ); } case QMetaType::QPoint: { QPoint start = m_startValue.toPoint(); QPoint end = m_endValue.toPoint(); return QPoint( start.x() + (end.x() - start.x()) * progress, start.y() + (end.y() - start.y()) * progress ); } case QMetaType::QPointF: { QPointF start = m_startValue.toPointF(); QPointF end = m_endValue.toPointF(); return QPointF( start.x() + (end.x() - start.x()) * progress, start.y() + (end.y() - start.y()) * progress ); } case QMetaType::QSize: { QSize start = m_startValue.toSize(); QSize end = m_endValue.toSize(); return QSize( start.width() + (end.width() - start.width()) * progress, start.height() + (end.height() - start.height()) * progress ); } case QMetaType::QSizeF: { QSizeF start = m_startValue.toSizeF(); QSizeF end = m_endValue.toSizeF(); return QSizeF( start.width() + (end.width() - start.width()) * progress, start.height() + (end.height() - start.height()) * progress ); } case QMetaType::QRect: { QRect start = m_startValue.toRect(); QRect end = m_endValue.toRect(); return QRect( start.x() + (end.x() - start.x()) * progress, start.y() + (end.y() - start.y()) * progress, start.width() + (end.width() - start.width()) * progress, start.height() + (end.height() - start.height()) * progress ); } case QMetaType::QRectF: { QRectF start = m_startValue.toRectF(); QRectF end = m_endValue.toRectF(); return QRectF( start.x() + (end.x() - start.x()) * progress, start.y() + (end.y() - start.y()) * progress, start.width() + (end.width() - start.width()) * progress, start.height() + (end.height() - start.height()) * progress ); } default: return QVariant(); } } ================================================ FILE: Anime_Template_Project/utility_class/timer_animation.h ================================================ #ifndef TIMER_ANIMATION_H #define TIMER_ANIMATION_H #include #include #include #include #include #include #include #include class Timer_animation : public QObject { Q_OBJECT public: enum State { Running, Paused, Stopped }; Q_ENUM(State) enum Direction { Forward, Backward }; Q_ENUM(Direction) explicit Timer_animation(QObject *target = nullptr, const QByteArray &propertyName = QByteArray(), QObject *parent = nullptr); void setStartValue(const QVariant &value); void setEndValue(const QVariant &value); void setDuration(int msecs); void setDirection(Direction dir); QObject* targetObject() const { return m_target; } QByteArray propertyName() const { return m_propertyName; } QVariant startValue() const { return m_startValue; } QVariant endValue() const { return m_endValue; } int duration() const { return m_duration; } State state() const { return m_state; } Direction direction() const { return m_direction; } void setState(State newState); public slots: void start(bool autoDelete = false); void pause(); void resume(); void stop(); signals: void started(); void valueChanged(const QVariant ¤tValue); void incrementChanged(const QVariant &increment); void finished(); void stateChanged(State newState); private slots: void update(); private: QVariant m_previousValue; QVariant calculateIncrement(const QVariant &prev, const QVariant &curr) const; QVariant interpolate(qreal progress) const; void calculateInterval(); QObject *m_target = nullptr; QByteArray m_propertyName; QVariant m_startValue; QVariant m_endValue; int m_duration = 1000; QTimer m_timer; qint64 m_startTime = 0; qint64 m_pausedTime = 0; State m_state = Stopped; Direction m_direction = Forward; bool m_autoDelete = false; qreal progress; }; #endif // TIMER_ANIMATION_H ================================================ FILE: Anime_Template_Project/utility_class/timer_animationgroup.cpp ================================================ #include "timer_animationgroup.h" #include Timer_AnimationGroup::Timer_AnimationGroup(QObject *parent) : QObject(parent) { } void Timer_AnimationGroup::addAnimation(int x, Timer_animation* animation) { if (!animation) return; animation->setParent(this); m_animationGroups[x].append(animation); } void Timer_AnimationGroup::clearAnimations() { m_animationGroups.clear(); m_executionQueue.clear(); } void Timer_AnimationGroup::start() { if (m_isRunning) return; m_executionQueue = m_animationGroups.keys(); std::sort(m_executionQueue.begin(), m_executionQueue.end()); if (m_currentDirection == Timer_animation::Backward) { std::reverse(m_executionQueue.begin(), m_executionQueue.end()); } m_currentIndex = -1; m_isRunning = true; emit started(); processNextGroup(); } void Timer_AnimationGroup::processNextGroup() { m_currentIndex++; if (m_currentIndex >= m_executionQueue.size()) { m_isRunning = false; emit finished(); return; } int currentX = m_executionQueue[m_currentIndex]; m_lastProcessedX = currentX; auto& group = m_animationGroups[currentX]; if (group.isEmpty()) { processNextGroup(); return; } m_activeCount = group.size(); for (Timer_animation* anim : group) { connect(anim, &Timer_animation::finished, this, &Timer_AnimationGroup::handleAnimationFinished, Qt::UniqueConnection); anim->start(); } emit groupStarted(currentX); } void Timer_AnimationGroup::rebuildExecutionQueue() { m_executionQueue = m_animationGroups.keys(); if (m_currentDirection == Timer_animation::Forward) { std::sort(m_executionQueue.begin(), m_executionQueue.end()); } else { std::sort(m_executionQueue.begin(), m_executionQueue.end(), std::greater()); } } void Timer_AnimationGroup::restartFromCurrent() { QList remaining; if (m_lastProcessedX != -1) { int pos = m_executionQueue.indexOf(m_lastProcessedX); if (pos != -1) { remaining = m_executionQueue.mid(pos); } } rebuildExecutionQueue(); if (!remaining.isEmpty()) { int newStart = -1; for (int i = 0; i < m_executionQueue.size(); ++i) { if (m_executionQueue[i] == remaining.first()) { newStart = i; break; } } if (newStart != -1) m_executionQueue = m_executionQueue.mid(newStart); } m_currentIndex = -1; processNextGroup(); } void Timer_AnimationGroup::handleAnimationFinished() { m_activeCount--; if (m_activeCount <= 0) { emit groupFinished(m_lastProcessedX); processNextGroup(); } } void Timer_AnimationGroup::pause() { if (!m_isRunning) return; int currentX = m_executionQueue[m_currentIndex]; for (Timer_animation* anim : m_animationGroups.value(currentX)) { anim->pause(); } } void Timer_AnimationGroup::resume() { if (!m_isRunning) return; int currentX = m_executionQueue[m_currentIndex]; for (Timer_animation* anim : m_animationGroups.value(currentX)) { anim->resume(); } } void Timer_AnimationGroup::stop() { if (!m_isRunning) return; int currentX = m_executionQueue[m_currentIndex]; for (Timer_animation* anim : m_animationGroups.value(currentX)) { anim->stop(); } m_isRunning = false; emit finished(); } void Timer_AnimationGroup::setGroupDirection(Timer_animation::Direction dir) { if (m_currentDirection == dir) return; m_currentDirection = dir; for (auto& group : m_animationGroups) { for (Timer_animation* anim : group) { anim->setDirection(dir); } } if (m_isRunning) restartFromCurrent(); } ================================================ FILE: Anime_Template_Project/utility_class/timer_animationgroup.h ================================================ #ifndef ANIMATION_GROUP_H #define ANIMATION_GROUP_H #include #include #include #include "timer_animation.h" class Timer_AnimationGroup : public QObject { Q_OBJECT public: explicit Timer_AnimationGroup(QObject *parent = nullptr); void addAnimation(int x, Timer_animation* animation); void clearAnimations(); void start(); void pause(); void resume(); void stop(); void setGroupDirection(Timer_animation::Direction dir); Timer_animation::Direction groupDirection() const { return m_currentDirection; } signals: void started(); void groupStarted(int x); void groupFinished(int x); void finished(); private slots: void handleAnimationFinished(); private: void processNextGroup(); void rebuildExecutionQueue(); void restartFromCurrent(); QMap> m_animationGroups; QList m_executionQueue; Timer_animation::Direction m_currentDirection = Timer_animation::Forward; int m_currentIndex = -1; int m_activeCount = 0; bool m_isRunning = false; int m_lastProcessedX = -1; }; #endif // ANIMATION_GROUP_H ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================
 

Anime_Template

这是一个集合了各种 QWidget 控件的模板,展示了从分形到交互式波等多种炫酷效果,和基础的控件。

这是我日后制作属于我自己动漫相关软件的基石。

--- | 主题 | 图片 | 描述 | 文件名 | 跳转链接 | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------- | :--------------- | :--------------------- | | 《QWidget交互波》 | | 展示波的交互。 | `Wave` | [bilibili演示](https://www.bilibili.com/video/BV1kETHzNERE) | | 《QWidget--分形》迭代型分形们 | | 演示迭代型分形的生成与展示。 | `FractalWidget` | [bilibili演示](https://www.bilibili.com/video/BV12aKtzdErC) | | 《QWidget交融动画按钮》 | | 液态交融动画。 | `Liquid` | [bilibili演示](https://www.bilibili.com/video/BV1w25PzBEq3) | | 《QWidget 眼球克苏鲁》 | | 眼球克苏鲁,掉落的血滴 ,狰狞的触手,聚焦的瞳孔,跟随鼠标的眼球。 | `Eye_of_Cthulhu` | [bilibili演示](https://www.bilibili.com/video/BV11MgSz4EL7) | | 《QWidget星空连接》 | | 星空链接 | `Star_Sky_Connection` | [bilibili演示](https://www.bilibili.com/video/BV1iw3uzAEee) | | 《QWidget轮播图》轮播图 | | 主显示展开其余收缩的轮播图。 | `Carousel_card` | [bilibili演示](https://www.bilibili.com/video/BV1ksZdYjE3w) | | 《QWidget轮播图》椭圆无限滚动滑动轮播图 | | 高级轮播图,实现椭圆轨迹的无限滚动。 | `Adaptive_Carousel` | [bilibili演示](https://www.bilibili.com/video/BV1AXonY7EPz) | | 《QWidget按钮 》 波纹和波浪 | | 按钮点击时带有波纹或波浪动画效果。 | `button_class` | [bilibili演示](https://www.bilibili.com/video/BV12fdFYZEsk) | | 《QWidget登录页面 》 登录注册效果 | | 仿html/css登录注册页面。 | `Login_interface` | [bilibili演示](https://www.bilibili.com/video/BV18FdjYVEBN) | | 《QWidget旋钮》 渐变旋钮控件 | | 一个带有渐变色彩的自定义旋钮控件。 | `dial_class` | [bilibili演示](https://www.bilibili.com/video/BV1dTdSY2Ef1) | | 《QWidget蟑螂须》拉链滑块控件 | | 广东双马尾控件。 | `Zipper_Slider` | [bilibili演示](https://www.bilibili.com/video/BV1DWLbzWEjd) | | 《QWidget拟态化按钮》 | | 具有拟态设计风格的按钮。 | `Mimic_Button` | [bilibili演示](https://www.bilibili.com/video/BV1yXGrz3E6g) | | 《QWidget流动渐变字》 | | 文字带有流动渐变效果的展示。 | `Flowing_Gradient_Font` | [bilibili演示](https://www.bilibili.com/video/BV1fLEgzoEdW) | | 《QWidget像素过渡》 | | 像素化的过渡动画效果。 | `PixelTransition` | [bilibili演示](https://www.bilibili.com/video/BV1Km7Kz4EyF) | | 《QWidget线条动画》 | | 生成式线条动画,创造动态视觉效果。 | `Generative_Lines` | [bilibili演示](https://www.bilibili.com/video/BV1W27mzMEmK) | | 《QWidget简易点浪动画》 | | 简单的点状波浪动画。 | `Point_Wave` | [bilibili演示](https://www.bilibili.com/video/BV1vhTJzEEPT) | | 《QWidget故障文本》 | | 模拟故障或数字失真效果的文本展示。 | `Glitch_Text` | [bilibili演示](https://www.bilibili.com/video/BV1nyT3zqEbW) | | 《QWidget物理文本》物理模拟 | | 文本具有物理模拟效果,如碰撞、重力等。 | `Physical_Text` | [bilibili演示](https://www.bilibili.com/video/BV1zRTBzyEZB) | | 《QWidget文本动画》 翻转弹性 | | 文本字符可以进行翻转或弹性动画。 | `SplitText` | [bilibili演示](https://www.bilibili.com/video/BV1kvMmzVEbb) | | 《QWidget压力方块》 压力方块 | | 模拟压力的方块效果。 | `Pressure_Block` | [bilibili演示](https://www.bilibili.com/video/BV1VSN4zaEWK) | | 《QWidget模糊文本》模糊文本 | | 文字具有模糊效果,可用于过渡或强调。 | `Blur_Text` | [bilibili演示](https://www.bilibili.com/video/BV1prKNzHExz) | | 《QWidget二叉花树》 二叉花树 | | 二叉树的形成和节点上花的形成,花的凋落 | `TreeScene` | [bilibili演示](https://www.bilibili.com/video/BV1Q7KZzmEAw) | | 《QWidget手风琴》 | | 手风琴 | `Accordion` | [bilibili演示](https://www.bilibili.com/video/BV1zFK2zhEfY) | | 《QWidget蜂巢网格》 | | 蜂巢网格 | `Honeycomb_Grid` | [bilibili演示](https://www.bilibili.com/video/BV1iw3uzAEee) | | 《QWidget字母故障》 | | 字母故障 | `Letter_Glitch` | [bilibili演示](https://www.bilibili.com/video/BV1kcGHzLEDq) | | 《QWidget磁力线》 | | 磁力线 | `Magnet_Lines` | [bilibili演示](https://www.bilibili.com/video/BV1JTGHz3EAQ) | | 《QWidget 粒子生成》 | | 通过opencv对图片进行处理,可交互的粒子 | `Particle_Generation` | [bilibili演示](https://www.bilibili.com/video/BV1nKuqzjEnD/) | | 《QWidget Loading集》 | | 各式各样的Loading们 | `LoadingAnime` | [bilibili演示](https://www.bilibili.com/video/BV1LpgVz3ES1/) |