添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

通过定时器溢出,令滚轮事件微微滞后达到平滑效果。

定时器溢出是需要时间的,无法立马处理完所有的滚轮事件,所以干脆自己复制一个滚轮事件 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的事...