通过定时器溢出,令滚轮事件微微滞后达到平滑效果。
定时器溢出是需要时间的,无法立马处理完所有的滚轮事件,所以干脆自己复制一个滚轮事件
lastWheelEvent
,然后计算每一次滚动需要移动的距离和步数,将这两个参数绑定在一起放入队列中。定时器每次溢出时就将所有未处理完的事件对应的距离累加得到
totalDelta
,每个未处理事件的步数-1,将 totalDelta 和 lastWheelEvent 作为参数传入
QWeelEvent
的构造函数,构建出真正需要的滚轮事件
e
并将其发送到app的事件处理队列中,发生滚动。
//**********************************************************h****************************************************************
class SmoothScroll : public QTreeWidget
{
Q_OBJECT;
public:
SmoothScroll(QWidget* parent = NULL);
enum SmoothMode
{
NO_SMOOTH,
CONSTANT,
LINEAR,
QUADRATIC,
COSINE
};
SmoothMode smoothMode();
void setSmoothMode(SmoothMode mode);
int fps();
void setFps(int fps);
// value in millisecond
int duration();
void setDuration(int mesc);
double acceration();
void setAcceration(double acceleration);
double smallStepRatio();
void setSmallStepRatio(double smallStepRatio);
double bigStepRatio();
void setBigStepRatio(double bigStepRatio);
Qt::Modifier smallStepModifier();
void setSmallStepModifier(Qt::Modifier smallStepModifier);
Qt::Modifier bigStepModifier();
void setbigStepModifier(Qt::Modifier bigStepModifier);
public slots:
void slotSmoothMove();
protected:
void wheelEvent(QWheelEvent *e);
private:
double subDelta(double delta, int stepsLeft);
QTimer *smoothMoveTimer;
QWheelEvent *lastWheelEvent;
int m_fps;
int m_duration;
SmoothMode m_smoothMode;
double m_acceleration;
double m_smallStepRatio;
double m_bigStepRatio;
Qt::Modifier m_smallStepModifier;
Qt::Modifier m_bigStepModifier;
int stepsTotal;
QList< QPair<double, int> > stepsLeftQueue;
};
//*****************************************************************cpp*********************************************************
SmoothScroll::SmoothScroll(QWidget* parent /*= NULL*/):QTreeWidget(parent)
{
lastWheelEvent = 0;
smoothMoveTimer = new QTimer(this);
connect(smoothMoveTimer, SIGNAL(timeout()), this, SLOT(slotSmoothMove()));
m_fps = 60;
m_duration = 400;
m_smoothMode = COSINE;
m_acceleration = 2.5;
m_smallStepModifier = Qt::SHIFT;
m_smallStepRatio = 1.0 / 5.0;
m_bigStepModifier = Qt::ALT;
m_bigStepRatio = 16.0;
verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu);
horizontalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu);
}
void SmoothScroll::wheelEvent(QWheelEvent *e)
{
//int value = verticalScrollBar()->sliderPosition();
//emit wheelScrollChange(value);
//QTreeWidget::wheelEvent(e);
if (m_smoothMode == NO_SMOOTH)
{
QTreeWidget::wheelEvent(e);
return;
}
static QQueue<qint64> scrollStamps;
qint64 now = QDateTime::currentDateTime().toMSecsSinceEpoch();
scrollStamps.enqueue(now);
while (now - scrollStamps.front() > 500)
scrollStamps.dequeue();
double accerationRatio = qMin(scrollStamps.size() / 15.0, 1.0);
if (!lastWheelEvent)
lastWheelEvent = new QWheelEvent(*e);
else
*lastWheelEvent = *e;
stepsTotal = m_fps * m_duration / 1000;
double multiplier = 1.0;
if (QApplication::keyboardModifiers() & smallStepModifier())
multiplier *= smallStepRatio();
if (QApplication::keyboardModifiers() & bigStepModifier())
multiplier *= bigStepRatio();
double delta = e->delta() * multiplier;
if (acceration() > 0)
delta += delta * acceration() * accerationRatio;
stepsLeftQueue.push_back(qMakePair(delta, stepsTotal));
smoothMoveTimer->start(1000 / m_fps);
}
int SmoothScroll::fps()
{
return m_fps;
}
void SmoothScroll::setFps(int fps)
{
m_fps = fps;
}
int SmoothScroll::duration()
{
return m_duration;
}
void SmoothScroll::setDuration(int mesc)
{
m_duration = mesc;
}
SmoothScroll::SmoothMode SmoothScroll::smoothMode()
{
return m_smoothMode;
}
void SmoothScroll::setSmoothMode(CMyTreeWidget::SmoothMode mode)
{
m_smoothMode = mode;
}
double SmoothScroll::acceration()
{
return m_acceleration;
}
void SmoothScroll::setAcceration(double acceleration)
{
m_acceleration = acceleration;
}
double SmoothScroll::smallStepRatio()
{
return m_smallStepRatio;
}
void SmoothScroll::setSmallStepRatio(double smallStepRatio)
{
m_smallStepRatio = smallStepRatio;
}
double SmoothScroll::bigStepRatio()
{
return m_bigStepRatio;
}
void SmoothScroll::setBigStepRatio(double bigStepRatio)
{
m_bigStepRatio = bigStepRatio;
}
Qt::Modifier SmoothScroll::smallStepModifier()
{
return m_smallStepModifier;
}
void SmoothScroll::setSmallStepModifier(
Qt::Modifier smallStepModifier)
{
m_smallStepModifier = smallStepModifier;
}
Qt::Modifier SmoothScroll::bigStepModifier()
{
return m_bigStepModifier;
}
void SmoothScroll::setbigStepModifier(
Qt::Modifier bigStepModifier)
{
m_bigStepModifier = bigStepModifier;
}
void SmoothScroll::slotSmoothMove()
{
double totalDelta = 0;
for (QList< QPair<double, int> >::Iterator it = stepsLeftQueue.begin();
it != stepsLeftQueue.end(); ++it)
{
totalDelta += subDelta(it->first, it->second);
--(it->second);
}
while (!stepsLeftQueue.empty() && stepsLeftQueue.begin()->second == 0)
stepsLeftQueue.pop_front();
Qt::Orientation orientation = lastWheelEvent->orientation();
// By default, when you press ALT, QT will scroll horizontally. But if we
// have defined the use of ALT key, we ignore this setting since horizontal
// scroll is not so useful in okular
if ((bigStepModifier() & Qt::ALT) || (smallStepModifier() & Qt::ALT))
orientation = Qt::Vertical;
QWheelEvent e(
lastWheelEvent->pos(),
lastWheelEvent->globalPos(),
qRound(totalDelta),
lastWheelEvent->buttons(),
0,
orientation
);
if (e.orientation() == Qt::Horizontal)
QApplication::sendEvent(horizontalScrollBar(), &e);
else
QApplication::sendEvent(verticalScrollBar(), &e);
if (stepsLeftQueue.empty()) {
smoothMoveTimer->stop();
}
}
double SmoothScroll::subDelta(double delta, int stepsLeft)
{
Q_ASSERT(m_smoothMode != NO_SMOOTH);
double m = stepsTotal / 2.0;
double x = abs(stepsTotal - stepsLeft - m);
// some mathmatical integral result.
switch (m_smoothMode) {
case NO_SMOOTH:
return 0;
break;
case CONSTANT:
return double(delta) / stepsTotal;
break;
case LINEAR:
return 2.0*delta / stepsTotal * (m - x) / m;
break;
case QUADRATIC:
return 3.0 / 4.0 / m * (1.0 - x*x / m / m) * delta;
break;
case COSINE:
return (cos(x * M_PI / m) + 1.0) / (2.0*m) * delta;
break;
}
return 0;
}
实现思路通过定时器溢出,令滚轮事件微微滞后达到平滑效果。定时器溢出是需要时间的,无法立马处理完所有的滚轮事件,所以干脆自己复制一个滚轮事件lastWheelEvent,然后计算每一次滚动需要移动的距离和步数,将这两个参数绑定在一起放入队列中。定时器每次溢出时就将所有未处理完的事件对应的距离累加得到totalDelta,每个未处理事件的步数-1,将 totalDelta 和 lastWheelEvent 作为参数传入QWeelEvent的构造函数,构建出真正需要的滚轮事件e并将其发送到app的事...