Added support to "cubic-bezier" timing function.

This commit is contained in:
Martín Lucas Golini
2022-09-08 20:14:07 -03:00
parent 0081548434
commit db57f39ae3
17 changed files with 332 additions and 60 deletions

View File

@@ -23,8 +23,6 @@
### CSS
* Add `cubic-bezier` timing function support.
* Add support for [calc](https://developer.mozilla.org/en-US/docs/Web/CSS/calc).
### UICodeEditor

View File

@@ -141,7 +141,7 @@ DropDownList {
border-color: var(--button-border);
border-radius: var(--button-radius);
border-width: var(--border-width);
transition: all 0.125s;
transition: all 0.125s cubic-bezier(0.77, 0.77, 0.38, 0.38);
}
DropDownList,

View File

@@ -1847,12 +1847,12 @@ Read [transition-property](https://developer.mozilla.org/en-US/docs/Web/CSS/tran
Read [transition-timing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function) documentation.
Timing function names are custom, and not the same as the standard, but `cubic-bezier` will be added
soon. Current timing functions supported: linear, quadratic-in, quadratic-out, quadratic-in-out,
Timing function names are custom, and not the same as the standard, but `cubic-bezier` is supported.
Current timing functions supported: linear, quadratic-in, quadratic-out, quadratic-in-out,
sine-in, sine-out, sine-in-out, exponential-in, exponential-out, exponential-in-out, quartic-in,
quartic-out, quartic-in-out, circular-in, circular-out, circular-in-out, cubic-in, cubic-out,
cubic-in-out, back-in, back-out, back-in-out, bounce-in, bounce-out, bounce-in-out, elastic-in,
elastic-out, elastic-in-out, none.
elastic-out, elastic-in-out, cubic-bezier, none.
### value

View File

@@ -1,6 +1,7 @@
#ifndef EE_MATH_EASE
#define EE_MATH_EASE
#include <eepp/system/functionstring.hpp>
#include <string>
namespace EE { namespace Math {
@@ -41,6 +42,7 @@ class Ease {
ElasticIn,
ElasticOut,
ElasticInOut,
CubizBezier,
None
};
@@ -108,6 +110,8 @@ class Ease {
return "elastic-out";
case Ease::ElasticInOut:
return "elastic-in-out";
case Ease::CubizBezier:
return "cubic-bezier";
case Ease::None:
return "none";
}
@@ -178,6 +182,13 @@ class Ease {
return Ease::ElasticOut;
if ( "elasticinout" == name || "elastic-in-out" == name )
return Ease::ElasticInOut;
if ( "cubicbezier" == name || "cubic-bezier" == name )
return Ease::CubizBezier;
// Handle the case that the name is a function
if ( name.find_first_of( '(' ) != std::string::npos ) {
System::FunctionString func( System::FunctionString::parse( name ) );
return fromName( func.getName(), defaultInterpolation );
}
return defaultInterpolation;
}
};

View File

@@ -10,6 +10,25 @@ typedef double ( *easingCbFunc )( double, double, double, double );
extern EE_API easingCbFunc easingCb[];
/**
* Keeps the initial value until the current time equals the duration.
*
* @param t Specifies the current time, between 0 and duration inclusive.
* @param b Specifies the initial value of the animation property.
* @param c Specifies the total change in the animation property.
* @param d Specifies the duration of the motion.
* @return The value of the interpolated property at the specified time.
*/
inline double noneInterpolation( double t, double b, double c, double d ) {
return t == 0 ? b : ( t == d ? c : b );
}
double EE_API cubicBezierInterpolation( double x1, double y1, double x2, double y2, double t );
inline double cubicBezierNoParams( double t, double, double, double ) {
return cubicBezierInterpolation( 0, 0, 1, 1, t );
}
/**
* Calculate the position of a point from a linear interpolation.
*
@@ -24,7 +43,7 @@ inline double linearInterpolation( double t, double b, double c, double d ) {
}
/**
* The <code>QuadraticIn()</code> method starts motion from a zero velocity
* The <code>quadraticIn()</code> method starts motion from a zero velocity
* and then accelerates motion as it executes.
* @param t Specifies the current time, between 0 and duration inclusive.
* @param b Specifies the initial value of the animation property.
@@ -39,7 +58,7 @@ inline double quadraticIn( double t, double b, double c, double d ) {
}
/**
* The <code>QuadraticOut()</code> method starts motion fast
* The <code>quadraticOut()</code> method starts motion fast
* and then decelerates motion to a zero velocity as it executes.
*
* @param t Specifies the current time, between 0 and duration inclusive.
@@ -55,8 +74,8 @@ inline double quadraticOut( double t, double b, double c, double d ) {
}
/**
* The <code>QuadraticInOut()</code> method combines the motion
* of the <code>QuadraticIn()</code> and <code>QuadraticOut()</code> methods
* The <code>quadraticInOut()</code> method combines the motion
* of the <code>quadraticIn()</code> and <code>QuadraticOut()</code> methods
* to start the motion from a zero velocity,
* accelerate motion, then decelerate to a zero velocity.
*
@@ -78,7 +97,7 @@ inline double quadraticInOut( double t, double b, double c, double d ) {
}
/**
* The <code>SineIn()</code> method starts motion from zero velocity
* The <code>sineIn()</code> method starts motion from zero velocity
* and then accelerates motion as it executes.
*
* @param t Specifies the current time, between 0 and duration inclusive.
@@ -122,7 +141,7 @@ inline double sineInOut( double t, double b, double c, double d ) {
}
/**
* The <code>ExponentialIn()</code> method starts motion slowly
* The <code>exponentialIn()</code> method starts motion slowly
* and then accelerates motion as it executes.
*
* @param t Specifies the current time, between 0 and duration inclusive.
@@ -136,7 +155,7 @@ inline double exponentialIn( double t, double b, double c, double d ) {
}
/**
* The <code>ExponentialOut()</code> method starts motion fast
* The <code>exponentialOut()</code> method starts motion fast
* and then decelerates motion to a zero velocity as it executes.
*
* @param t Specifies the current time, between 0 and duration inclusive.
@@ -150,8 +169,8 @@ inline double exponentialOut( double t, double b, double c, double d ) {
}
/**
* The <code>ExponentialInOut()</code> method combines the motion
* of the <code>ExponentialIn()</code> and <code>ExponentialOut()</code> methods
* The <code>exponentialInOut()</code> method combines the motion
* of the <code>exponentialIn()</code> and <code>ExponentialOut()</code> methods
* to start the motion from a zero velocity, accelerate motion,
* then decelerate to a zero velocity.
*

View File

@@ -57,6 +57,8 @@ class EE_API AnimationDefinition {
const Ease::Interpolation& getTimingFunction() const;
const std::vector<double>& getTimingFunctionParameters() const;
const AnimationFillMode& getFillMode() const;
void setName( const std::string& value );
@@ -69,6 +71,8 @@ class EE_API AnimationDefinition {
void setTimingFunction( const Ease::Interpolation& value );
void setTimingFunctionParameters( const std::vector<double>& timingFunctionParameters );
void setDirection( const AnimationDirection& value );
void setFillMode( const AnimationFillMode& value );
@@ -84,6 +88,7 @@ class EE_API AnimationDefinition {
Time mDuration = Time::Zero;
Int32 mIterations = 1; /* -1 == "infinite" */
Ease::Interpolation mTimingFunction = Ease::Interpolation::Linear;
std::vector<double> mTimingFunctionParameters{};
AnimationDirection mDirection = Normal;
AnimationFillMode mFillMode = None;
bool mPaused = false;
@@ -93,7 +98,8 @@ inline bool operator==( const AnimationDefinition& a, const AnimationDefinition&
return a.getDuration() == b.getDuration() && a.getTimingFunction() == b.getTimingFunction() &&
a.getDelay() == b.getDelay() && a.getDirection() == b.getDirection() &&
a.isPaused() == b.isPaused() && a.getIterations() == b.getIterations() &&
a.getName() == b.getName();
a.getName() == b.getName() &&
a.getTimingFunctionParameters() == b.getTimingFunctionParameters();
}
inline bool operator!=( const AnimationDefinition& a, const AnimationDefinition& b ) {

View File

@@ -26,6 +26,7 @@ class EE_API StyleSheetPropertyAnimation : public Action {
const PropertyDefinition* property, const std::string& startValue,
const std::string& endValue,
const Ease::Interpolation& timingFunction,
const std::vector<double> timingFunctionParameters,
const Uint32& propertyIndex, const bool& isDone );
static StyleSheetPropertyAnimation* fromAnimationKeyframes(
@@ -44,6 +45,7 @@ class EE_API StyleSheetPropertyAnimation : public Action {
New( const PropertyDefinition* property, const std::string& startValue,
const std::string& endValue, const Uint32& propertyIndex, const Time& duration,
const Time& delay, const Ease::Interpolation& timingFunction,
const std::vector<double>& timingFunctionParameters,
const AnimationOrigin& animationOrigin );
void start() override;

View File

@@ -0,0 +1,22 @@
#ifndef EE_UI_CSS_TIMINGFUNCTION_HPP
#define EE_UI_CSS_TIMINGFUNCTION_HPP
#include <eepp/core/string.hpp>
#include <eepp/math/ease.hpp>
using namespace EE;
using namespace EE::Math;
namespace EE { namespace UI { namespace CSS {
class EE_API TimingFunction {
public:
static TimingFunction parse( std::string timingFunction );
Ease::Interpolation interpolation{ Ease::None };
std::vector<double> parameters;
};
}}} // namespace EE::UI::CSS
#endif // EE_UI_CSS_TIMINGFUNCTION_HPP

View File

@@ -23,12 +23,15 @@ class EE_API TransitionDefinition {
Ease::Interpolation getTimingFunction() const { return timingFunction; }
std::vector<double> getTimingFunctionParameters() const { return timingFunctionParameters; }
const Time& getDelay() const { return delay; }
const Time& getDuration() const { return duration; }
std::string property;
Ease::Interpolation timingFunction = Ease::Interpolation::Linear;
std::vector<double> timingFunctionParameters{};
Time delay = Time::Zero;
Time duration = Time::Zero;
};

View File

@@ -327,6 +327,7 @@
../../include/eepp/ui/css/stylesheetspecification.hpp
../../include/eepp/ui/css/stylesheetstyle.hpp
../../include/eepp/ui/css/stylesheetvariable.hpp
../../include/eepp/ui/css/timingfunction.hpp
../../include/eepp/ui/css/transitiondefinition.hpp
../../include/eepp/ui/doc/syntaxcolorscheme.hpp
../../include/eepp/ui/doc/syntaxdefinition.hpp
@@ -814,6 +815,7 @@
../../src/eepp/ui/css/stylesheetspecification.cpp
../../src/eepp/ui/css/stylesheetstyle.cpp
../../src/eepp/ui/css/stylesheetvariable.cpp
../../src/eepp/ui/css/timingfunction.cpp
../../src/eepp/ui/css/transitiondefinition.cpp
../../src/eepp/ui/doc/syntaxcolorscheme.cpp
../../src/eepp/ui/doc/syntaxdefinition.cpp

View File

@@ -2,12 +2,141 @@
namespace EE { namespace Math { namespace easing {
easingCbFunc easingCb[] = {
linearInterpolation, quadraticIn, quadraticOut, quadraticInOut, sineIn, sineOut,
sineInOut, exponentialIn, exponentialOut, exponentialInOut, quarticIn, quarticOut,
quarticInOut, quinticIn, quinticOut, quinticInOut, circularIn, circularOut,
circularInOut, cubicIn, cubicOut, cubicInOut, backIn, backOut,
backInOut, bounceIn, bounceOut, bounceInOut, elasticIn, elasticOut,
elasticInOut};
easingCbFunc easingCb[] = { linearInterpolation,
quadraticIn,
quadraticOut,
quadraticInOut,
sineIn,
sineOut,
sineInOut,
exponentialIn,
exponentialOut,
exponentialInOut,
quarticIn,
quarticOut,
quarticInOut,
quinticIn,
quinticOut,
quinticInOut,
circularIn,
circularOut,
circularInOut,
cubicIn,
cubicOut,
cubicInOut,
backIn,
backOut,
backInOut,
bounceIn,
bounceOut,
bounceInOut,
elasticIn,
elasticOut,
elasticInOut,
cubicBezierNoParams,
noneInterpolation };
/**
* https://github.com/gre/bezier-easing
* BezierEasing - use bezier curve for transition easing function
* by Gaëtan Renaudeau 2014 - 2015 MIT License
*/
#define NEWTON_ITERATIONS 4
#define NEWTON_MIN_SLOPE 0.001
#define SUBDIVISION_PRECISION 0.0000001
#define SUBDIVISION_MAX_ITERATIONS 10
#define kSplineTableSize 11
#define kSampleStepSize ( 1.0 / ( kSplineTableSize - 1.0 ) )
double A( double aA1, double aA2 ) {
return 1.0 - 3.0 * aA2 + 3.0 * aA1;
}
double B( double aA1, double aA2 ) {
return 3.0 * aA2 - 6.0 * aA1;
}
double C( double aA1 ) {
return 3.0 * aA1;
}
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
double calcBezier( double aT, double aA1, double aA2 ) {
return ( ( A( aA1, aA2 ) * aT + B( aA1, aA2 ) ) * aT + C( aA1 ) ) * aT;
}
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
double getSlope( double aT, double aA1, double aA2 ) {
return 3.0 * A( aA1, aA2 ) * aT * aT + 2.0 * B( aA1, aA2 ) * aT + C( aA1 );
}
double binarySubdivide( double aX, double aA, double aB, double mX1, double mX2 ) {
double currentX, currentT;
size_t i = 0;
do {
currentT = aA + ( aB - aA ) / 2.0;
currentX = calcBezier( currentT, mX1, mX2 ) - aX;
if ( currentX > 0.0 ) {
aB = currentT;
} else {
aA = currentT;
}
} while ( eeabs( currentX ) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS );
return currentT;
}
double newtonRaphsonIterate( double aX, double aGuessT, double mX1, double mX2 ) {
for ( size_t i = 0; i < NEWTON_ITERATIONS; ++i ) {
double currentSlope = getSlope( aGuessT, mX1, mX2 );
if ( currentSlope == 0.0 ) {
return aGuessT;
}
double currentX = calcBezier( aGuessT, mX1, mX2 ) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
double cubicBezierInterpolation( double x1, double y1, double x2, double y2, double t ) {
if ( !( 0 <= x1 && x1 <= 1 && 0 <= x2 && x2 <= 1 ) )
return t; // 'bezier x values must be in [0, 1] range'
if ( x1 == y1 && x2 == y2 )
return t;
// Precompute samples table
double sampleValues[kSplineTableSize];
for ( size_t i = 0; i < kSplineTableSize; ++i )
sampleValues[i] = calcBezier( i * kSampleStepSize, x1, x2 );
auto getTForX = [&sampleValues, x1, x2]( double aX ) {
double intervalStart = 0.0;
size_t currentSample = 1;
double lastSample = kSplineTableSize - 1;
for ( ; currentSample != lastSample && sampleValues[currentSample] <= aX;
++currentSample ) {
intervalStart += kSampleStepSize;
}
--currentSample;
// Interpolate to provide an initial guess for t
double dist = ( aX - sampleValues[currentSample] ) /
( sampleValues[currentSample + 1] - sampleValues[currentSample] );
double guessForT = intervalStart + dist * kSampleStepSize;
double initialSlope = getSlope( guessForT, x1, x2 );
if ( initialSlope >= NEWTON_MIN_SLOPE ) {
return newtonRaphsonIterate( aX, guessForT, x1, x2 );
} else if ( initialSlope == 0.0 ) {
return guessForT;
} else {
return binarySubdivide( aX, intervalStart, intervalStart + kSampleStepSize, x1, x2 );
}
};
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if ( t == 0 || t == 1 )
return t;
return calcBezier( getTForX( t ), y1, y2 );
}
}}} // namespace EE::Math::easing

View File

@@ -1,9 +1,11 @@
#include <eepp/system/functionstring.hpp>
#include <eepp/ui/css/animationdefinition.hpp>
#include <eepp/ui/css/propertydefinition.hpp>
#include <eepp/ui/css/timingfunction.hpp>
namespace EE { namespace UI { namespace CSS {
bool isTimingFunction( const std::string& str ) {
inline bool isTimingFunction( const std::string& str ) {
return Ease::Interpolation::None != Ease::fromName( str, Ease::Interpolation::None );
}
@@ -15,6 +17,7 @@ std::unordered_map<std::string, AnimationDefinition> AnimationDefinition::parseA
std::vector<Time> delays;
std::vector<Int32> iterations;
std::vector<Ease::Interpolation> timingFunctions;
std::vector<std::vector<double>> timingFunctionParameters;
std::vector<AnimationDirection> directions;
std::vector<AnimationFillMode> fillModes;
std::vector<bool> pausedStates;
@@ -51,7 +54,9 @@ std::unordered_map<std::string, AnimationDefinition> AnimationDefinition::parseA
} else if ( "running" == val ) {
animationDef.setPaused( false );
} else if ( isTimingFunction( val ) ) {
animationDef.setTimingFunction( Ease::fromName( val ) );
TimingFunction tf( TimingFunction::parse( val ) );
animationDef.setTimingFunction( tf.interpolation );
animationDef.setTimingFunctionParameters( tf.parameters );
} else if ( Time::isValid( val ) ) {
if ( durationSet ) {
animationDef.setDelay( Time::fromString( val ) );
@@ -112,11 +117,9 @@ std::unordered_map<std::string, AnimationDefinition> AnimationDefinition::parseA
break;
}
case PropertyId::AnimationTimingFunction: {
Ease::Interpolation interpolation =
Ease::fromName( val, Ease::Interpolation::None );
if ( Ease::Interpolation::None != interpolation ) {
timingFunctions.push_back( interpolation );
}
TimingFunction tf( TimingFunction::parse( val ) );
timingFunctions.emplace_back( tf.interpolation );
timingFunctionParameters.emplace_back( tf.parameters );
break;
}
default:
@@ -152,6 +155,10 @@ std::unordered_map<std::string, AnimationDefinition> AnimationDefinition::parseA
if ( !timingFunctions.empty() )
animationDef.setTimingFunction( timingFunctions[i % timingFunctions.size()] );
if ( !timingFunctionParameters.empty() )
animationDef.setTimingFunctionParameters(
timingFunctionParameters[i % timingFunctionParameters.size()] );
animations[animationDef.getName()] = std::move( animationDef );
}
@@ -248,6 +255,15 @@ const String::HashType& AnimationDefinition::getId() const {
return mId;
}
const std::vector<double>& AnimationDefinition::getTimingFunctionParameters() const {
return mTimingFunctionParameters;
}
void AnimationDefinition::setTimingFunctionParameters(
const std::vector<double>& timingFunctionParameters ) {
mTimingFunctionParameters = timingFunctionParameters;
}
const AnimationDefinition::AnimationFillMode& AnimationDefinition::getFillMode() const {
return mFillMode;
}

View File

@@ -10,18 +10,34 @@ using namespace EE::Math::easing;
namespace EE { namespace UI { namespace CSS {
inline Float easingFn( const Ease::Interpolation& timingFunction,
const std::vector<double>& timingFunctionParameters, const double& t,
const double& b, const double& c, const double& d ) {
if ( timingFunction != Ease::Interpolation::CubizBezier )
return easingCb[timingFunction]( t, b, c, d );
if ( timingFunctionParameters.size() == 4 )
return cubicBezierInterpolation( timingFunctionParameters[0], timingFunctionParameters[1],
timingFunctionParameters[2], timingFunctionParameters[3],
t );
return t;
}
void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float& normalizedProgress,
const PropertyDefinition* property,
const std::string& startValue,
const std::string& endValue,
const Ease::Interpolation& timingFunction,
const std::vector<double> timingFunctionParameters,
const Uint32& propertyIndex, const bool& isDone ) {
switch ( property->getType() ) {
case PropertyType::NumberFloat:
case PropertyType::NumberInt: {
Float start = widget->convertLength( startValue, 0 );
Float end = widget->convertLength( endValue, 0 );
Float value = easingCb[timingFunction]( normalizedProgress, start, end - start, 1.f );
Float value = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start, end - start, 1.f );
if ( property->getType() == PropertyType::NumberFloat ) {
widget->applyProperty(
StyleSheetProperty( property, String::fromFloat( value ), propertyIndex ) );
@@ -42,7 +58,8 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float&
endColor = startColor;
endColor.a = 0;
}
Float progress = easingCb[timingFunction]( normalizedProgress, 0, 1, 1.f );
Float progress =
easingFn( timingFunction, timingFunctionParameters, normalizedProgress, 0, 1, 1.f );
Color resColor( startColor );
resColor.r = static_cast<Uint8>( eemin(
static_cast<Int32>( startColor.r + ( endColor.r - startColor.r ) * progress ),
@@ -65,7 +82,8 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float&
property->getRelativeTarget(), 0.f, propertyIndex );
Float start = widget->convertLength( startValue, containerLength );
Float end = widget->convertLength( endValue, containerLength );
Float value = easingCb[timingFunction]( normalizedProgress, start, end - start, 1.f );
Float value = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start, end - start, 1.f );
widget->applyProperty(
StyleSheetProperty( property, String::fromFloat( value, "px" ), propertyIndex ) );
@@ -77,8 +95,10 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float&
case PropertyType::RadiusLength: {
Sizef start( Borders::radiusFromString( widget, startValue ) );
Sizef end( Borders::radiusFromString( widget, endValue ) );
Float x = easingCb[timingFunction]( normalizedProgress, start.x, end.x - start.x, 1.f );
Float y = easingCb[timingFunction]( normalizedProgress, start.y, end.y - start.y, 1.f );
Float x = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.x, end.x - start.x, 1.f );
Float y = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.y, end.y - start.y, 1.f );
widget->applyProperty( StyleSheetProperty(
property, String::fromFloat( x ) + " " + String::fromFloat( y ), propertyIndex ) );
if ( isDone ) {
@@ -89,8 +109,10 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float&
case PropertyType::Vector2: {
Vector2f start( StyleSheetProperty( property, startValue ).asVector2f() );
Vector2f end( StyleSheetProperty( property, endValue ).asVector2f() );
Float x = easingCb[timingFunction]( normalizedProgress, start.x, end.x - start.x, 1.f );
Float y = easingCb[timingFunction]( normalizedProgress, start.y, end.y - start.y, 1.f );
Float x = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.x, end.x - start.x, 1.f );
Float y = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.y, end.y - start.y, 1.f );
widget->applyProperty( StyleSheetProperty(
property, String::fromFloat( x ) + " " + String::fromFloat( y ), propertyIndex ) );
if ( isDone ) {
@@ -104,8 +126,10 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float&
->calcDrawableSize( startValue ) );
Sizef end(
widget->getBackground()->getLayer( propertyIndex )->calcDrawableSize( endValue ) );
Float x = easingCb[timingFunction]( normalizedProgress, start.x, end.x - start.x, 1.f );
Float y = easingCb[timingFunction]( normalizedProgress, start.y, end.y - start.y, 1.f );
Float x = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.x, end.x - start.x, 1.f );
Float y = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.y, end.y - start.y, 1.f );
widget->applyProperty( StyleSheetProperty(
property, String::fromFloat( x, "px" ) + " " + String::fromFloat( y, "px" ),
propertyIndex ) );
@@ -120,8 +144,10 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float&
->calcDrawableSize( startValue ) );
Sizef end(
widget->getForeground()->getLayer( propertyIndex )->calcDrawableSize( endValue ) );
Float x = easingCb[timingFunction]( normalizedProgress, start.x, end.x - start.x, 1.f );
Float y = easingCb[timingFunction]( normalizedProgress, start.y, end.y - start.y, 1.f );
Float x = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.x, end.x - start.x, 1.f );
Float y = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
start.y, end.y - start.y, 1.f );
widget->applyProperty( StyleSheetProperty(
property, String::fromFloat( x, "px" ) + " " + String::fromFloat( y, "px" ),
propertyIndex ) );
@@ -192,11 +218,13 @@ StyleSheetPropertyAnimation* StyleSheetPropertyAnimation::New(
StyleSheetPropertyAnimation* StyleSheetPropertyAnimation::New(
const PropertyDefinition* property, const std::string& startValue, const std::string& endValue,
const Uint32& propertyIndex, const Time& duration, const Time& delay,
const Ease::Interpolation& timingFunction, const AnimationOrigin& animationOrigin ) {
const Ease::Interpolation& timingFunction, const std::vector<double>& timingFunctionParameters,
const AnimationOrigin& animationOrigin ) {
AnimationDefinition animation;
animation.setDelay( delay );
animation.setDuration( duration );
animation.setTimingFunction( timingFunction );
animation.setTimingFunctionParameters( timingFunctionParameters );
return New( animation, property, { startValue, endValue }, { 0, 1 }, propertyIndex,
animationOrigin );
}
@@ -342,8 +370,8 @@ void StyleSheetPropertyAnimation::onUpdate( const Time& ) {
Float curTime = normalizedProgress - mAnimationStepsTime[curPos - 1];
Float relativeProgress = curTime / relTime;
tweenProperty( widget, relativeProgress, mPropertyDef, mStates[curPos - 1],
mStates[curPos], mAnimation.getTimingFunction(), mPropertyIndex,
isDone() );
mStates[curPos], mAnimation.getTimingFunction(),
mAnimation.getTimingFunctionParameters(), mPropertyIndex, isDone() );
}
}
}

View File

@@ -0,0 +1,29 @@
#include <eepp/system/functionstring.hpp>
#include <eepp/ui/css/timingfunction.hpp>
using namespace EE::System;
namespace EE { namespace UI { namespace CSS {
TimingFunction TimingFunction::parse( std::string timingFunction ) {
String::trimInPlace( timingFunction );
String::toLowerInPlace( timingFunction );
if ( timingFunction.find_first_of( '(' ) == std::string::npos ) {
return { Ease::fromName( timingFunction ), {} };
} else {
TimingFunction tf;
FunctionString func( FunctionString::parse( timingFunction ) );
if ( !func.isEmpty() ) {
tf.interpolation = Ease::fromName( timingFunction, Ease::Interpolation::None );
const auto& params = func.getParameters();
for ( const auto& param : params ) {
double d;
if ( String::fromString( d, param ) )
tf.parameters.emplace_back( d );
}
}
return tf;
}
}
}}} // namespace EE::UI::CSS

View File

@@ -1,5 +1,7 @@
#include <eepp/core/string.hpp>
#include <eepp/system/functionstring.hpp>
#include <eepp/ui/css/propertydefinition.hpp>
#include <eepp/ui/css/timingfunction.hpp>
#include <eepp/ui/css/transitiondefinition.hpp>
namespace EE { namespace UI { namespace CSS {
@@ -10,6 +12,7 @@ std::map<std::string, TransitionDefinition> TransitionDefinition::parseTransitio
std::vector<Time> durations;
std::vector<Time> delays;
std::vector<Ease::Interpolation> timingFunctions;
std::vector<std::vector<double>> timingFunctionParameters;
TransitionsMap transitions;
for ( auto& prop : styleSheetProperties ) {
@@ -19,11 +22,11 @@ std::map<std::string, TransitionDefinition> TransitionDefinition::parseTransitio
const PropertyDefinition* propDef = prop->getPropertyDefinition();
if ( propDef->getPropertyId() == PropertyId::Transition ) {
auto strTransitions = String::split( prop->getValue(), ',' );
auto strTransitions = String::split( prop->getValue(), ",", ",", "()" );
for ( auto tit = strTransitions.begin(); tit != strTransitions.end(); ++tit ) {
auto strTransition = String::trim( *tit );
auto splitTransition = String::split( strTransition, ' ' );
auto splitTransition = String::split( strTransition, " ", "", "()" );
if ( !splitTransition.empty() ) {
TransitionDefinition transitionDef;
@@ -40,11 +43,12 @@ std::map<std::string, TransitionDefinition> TransitionDefinition::parseTransitio
transitionDef.duration = duration;
if ( splitTransition.size() >= 3 ) {
transitionDef.timingFunction =
Ease::fromName( String::toLower( splitTransition[2] ) );
TimingFunction tf( TimingFunction::parse( splitTransition[2] ) );
transitionDef.timingFunction = std::move( tf.interpolation );
transitionDef.timingFunctionParameters = std::move( tf.parameters );
if ( transitionDef.timingFunction == Ease::Linear &&
splitTransition[2] != "linear" && splitTransition.size() == 3 ) {
if ( transitionDef.timingFunction == Ease::None &&
splitTransition.size() == 3 ) {
transitionDef.delay =
StyleSheetProperty( prop->getName(),
String::toLower( splitTransition[2] ) )
@@ -78,12 +82,12 @@ std::map<std::string, TransitionDefinition> TransitionDefinition::parseTransitio
delays.push_back( StyleSheetProperty( prop->getName(), delay ).asTime() );
}
} else if ( propDef->getPropertyId() == PropertyId::TransitionTimingFunction ) {
auto strTimingFuncs = String::split( prop->getValue(), ',' );
auto strTimingFuncs = String::split( prop->getValue(), ",", "", "()" );
for ( auto dit = strTimingFuncs.begin(); dit != strTimingFuncs.end(); ++dit ) {
std::string timingFunction( String::trim( *dit ) );
String::toLowerInPlace( timingFunction );
timingFunctions.push_back( Ease::fromName( timingFunction ) );
TimingFunction tf( TimingFunction::parse( *dit ) );
timingFunctions.emplace_back( tf.interpolation );
timingFunctionParameters.emplace_back( tf.parameters );
}
} else if ( propDef->getPropertyId() == PropertyId::TransitionProperty ) {
auto strProperties = String::split( prop->getValue(), ',' );
@@ -108,8 +112,11 @@ std::map<std::string, TransitionDefinition> TransitionDefinition::parseTransitio
if ( !delays.empty() )
transitionDef.delay = delays[i % delays.size()];
if ( timingFunctions.empty() )
transitionDef.timingFunction = timingFunctions[i % delays.size()];
if ( !timingFunctions.empty() ) {
size_t idx = !delays.empty() ? i % delays.size() : 0;
transitionDef.timingFunction = timingFunctions[idx];
transitionDef.timingFunctionParameters = timingFunctionParameters[idx];
}
transitions[property] = transitionDef;
}

View File

@@ -494,7 +494,8 @@ void UIStyle::applyStyleSheetProperty( const StyleSheetProperty& property,
StyleSheetPropertyAnimation* newTransition = StyleSheetPropertyAnimation::New(
propertyDefinition, startValue, property.getValue(), property.getIndex(),
transitionInfo.getDuration(), transitionInfo.getDelay(),
transitionInfo.getTimingFunction(), AnimationOrigin::Transition );
transitionInfo.getTimingFunction(), transitionInfo.getTimingFunctionParameters(),
AnimationOrigin::Transition );
newTransition->setElapsed( elapsed );
newTransition->setTag( propertyDefinition->getId() );
mWidget->runAction( newTransition );

View File

@@ -60,8 +60,7 @@ void UIViewPager::onChildCountChange( Node* child, const bool& removed ) {
}
if ( !removed && child != mContainer ) {
child->addEventListener( Event::OnPositionChange,
[&]( const Event* event ) { updateChilds(); } );
child->addEventListener( Event::OnPositionChange, [&]( const Event* ) { updateChilds(); } );
}
UIWidget::onChildCountChange( child, removed );
@@ -230,7 +229,7 @@ void UIViewPager::moveToPage( const Int32& pageNum, bool animate ) {
}
}
Uint32 UIViewPager::onCalculateDrag( const Vector2f& position, const Uint32& flags ) {
Uint32 UIViewPager::onCalculateDrag( const Vector2f&, const Uint32& flags ) {
if ( !( flags & EE_BUTTON_LMASK ) ) {
onMouseUpEvent();
}