mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
Added support to "cubic-bezier" timing function.
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
22
include/eepp/ui/css/timingfunction.hpp
Normal file
22
include/eepp/ui/css/timingfunction.hpp
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
src/eepp/ui/css/timingfunction.cpp
Normal file
29
src/eepp/ui/css/timingfunction.cpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user