I am animating a QGraphicsTextItem
that I have added a frame around. During the animation the text seems to shake slightly inside the frame, which is very annoying.
An example code:
class MovingFrameText : public QGraphicsTextItem
{
Q_OBJECT;
public:
MovingFrameText( ) : QGraphicsTextItem(0)
{
setPlainText ( "human ");
QFont f = font();
f.setPixelSize(40);
setFont(f);
setFlags(QGraphicsItem::ItemIsMovable);
}
QRectF boundingRect() const
{
return QGraphicsTextItem::boundingRect().adjusted(-2,-2,+2,+2);
}
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
QGraphicsTextItem::paint(painter,option,widget);
painter->setPen(Qt::black);
painter->drawRect(boundingRect());
}
};
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
MovingFrameText t;
t.setPos(640,680);
QGraphicsScene scene;
scene.addItem(&t);
QGraphicsView view(&scene);
view.resize(640, 680);
view.show();
auto moveAnimation = new QPropertyAnimation( &t, "pos" );
moveAnimation->setDuration( 10000 );
moveAnimation->setStartValue( QPointF(640, 680) );
moveAnimation->setEndValue( QPointF(0, 0) );
moveAnimation->setEasingCurve( QEasingCurve::Linear );
moveAnimation->start(QAbstractAnimation::DeleteWhenStopped);
return app.exec();
}
Is there any way to smooth the animation?
You can substantially improve the animation by:
QVariantAnimation
instead of QPropertyAnimation
and calling QGraphicsItem::update
on each iterationQPainter
with a QPixmap
as a canvas for all painting operations and then painting the canvas using the painter passed to the paint
methodNote: The QGraphicsTextItem
will still shake a bit, but at least it will behave as one object instead of several independent ones.
Here is an example I have prepared for you of how your code could be changed in order to implement the proposed solution:
class MovingFrameText : public QGraphicsTextItem
{
public:
MovingFrameText(const QString &text, QGraphicsItem *parent = nullptr)
: QGraphicsTextItem(parent)
{
QFont f(font());
f.setPixelSize(40);
setFont(f);
setPlainText(text);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setClipping(true);
painter->setClipRect(option->rect);
painter->setRenderHint(QPainter::SmoothPixmapTransform);
QPixmap canvas(option->rect.size());
QPainter canvasPainter;
canvas.fill(Qt::transparent);
canvasPainter.begin(&canvas);
canvasPainter.setFont(font());
canvasPainter.drawRect(option->rect.adjusted(0, 0, -1, -1));
canvasPainter.drawText(option->rect, toPlainText());
painter->drawPixmap(0, 0, canvas);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
QGraphicsView view;
auto *t = new MovingFrameText("human");
view.setScene(new QGraphicsScene(&view));
view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
view.setSceneRect(0, 0, 640, 680);
view.scene()->addItem(t);
view.show();
auto *moveAnimation = new QVariantAnimation();
moveAnimation->setDuration(10000);
moveAnimation->setStartValue(QPointF(640, 680));
moveAnimation->setEndValue(QPointF(0, 0));
moveAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QObject::connect(moveAnimation, &QVariantAnimation::valueChanged, [t](const QVariant &value){
t->setPos(value.toPointF());
t->update();
});
return app.exec();
}