PySide6实现椭圆扩散hover效果自定义按钮效果不符求助
核心问题
你的实现和目标效果差别的根源在几个逻辑写偏了:
- 扩散椭圆的圆心写死成了按钮中心,目标效果是从鼠标进入按钮的落点开始往外扩散
- 给按钮边框加了多余的padding缩放动效,目标效果里按钮本身大小、圆角全程不动,只有内部椭圆扩散
- 文字颜色靠硬编码动画值阈值切换,和扩散进度不同步,过渡生硬
- 椭圆最大半径写死成固定值75,按钮尺寸变了就会出现动画结束还盖不满的情况
修正方案
调整绘制逻辑和动画参数:
- 鼠标进入按钮时记录当前鼠标相对于控件的坐标,作为椭圆扩散的起点
- 删掉按钮轮廓的缩放逻辑,始终按控件原始尺寸绘制固定圆角的按钮基底,裁剪区域也固定为按钮形状
- 每次绘制时动态计算椭圆需要的最大半径:取扩散起点到按钮四个角的距离最大值,保证动画结束时椭圆能完全盖住整个按钮,适配任意按钮尺寸
- 调整绘制分层,不用硬编码颜色切换阈值,自然实现文字随扩散同步变色:
- 最底层先画默认状态:浅灰圆角背景 + 黑色居中文字
- 中间层在按钮形状裁剪范围内,画随动画进度放大的白色椭圆(hover状态的背景色)
- 最上层同样在裁剪范围内,画浅色的居中文字,椭圆扩散到哪,浅色文字就覆盖到哪,和扩散进度完全同步
- 微调动画时长和缓动曲线,更贴近原效果的手感
修正后的完整可运行代码:
import sys from math import hypot from PySide6 import QtCore, QtWidgets, QtGui from PySide6.QtWidgets import * from PySide6.QtCore import * from PySide6.QtGui import * class Button19(QPushButton): Radius = 10 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 颜色配置:(默认色, hover色) self.bgColors = (QColor(Qt.lightGray), QColor(Qt.white)) self.textColors = (QColor(Qt.black), QColor(Qt.lightGray)) font = self.font() font.setBold(True) self.setFont(font) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setText("Button") # 动画配置 self.hoverAnim = QVariantAnimation(self) self.hoverAnim.setStartValue(0.) self.hoverAnim.setEndValue(1.) self.hoverAnim.setEasingCurve(QEasingCurve.OutQuad) self.hoverAnim.setDuration(450) self.hoverAnim.valueChanged.connect(self.update) self._isHover = False self._hoverStartPos = QPointF() # 记录鼠标进入的起点坐标 def enterEvent(self, event): super().enterEvent(event) self._isHover = True self._hoverStartPos = event.position() # 记录鼠标进入时的相对坐标 self.hoverAnim.setDirection(QVariantAnimation.Forward) self.hoverAnim.start() def leaveEvent(self, event): super().leaveEvent(event) self._isHover = False self.hoverAnim.setDirection(QVariantAnimation.Backward) self.hoverAnim.start() def paintEvent(self, event): aniProgress = self.hoverAnim.currentValue() painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(Qt.NoPen) # 按钮固定轮廓,无缩放 btnRect = QRectF(self.rect()).adjusted(5, 5, -5, -5) btnPath = QPainterPath() btnPath.addRoundedRect(btnRect, self.Radius, self.Radius) # 第一层:绘制默认状态 浅灰底+黑字 painter.setBrush(self.bgColors[0]) painter.drawPath(btnPath) painter.setPen(self.textColors[0]) painter.drawText(btnRect, Qt.AlignCenter, self.text()) # 计算扩散椭圆需要的最大半径:到四个角的最远距离 maxRadius = max( hypot(self._hoverStartPos.x(), self._hoverStartPos.y()), hypot(self.width() - self._hoverStartPos.x(), self._hoverStartPos.y()), hypot(self._hoverStartPos.x(), self.height() - self._hoverStartPos.y()), hypot(self.width() - self._hoverStartPos.x(), self.height() - self._hoverStartPos.y()) ) currentRadius = maxRadius * aniProgress # 第二层:在按钮裁剪范围内绘制扩散白圆 painter.save() painter.setClipPath(btnPath) painter.setBrush(self.bgColors[1]) painter.drawEllipse(self._hoverStartPos, currentRadius, currentRadius) # 第三层:绘制hover状态的浅色文字,自动和圆扩散同步 painter.setPen(self.textColors[1]) painter.drawText(btnRect, Qt.AlignCenter, self.text()) painter.restore() if __name__ == "__main__": app = QApplication(sys.argv) wind = QMainWindow() wind.setStyleSheet("QMainWindow{background-color:rgb(247,247,250)}") wind.resize(180, 80) wid = QWidget() lay = QHBoxLayout(wid) lay.setAlignment(Qt.AlignCenter) lay.addWidget(Button19()) wind.setCentralWidget(wid) wind.show() sys.exit(app.exec())
补充说明:如果需要调整效果,直接改类里的颜色值、Radius(圆角大小)、动画时长/缓动曲线即可,扩散逻辑会自动适配任意按钮尺寸,不需要手动改半径参数。
内容的提问来源于stack exchange,提问作者Dream59
相关产品推荐
相关产品推荐

