From: Aaron Kennedy Date: Mon, 21 Nov 2011 17:51:31 +0000 (+0000) Subject: Adding custom easing curves to property animations X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=fbbc3cab4ac57b99387c7955e712cfea7cd72856;p=konrad%2Fqtdeclarative.git Adding custom easing curves to property animations QDeclarativeEasingValueType gets the property customBezierCurve. This allows to define a custom easing curve as a cubic bezier curve. Change-Id: I33ae128ce29bba2834eedcbb90a9769a5391f997 Reviewed-by: Aaron Kennedy --- diff --git a/examples/declarative/animation/easing/easing.qml b/examples/declarative/animation/easing/easing.qml index 911e6ce..233bb2d 100644 --- a/examples/declarative/animation/easing/easing.qml +++ b/examples/declarative/animation/easing/easing.qml @@ -45,6 +45,9 @@ Rectangle { id: window width: 600; height: 460; color: "#232323" + property var easingCurve: [ 0.2, 0.2, 0.13, 0.65, 0.2, 0.8, + 0.624, 0.98, 0.93, 0.95, 1, 1 ] + ListModel { id: easingTypes ListElement { name: "Easing.Linear"; type: Easing.Linear; ballColor: "DarkRed" } @@ -88,6 +91,7 @@ Rectangle { ListElement { name: "Easing.InBounce"; type: Easing.InBounce; ballColor: "DimGray" } ListElement { name: "Easing.InOutBounce"; type: Easing.InOutBounce; ballColor: "SlateGray" } ListElement { name: "Easing.OutInBounce"; type: Easing.OutInBounce; ballColor: "DarkSlateGray" } + ListElement { name: "Easing.Bezier"; type: Easing.Bezier; ballColor: "Chartreuse"; } } Component { @@ -128,8 +132,8 @@ Rectangle { } transitions: Transition { - NumberAnimation { properties: "x"; easing.type: type; duration: 1000 } - ColorAnimation { properties: "color"; easing.type: type; duration: 1000 } + NumberAnimation { properties: "x"; easing.type: type; easing.bezierCurve: getBezierCurve(name); duration: 1000 } + ColorAnimation { properties: "color"; easing.type: type; easing.bezierCurve: getBezierCurve(name); duration: 1000 } } } } @@ -156,4 +160,11 @@ Rectangle { Repeater { model: easingTypes; delegate: delegate } } } + + function getBezierCurve(name) + { + if (name === "Easing.Bezier") + return easingCurve; + return []; + } } diff --git a/src/declarative/items/qquickcanvas.h b/src/declarative/items/qquickcanvas.h index a5a9757..bf8693a 100644 --- a/src/declarative/items/qquickcanvas.h +++ b/src/declarative/items/qquickcanvas.h @@ -52,9 +52,10 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class QQuickItem; class QSGEngine; +class QQuickItem; class QSGTexture; +class QInputMethodEvent; class QQuickCanvasPrivate; class QOpenGLFramebufferObject; class QDeclarativeIncubationController; diff --git a/src/declarative/qml/qdeclarativevaluetype.cpp b/src/declarative/qml/qdeclarativevaluetype.cpp index 9a941e6..ffd0041 100644 --- a/src/declarative/qml/qdeclarativevaluetype.cpp +++ b/src/declarative/qml/qdeclarativevaluetype.cpp @@ -958,6 +958,52 @@ void QDeclarativeEasingValueType::setPeriod(qreal period) easing.setPeriod(period); } +void QDeclarativeEasingValueType::setBezierCurve(const QVariantList &customCurveVariant) +{ + if (customCurveVariant.isEmpty()) + return; + + QVariantList variantList = customCurveVariant; + if ((variantList.count() % 6) == 0) { + bool allRealsOk = true; + QList reals; + for (int i = 0; i < variantList.count(); i++) { + bool ok; + const qreal real = variantList.at(i).toReal(&ok); + reals.append(real); + if (!ok) + allRealsOk = false; + } + if (allRealsOk) { + QEasingCurve newEasingCurve(QEasingCurve::BezierSpline); + for (int i = 0; i < reals.count() / 6; i++) { + const qreal c1x = reals.at(i * 6); + const qreal c1y = reals.at(i * 6 + 1); + const qreal c2x = reals.at(i * 6 + 2); + const qreal c2y = reals.at(i * 6 + 3); + const qreal c3x = reals.at(i * 6 + 4); + const qreal c3y = reals.at(i * 6 + 5); + + const QPointF c1(c1x, c1y); + const QPointF c2(c2x, c2y); + const QPointF c3(c3x, c3y); + + newEasingCurve.addCubicBezierSegment(c1, c2, c3); + easing = newEasingCurve; + } + } + } +} + +QVariantList QDeclarativeEasingValueType::bezierCurve() const +{ + QVariantList rv; + QList points = easing.cubicBezierSpline(); + for (int ii = 0; ii < points.count(); ++ii) + rv << QVariant(points.at(ii).x()) << QVariant(points.at(ii).y()); + return rv; +} + QDeclarativeFontValueType::QDeclarativeFontValueType(QObject *parent) : QDeclarativeValueType(parent), pixelSizeSet(false), pointSizeSet(false) { diff --git a/src/declarative/qml/qdeclarativevaluetype_p.h b/src/declarative/qml/qdeclarativevaluetype_p.h index 1feab41..9f00d97 100644 --- a/src/declarative/qml/qdeclarativevaluetype_p.h +++ b/src/declarative/qml/qdeclarativevaluetype_p.h @@ -456,6 +456,7 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeEasingValueType : public QDeclara Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude) Q_PROPERTY(qreal overshoot READ overshoot WRITE setOvershoot) Q_PROPERTY(qreal period READ period WRITE setPeriod) + Q_PROPERTY(QVariantList bezierCurve READ bezierCurve WRITE setBezierCurve) public: enum Type { Linear = QEasingCurve::Linear, @@ -480,7 +481,8 @@ public: InBounce = QEasingCurve::InBounce, OutBounce = QEasingCurve::OutBounce, InOutBounce = QEasingCurve::InOutBounce, OutInBounce = QEasingCurve::OutInBounce, InCurve = QEasingCurve::InCurve, OutCurve = QEasingCurve::OutCurve, - SineCurve = QEasingCurve::SineCurve, CosineCurve = QEasingCurve::CosineCurve + SineCurve = QEasingCurve::SineCurve, CosineCurve = QEasingCurve::CosineCurve, + Bezier = QEasingCurve::BezierSpline }; QDeclarativeEasingValueType(QObject *parent = 0); @@ -500,6 +502,9 @@ public: void setAmplitude(qreal); void setOvershoot(qreal); void setPeriod(qreal); + void setBezierCurve(const QVariantList &); + QVariantList bezierCurve() const; + private: QEasingCurve easing; diff --git a/tools/qmleasing/TextField.qml b/tools/qmleasing/TextField.qml new file mode 100644 index 0000000..d99d469 --- /dev/null +++ b/tools/qmleasing/TextField.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + width: input.x + input.width + height: border.height + + property alias name: name.text + property alias text: input.text + + Text { + id: name + height: parent.height + } + + TextInput { + id: input + anchors.left: name.right + anchors.leftMargin: 4 + focus: true + width: 50 + horizontalAlignment: "AlignRight" + Rectangle { + id: border + x: -2; y: -2 + width: parent.width + 4 + height: parent.height + 4 + color: "transparent" + border.color: input.activeFocus?"green":"lightgreen" + + border.width: 3 + radius: 5 + } + } +} + diff --git a/tools/qmleasing/easing.qml b/tools/qmleasing/easing.qml new file mode 100644 index 0000000..fe5f831 --- /dev/null +++ b/tools/qmleasing/easing.qml @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import EasingPlot 1.0 + +Rectangle { + width: 775; height: 550 + + function precision(n) + { + var str = n.toPrecision(3); + while (str.length > 1 && (str[str.length - 1] == "0" || str[str.length - 1] == ".")) + str = str.substr(0, str.length - 1); + return str; + } + + function updateEasing() { + var ini = Math.min(100, Math.max(0, Number(in_inf.text))); + var outi = Math.min(100, Math.max(0, Number(out_inf.text))); + + var ins = Number(in_slope.text); + var outs = Number(out_slope.text); + + var p1 = [ (ini / 100), (ini / 100) * ins ]; + var p2 = [ 1 - (outi / 100), 1 - (outi / 100) * outs ]; + + text.text = "[ " + precision(p1[0]) + ", " + precision(p1[1]) + ", " + precision(p2[0]) + ", " + precision(p2[1]) + ", 1, 1 ]"; + } + + Rectangle { + id: border + width: 500; height: 500 + x: 25; y: 25 + border.color: "lightsteelblue" + border.width: 3 + radius: 5 + color: "transparent" + + EasingPlot { + id: plot + + anchors.centerIn: parent + width: parent.width - 10 + height: parent.height - 10 + + easing.type: "Bezier" + easing.bezierCurve: eval(text.text) + } + + } + + Text { + text: "After Effects curve" + anchors.horizontalCenter: text.horizontalCenter + anchors.bottom: column.top + anchors.bottomMargin: 14 + } + + Column { + id: column + + y: 70 + anchors.right: parent.right + anchors.rightMargin: 25 + spacing: 5 + TextField { + id: in_inf + focus: true + name: "Input influence:" + text: "33" + anchors.right: parent.right + KeyNavigation.tab: in_slope + KeyNavigation.backtab: text + onTextChanged: updateEasing(); + } + TextField { + id: in_slope + name: "Input slope:" + text: "0" + anchors.right: parent.right + KeyNavigation.tab: out_inf + KeyNavigation.backtab: in_inf + onTextChanged: updateEasing(); + } + TextField { + id: out_inf + name: "Output influence:" + text: "33" + anchors.right: parent.right + KeyNavigation.tab: out_slope + KeyNavigation.backtab: in_slope + onTextChanged: updateEasing(); + } + TextField { + id: out_slope + name: "Output slope:" + text: "0" + anchors.right: parent.right + KeyNavigation.tab: text + KeyNavigation.backtab: out_info + onTextChanged: updateEasing(); + } + } + + Text { + text: "QML Bezier curve" + anchors.horizontalCenter: text.horizontalCenter + anchors.bottom: text.top + anchors.bottomMargin: 10 + } + + TextEdit { + id: text + x: 200 + width: 200 + height: 200 + + Rectangle { + x: -2; y: -2 + width: parent.width + 4 + height: parent.height + 4 + color: "transparent" + border.color: text.activeFocus?"green":"lightgreen" + + border.width: 3 + radius: 5 + } + + wrapMode: "WordWrap" + + anchors.top: column.bottom + anchors.topMargin: 50 + anchors.right: column.right + KeyNavigation.tab: in_inf + KeyNavigation.backtab: out_slope + } + + + Item { + anchors.left: text.left + anchors.top: text.bottom + anchors.topMargin: 35 + width: text.width + height: rect.height + + Rectangle { + color: "gray" + width: 50; height: 50 + id: rect + + NumberAnimation on x { + id: animation + running: false + easing: plot.easing + duration: 1000 + } + + radius: 5 + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (rect.x < 5) { + animation.to = text.width - rect.width; + } else { + animation.to = 0; + } + animation.start(); + } + } + + Text { + anchors.centerIn: parent + text: "Click to Try" + } + } + + Component.onCompleted: updateEasing(); +} diff --git a/tools/qmleasing/main.cpp b/tools/qmleasing/main.cpp new file mode 100644 index 0000000..a9f1da4 --- /dev/null +++ b/tools/qmleasing/main.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +class EasingPlot : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged); + +public: + EasingPlot(); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + +signals: + void easingChanged(); + +protected: + virtual void paint(QPainter *painter); + +private: + QEasingCurve m_easing; +}; + +EasingPlot::EasingPlot() +{ +} + +QEasingCurve EasingPlot::easing() const +{ + return m_easing; +} + +void EasingPlot::setEasing(const QEasingCurve &e) +{ + if (m_easing == e) + return; + + m_easing = e; + emit easingChanged(); + + update(); +} + +void EasingPlot::paint(QPainter *painter) +{ + QPointF lastPoint(0, 0); + + for (int ii = 1; ii <= 100; ++ii) { + qreal value = m_easing.valueForProgress(qreal(ii) / 100.); + + QPointF currentPoint(width() * qreal(ii) / 100., value * (height() - 1)); + painter->drawLine(lastPoint, currentPoint); + + lastPoint = currentPoint; + } +} + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + + qmlRegisterType("EasingPlot", 1, 0, "EasingPlot"); + + QQuickView view; + view.setSource(QUrl("qrc:/easing.qml")); + view.show(); + + return app.exec(); +} + +#include "main.moc" diff --git a/tools/qmleasing/qmleasing.pro b/tools/qmleasing/qmleasing.pro new file mode 100644 index 0000000..4a64fe9 --- /dev/null +++ b/tools/qmleasing/qmleasing.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +QT += declarative widgets +CONFIG -= app_bundle + +# Input +SOURCES += main.cpp + +RESOURCES = $$PWD/resources.qrc diff --git a/tools/qmleasing/resources.qrc b/tools/qmleasing/resources.qrc new file mode 100644 index 0000000..c7a67b8 --- /dev/null +++ b/tools/qmleasing/resources.qrc @@ -0,0 +1,6 @@ + + + easing.qml + TextField.qml + + diff --git a/tools/tools.pro b/tools/tools.pro index ae2ca0c..46d381d 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -1,5 +1,5 @@ TEMPLATE = subdirs -SUBDIRS += qmlviewer qmlscene qmlplugindump qmlmin +SUBDIRS += qmlviewer qmlscene qmlplugindump qmlmin qmleasing contains(QT_CONFIG, qmltest): SUBDIRS += qmltestrunner