Modified: trunk/Source/WebCore/platform/ScrollAnimationSmooth.cpp (282720 => 282721)
--- trunk/Source/WebCore/platform/ScrollAnimationSmooth.cpp 2021-09-18 04:24:53 UTC (rev 282720)
+++ trunk/Source/WebCore/platform/ScrollAnimationSmooth.cpp 2021-09-18 06:03:19 UTC (rev 282721)
@@ -29,7 +29,9 @@
#include "ScrollAnimationSmooth.h"
#include "FloatPoint.h"
+#include "GeometryUtilities.h"
#include "ScrollableArea.h"
+#include "TimingFunction.h"
#if USE(GLIB_EVENT_LOOP)
#include <wtf/glib/RunLoopSourcePriority.h>
@@ -37,14 +39,13 @@
namespace WebCore {
-static const double frameRate = 60;
-static const Seconds tickTime = 1_s / frameRate;
-static const Seconds minimumTimerInterval { 1_ms };
-static const double smoothFactorForProgrammaticScroll = 1;
+static const float animationSpeed { 1000.0f };
+static const Seconds maxAnimationDuration { 200_ms };
ScrollAnimationSmooth::ScrollAnimationSmooth(ScrollAnimationClient& client)
: ScrollAnimation(client)
, m_animationTimer(RunLoop::current(), this, &ScrollAnimationSmooth::animationTimerFired)
+ , m_easeInOutTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::TimingFunctionPreset::EaseInOut))
{
#if USE(GLIB_EVENT_LOOP)
m_animationTimer.setPriority(WTF::RunLoopSourcePriority::DisplayRefreshMonitorTimer);
@@ -53,34 +54,34 @@
ScrollAnimationSmooth::~ScrollAnimationSmooth() = default;
-bool ScrollAnimationSmooth::startAnimatedScroll(ScrollbarOrientation orientation, ScrollGranularity granularity, const FloatPoint& fromPosition, float step, float multiplier)
+bool ScrollAnimationSmooth::startAnimatedScroll(ScrollbarOrientation orientation, ScrollGranularity, const FloatPoint& fromPosition, float step, float multiplier)
{
- float minScrollPosition;
- float maxScrollPosition;
+ m_startPosition = fromPosition;
+ auto destinationPosition = fromPosition;
+ switch (orientation) {
+ case HorizontalScrollbar:
+ destinationPosition.setX(destinationPosition.x() + step * multiplier);
+ break;
+ case VerticalScrollbar:
+ destinationPosition.setY(destinationPosition.y() + step * multiplier);
+ break;
+ }
+
+ m_duration = durationFromDistance(destinationPosition - m_startPosition);
+ WTFLogAlways("Animation duration: %.2fms", m_duration.milliseconds());
+
auto extents = m_client.scrollExtentsForAnimation(*this);
- initializeAxesData(extents, fromPosition);
-
- if (orientation == HorizontalScrollbar) {
- minScrollPosition = extents.minimumScrollPosition.x();
- maxScrollPosition = extents.maximumScrollPosition.x();
- } else {
- minScrollPosition = extents.minimumScrollPosition.y();
- maxScrollPosition = extents.maximumScrollPosition.y();
- }
- auto& data = "" == HorizontalScrollbar ? m_horizontalData : m_verticalData;
- bool needToScroll = updatePerAxisData(data, granularity, data.desiredPosition + (step * multiplier), minScrollPosition, maxScrollPosition);
- if (needToScroll && !isActive()) {
- m_startTime = orientation == HorizontalScrollbar ? m_horizontalData.startTime : m_verticalData.startTime;
- animationTimerFired();
- }
- return needToScroll;
+ return startOrRetargetAnimation(extents, destinationPosition);
}
bool ScrollAnimationSmooth::startAnimatedScrollToDestination(const FloatPoint& fromPosition, const FloatPoint& destinationPosition)
{
+ m_startPosition = fromPosition;
+ m_duration = durationFromDistance(destinationPosition - m_startPosition);
+
+ WTFLogAlways("Animation duration: %.2fms", m_duration.milliseconds());
+
auto extents = m_client.scrollExtentsForAnimation(*this);
- initializeAxesData(extents, fromPosition);
-
return startOrRetargetAnimation(extents, destinationPosition);
}
@@ -95,12 +96,11 @@
bool ScrollAnimationSmooth::startOrRetargetAnimation(const ScrollExtents& extents, const FloatPoint& destinationPosition)
{
- auto granularity = ScrollByPage;
- bool needToScroll = updatePerAxisData(m_horizontalData, granularity, destinationPosition.x(), extents.minimumScrollPosition.x(), extents.maximumScrollPosition.x(), smoothFactorForProgrammaticScroll);
- needToScroll |=
- updatePerAxisData(m_verticalData, granularity, destinationPosition.y(), extents.minimumScrollPosition.y(), extents.maximumScrollPosition.y(), smoothFactorForProgrammaticScroll);
+ m_destinationPosition = destinationPosition.constrainedBetween(extents.minimumScrollPosition, extents.maximumScrollPosition);
+ bool needToScroll = m_startPosition != m_destinationPosition;
+
if (needToScroll && !isActive()) {
- m_startTime = m_verticalData.startTime;
+ m_startTime = MonotonicTime::now();
animationTimerFired();
}
return needToScroll;
@@ -115,311 +115,37 @@
void ScrollAnimationSmooth::updateScrollExtents()
{
auto extents = m_client.scrollExtentsForAnimation(*this);
- m_horizontalData.visibleLength = extents.visibleSize.width();
- m_verticalData.visibleLength = extents.visibleSize.height();
+ // FIXME: Ideally fix up m_startPosition so m_currentPosition doesn't go backwards.
+ m_destinationPosition = m_destinationPosition.constrainedBetween(extents.minimumScrollPosition, extents.maximumScrollPosition);
}
-void ScrollAnimationSmooth::initializeAxesData(const ScrollExtents& extents, const FloatPoint& startPosition)
+Seconds ScrollAnimationSmooth::durationFromDistance(const FloatSize& delta) const
{
- m_horizontalData = PerAxisData(startPosition.x(), extents.visibleSize.width());
- m_verticalData = PerAxisData(startPosition.y(), extents.visibleSize.height());
+ float distance = euclidianDistance(delta);
+ return std::min(Seconds(distance / animationSpeed), maxAnimationDuration);
}
-static inline double curveAt(ScrollAnimationSmooth::Curve curve, double t)
+inline float linearInterpolation(float progress, float a, float b)
{
- switch (curve) {
- case ScrollAnimationSmooth::Curve::Linear:
- return t;
- case ScrollAnimationSmooth::Curve::Quadratic:
- return t * t;
- case ScrollAnimationSmooth::Curve::Cubic:
- return t * t * t;
- case ScrollAnimationSmooth::Curve::Quartic:
- return t * t * t * t;
- case ScrollAnimationSmooth::Curve::Bounce:
- // Time base is chosen to keep the bounce points simpler:
- // 1 (half bounce coming in) + 1 + .5 + .25
- static const double timeBase = 2.75;
- static const double timeBaseSquared = timeBase * timeBase;
- if (t < 1 / timeBase)
- return timeBaseSquared * t * t;
- if (t < 2 / timeBase) {
- // Invert a [-.5,.5] quadratic parabola, center it in [1,2].
- double t1 = t - 1.5 / timeBase;
- const double parabolaAtEdge = 1 - .5 * .5;
- return timeBaseSquared * t1 * t1 + parabolaAtEdge;
- }
- if (t < 2.5 / timeBase) {
- // Invert a [-.25,.25] quadratic parabola, center it in [2,2.5].
- double t2 = t - 2.25 / timeBase;
- const double parabolaAtEdge = 1 - .25 * .25;
- return timeBaseSquared * t2 * t2 + parabolaAtEdge;
- }
- // Invert a [-.125,.125] quadratic parabola, center it in [2.5,2.75].
- const double parabolaAtEdge = 1 - .125 * .125;
- t -= 2.625 / timeBase;
- return timeBaseSquared * t * t + parabolaAtEdge;
- }
- ASSERT_NOT_REACHED();
- return 0;
+ return a + progress * (b - a);
}
-static inline double attackCurve(ScrollAnimationSmooth::Curve curve, double deltaTime, double curveT, double startPosition, double attackPosition)
+bool ScrollAnimationSmooth::animateScroll(MonotonicTime currentTime)
{
- double t = deltaTime / curveT;
- double positionFactor = curveAt(curve, t);
- return startPosition + positionFactor * (attackPosition - startPosition);
-}
+ MonotonicTime endTime = m_startTime + m_duration;
+ currentTime = std::min(currentTime, endTime);
-static inline double releaseCurve(ScrollAnimationSmooth::Curve curve, double deltaTime, double curveT, double releasePosition, double desiredPosition)
-{
- double t = deltaTime / curveT;
- double positionFactor = 1 - curveAt(curve, 1 - t);
- return releasePosition + (positionFactor * (desiredPosition - releasePosition));
-}
+ double fractionComplete = (currentTime - m_startTime) / m_duration;
+ double progress = m_easeInOutTimingFunction->transformTime(fractionComplete, m_duration.value());
-static inline double coastCurve(ScrollAnimationSmooth::Curve curve, double factor)
-{
- return 1 - curveAt(curve, 1 - factor);
-}
+ m_currentPosition = {
+ linearInterpolation(progress, m_startPosition.x(), m_destinationPosition.x()),
+ linearInterpolation(progress, m_startPosition.y(), m_destinationPosition.y()),
+ };
-static inline double curveIntegralAt(ScrollAnimationSmooth::Curve curve, double t)
-{
- switch (curve) {
- case ScrollAnimationSmooth::Curve::Linear:
- return t * t / 2;
- case ScrollAnimationSmooth::Curve::Quadratic:
- return t * t * t / 3;
- case ScrollAnimationSmooth::Curve::Cubic:
- return t * t * t * t / 4;
- case ScrollAnimationSmooth::Curve::Quartic:
- return t * t * t * t * t / 5;
- case ScrollAnimationSmooth::Curve::Bounce:
- static const double timeBase = 2.75;
- static const double timeBaseSquared = timeBase * timeBase;
- static const double timeBaseSquaredOverThree = timeBaseSquared / 3;
- double area;
- double t1 = std::min(t, 1 / timeBase);
- area = timeBaseSquaredOverThree * t1 * t1 * t1;
- if (t < 1 / timeBase)
- return area;
-
- t1 = std::min(t - 1 / timeBase, 1 / timeBase);
- // The integral of timeBaseSquared * (t1 - .5 / timeBase) * (t1 - .5 / timeBase) + parabolaAtEdge
- static const double secondInnerOffset = timeBaseSquared * .5 / timeBase;
- double bounceArea = t1 * (t1 * (timeBaseSquaredOverThree * t1 - secondInnerOffset) + 1);
- area += bounceArea;
- if (t < 2 / timeBase)
- return area;
-
- t1 = std::min(t - 2 / timeBase, 0.5 / timeBase);
- // The integral of timeBaseSquared * (t1 - .25 / timeBase) * (t1 - .25 / timeBase) + parabolaAtEdge
- static const double thirdInnerOffset = timeBaseSquared * .25 / timeBase;
- bounceArea = t1 * (t1 * (timeBaseSquaredOverThree * t1 - thirdInnerOffset) + 1);
- area += bounceArea;
- if (t < 2.5 / timeBase)
- return area;
-
- t1 = t - 2.5 / timeBase;
- // The integral of timeBaseSquared * (t1 - .125 / timeBase) * (t1 - .125 / timeBase) + parabolaAtEdge
- static const double fourthInnerOffset = timeBaseSquared * .125 / timeBase;
- bounceArea = t1 * (t1 * (timeBaseSquaredOverThree * t1 - fourthInnerOffset) + 1);
- area += bounceArea;
- return area;
- }
- ASSERT_NOT_REACHED();
- return 0;
+ return currentTime < endTime;
}
-static inline double attackArea(ScrollAnimationSmooth::Curve curve, double startT, double endT)
-{
- double startValue = curveIntegralAt(curve, startT);
- double endValue = curveIntegralAt(curve, endT);
- return endValue - startValue;
-}
-
-static inline double releaseArea(ScrollAnimationSmooth::Curve curve, double startT, double endT)
-{
- double startValue = curveIntegralAt(curve, 1 - endT);
- double endValue = curveIntegralAt(curve, 1 - startT);
- return endValue - startValue;
-}
-
-static inline void getAnimationParametersForGranularity(ScrollGranularity granularity, Seconds& animationDuration, Seconds& repeatMinimumSustainTime, Seconds& attackDuration, Seconds& releaseDuration, ScrollAnimationSmooth::Curve& coastTimeCurve, Seconds& maximumCoastTime)
-{
- switch (granularity) {
- case ScrollByDocument:
- animationDuration = tickTime * 10;
- repeatMinimumSustainTime = tickTime * 10;
- attackDuration = tickTime * 10;
- releaseDuration = tickTime * 10;
- coastTimeCurve = ScrollAnimationSmooth::Curve::Linear;
- maximumCoastTime = 1_s;
- break;
- case ScrollByLine:
- animationDuration = tickTime * 10;
- repeatMinimumSustainTime = tickTime * 7;
- attackDuration = tickTime * 3;
- releaseDuration = tickTime * 3;
- coastTimeCurve = ScrollAnimationSmooth::Curve::Linear;
- maximumCoastTime = 1_s;
- break;
- case ScrollByPage:
- animationDuration = tickTime * 15;
- repeatMinimumSustainTime = tickTime * 10;
- attackDuration = tickTime * 5;
- releaseDuration = tickTime * 5;
- coastTimeCurve = ScrollAnimationSmooth::Curve::Linear;
- maximumCoastTime = 1_s;
- break;
- case ScrollByPixel:
- animationDuration = tickTime * 11;
- repeatMinimumSustainTime = tickTime * 2;
- attackDuration = tickTime * 3;
- releaseDuration = tickTime * 3;
- coastTimeCurve = ScrollAnimationSmooth::Curve::Quadratic;
- maximumCoastTime = 1250_ms;
- break;
- default:
- ASSERT_NOT_REACHED();
- }
-}
-
-bool ScrollAnimationSmooth::updatePerAxisData(PerAxisData& data, ScrollGranularity granularity, float newPosition, float minScrollPosition, float maxScrollPosition, double smoothFactor)
-{
- if (!data.startTime || newPosition == data.currentPosition) {
- data.desiredPosition = data.currentPosition;
- data.startTime = { };
- }
-
- newPosition = std::max(std::min(newPosition, maxScrollPosition), minScrollPosition);
- if (newPosition == data.desiredPosition)
- return false;
-
- Seconds animationDuration, repeatMinimumSustainTime, attackDuration, releaseDuration, maximumCoastTime;
- Curve coastTimeCurve;
- getAnimationParametersForGranularity(granularity, animationDuration, repeatMinimumSustainTime, attackDuration, releaseDuration, coastTimeCurve, maximumCoastTime);
-
- animationDuration *= smoothFactor;
- repeatMinimumSustainTime *= smoothFactor;
- attackDuration *= smoothFactor;
- releaseDuration *= smoothFactor;
- maximumCoastTime *= smoothFactor;
-
- data.desiredPosition = newPosition;
- if (!data.startTime)
- data.attackDuration = attackDuration;
- data.animationDuration = animationDuration;
- data.releaseDuration = releaseDuration;
-
- // Prioritize our way out of over constraint.
- if (data.attackDuration + data.releaseDuration > data.animationDuration) {
- if (data.releaseDuration > data.animationDuration)
- data.releaseDuration = data.animationDuration;
- data.attackDuration = data.animationDuration - data.releaseDuration;
- }
-
- if (!data.startTime) {
- // FIXME: This should be the time from the event that got us here.
- data.startTime = MonotonicTime::now() - tickTime / 2.;
- data.startPosition = data.currentPosition;
- data.lastAnimationTime = data.startTime;
- }
- data.startVelocity = data.currentVelocity;
-
- double remainingDelta = data.desiredPosition - data.currentPosition;
- double attackAreaLeft = 0;
- Seconds deltaTime = data.lastAnimationTime - data.startTime;
- Seconds attackTimeLeft = std::max(0_s, data.attackDuration - deltaTime);
- Seconds timeLeft = data.animationDuration - deltaTime;
- Seconds minTimeLeft = data.releaseDuration + std::min(repeatMinimumSustainTime, data.animationDuration - data.releaseDuration - attackTimeLeft);
- if (timeLeft < minTimeLeft) {
- data.animationDuration = deltaTime + minTimeLeft;
- timeLeft = minTimeLeft;
- }
-
- if (maximumCoastTime > (repeatMinimumSustainTime + releaseDuration)) {
- double targetMaxCoastVelocity = data.visibleLength * .25 * frameRate;
- // This needs to be as minimal as possible while not being intrusive to page up/down.
- double minCoastDelta = data.visibleLength;
-
- if (fabs(remainingDelta) > minCoastDelta) {
- double maxCoastDelta = maximumCoastTime.value() * targetMaxCoastVelocity;
- double coastFactor = std::min(1., (fabs(remainingDelta) - minCoastDelta) / (maxCoastDelta - minCoastDelta));
-
- // We could play with the curve here - linear seems a little soft. Initial testing makes me want to feed into the sustain time more aggressively.
- Seconds coastMinTimeLeft = std::min(maximumCoastTime, minTimeLeft + (maximumCoastTime - minTimeLeft) * coastCurve(coastTimeCurve, coastFactor));
-
- if (Seconds additionalTime = std::max(0_s, coastMinTimeLeft - minTimeLeft)) {
- Seconds additionalReleaseTime = std::min(additionalTime, additionalTime * (releaseDuration / (releaseDuration + repeatMinimumSustainTime)));
- data.releaseDuration = releaseDuration + additionalReleaseTime;
- data.animationDuration = deltaTime + coastMinTimeLeft;
- timeLeft = coastMinTimeLeft;
- }
- }
- }
-
- Seconds releaseTimeLeft = std::min(timeLeft, data.releaseDuration);
- Seconds sustainTimeLeft = std::max(0_s, timeLeft - releaseTimeLeft - attackTimeLeft);
- if (attackTimeLeft) {
- double attackSpot = deltaTime / data.attackDuration;
- attackAreaLeft = attackArea(Curve::Cubic, attackSpot, 1) * data.attackDuration.value();
- }
-
- double releaseSpot = (data.releaseDuration - releaseTimeLeft) / data.releaseDuration;
- double releaseAreaLeft = releaseArea(Curve::Cubic, releaseSpot, 1) * data.releaseDuration.value();
-
- data.desiredVelocity = remainingDelta / (attackAreaLeft + sustainTimeLeft.value() + releaseAreaLeft);
- data.releasePosition = data.desiredPosition - data.desiredVelocity * releaseAreaLeft;
- if (attackAreaLeft)
- data.attackPosition = data.startPosition + data.desiredVelocity * attackAreaLeft;
- else
- data.attackPosition = data.releasePosition - (data.animationDuration - data.releaseDuration - data.attackDuration).value() * data.desiredVelocity;
-
- if (sustainTimeLeft) {
- double roundOff = data.releasePosition - ((attackAreaLeft ? data.attackPosition : data.currentPosition) + data.desiredVelocity * sustainTimeLeft.value());
- data.desiredVelocity += roundOff / sustainTimeLeft.value();
- }
-
- return true;
-}
-
-bool ScrollAnimationSmooth::animateScroll(PerAxisData& data, MonotonicTime currentTime)
-{
- if (!data.startTime)
- return false;
-
- Seconds lastScrollInterval = currentTime - data.lastAnimationTime;
- if (lastScrollInterval < minimumTimerInterval)
- return true;
-
- data.lastAnimationTime = currentTime;
-
- Seconds deltaTime = currentTime - data.startTime;
- double newPosition = data.currentPosition;
-
- if (deltaTime > data.animationDuration) {
- data = "" data.visibleLength);
- return false;
- }
- if (deltaTime < data.attackDuration)
- newPosition = attackCurve(Curve::Cubic, deltaTime.value(), data.attackDuration.value(), data.startPosition, data.attackPosition);
- else if (deltaTime < (data.animationDuration - data.releaseDuration))
- newPosition = data.attackPosition + (deltaTime - data.attackDuration).value() * data.desiredVelocity;
- else {
- // release is based on targeting the exact final position.
- Seconds releaseDeltaT = deltaTime - (data.animationDuration - data.releaseDuration);
- newPosition = releaseCurve(Curve::Cubic, releaseDeltaT.value(), data.releaseDuration.value(), data.releasePosition, data.desiredPosition);
- }
-
- // Normalize velocity to a per second amount. Could be used to check for jank.
- if (lastScrollInterval > 0_s)
- data.currentVelocity = (newPosition - data.currentPosition) / lastScrollInterval.value();
- data.currentPosition = newPosition;
-
- return true;
-}
-
void ScrollAnimationSmooth::animationTimerFired()
{
MonotonicTime currentTime = MonotonicTime::now();
@@ -426,16 +152,11 @@
Seconds deltaToNextFrame = 1_s * ceil((currentTime - m_startTime).value() * frameRate) / frameRate - (currentTime - m_startTime);
currentTime += deltaToNextFrame;
- bool continueAnimation = false;
- if (animateScroll(m_horizontalData, currentTime))
- continueAnimation = true;
- if (animateScroll(m_verticalData, currentTime))
- continueAnimation = true;
-
+ bool continueAnimation = animateScroll(currentTime);
if (continueAnimation)
startNextTimer(std::max(minimumTimerInterval, deltaToNextFrame));
- m_client.scrollAnimationDidUpdate(*this, { m_horizontalData.currentPosition, m_verticalData.currentPosition });
+ m_client.scrollAnimationDidUpdate(*this, m_currentPosition);
if (!continueAnimation)
m_client.scrollAnimationDidEnd(*this);
}
Modified: trunk/Source/WebCore/platform/ScrollAnimationSmooth.h (282720 => 282721)
--- trunk/Source/WebCore/platform/ScrollAnimationSmooth.h 2021-09-18 04:24:53 UTC (rev 282720)
+++ trunk/Source/WebCore/platform/ScrollAnimationSmooth.h 2021-09-18 06:03:19 UTC (rev 282721)
@@ -33,7 +33,7 @@
namespace WebCore {
class FloatPoint;
-enum class ScrollClamping : bool;
+class TimingFunction;
class ScrollAnimationSmooth final: public ScrollAnimation {
public:
@@ -40,14 +40,6 @@
ScrollAnimationSmooth(ScrollAnimationClient&);
virtual ~ScrollAnimationSmooth();
- enum class Curve {
- Linear,
- Quadratic,
- Cubic,
- Quartic,
- Bounce
- };
-
bool startAnimatedScroll(ScrollbarOrientation, ScrollGranularity, const FloatPoint& fromPosition, float step, float multiplier);
bool startAnimatedScrollToDestination(const FloatPoint& fromPosition, const FloatPoint& destinationPosition);
@@ -56,57 +48,28 @@
void updateScrollExtents() final;
bool isActive() const final;
+
private:
- struct PerAxisData {
- PerAxisData() = default;
- PerAxisData(float position, int length)
- : currentPosition(position)
- , desiredPosition(position)
- , visibleLength(length)
- {
- }
-
- float currentPosition { 0 };
- double currentVelocity { 0 };
-
- double desiredPosition { 0 };
- double desiredVelocity { 0 };
-
- double startPosition { 0 };
- MonotonicTime startTime;
- double startVelocity { 0 };
-
- Seconds animationDuration;
- MonotonicTime lastAnimationTime;
-
- double attackPosition { 0 };
- Seconds attackDuration;
- Curve attackCurve { Curve::Quadratic };
-
- double releasePosition { 0 };
- Seconds releaseDuration;
- Curve releaseCurve { Curve::Quadratic };
-
- int visibleLength { 0 };
- };
-
bool startOrRetargetAnimation(const ScrollExtents&, const FloatPoint& destination);
- bool updatePerAxisData(PerAxisData&, ScrollGranularity, float newPosition, float minScrollPosition, float maxScrollPosition, double smoothFactor = 1);
- bool animateScroll(PerAxisData&, MonotonicTime currentTime);
-
- void initializeAxesData(const ScrollExtents&, const FloatPoint& startPosition);
-
void requestAnimationTimerFired();
void startNextTimer(Seconds delay);
void animationTimerFired();
+
+ Seconds durationFromDistance(const FloatSize&) const;
+
+ bool animateScroll(MonotonicTime);
- PerAxisData m_horizontalData;
- PerAxisData m_verticalData;
+ MonotonicTime m_startTime;
+ Seconds m_duration;
- MonotonicTime m_startTime;
+ FloatPoint m_startPosition;
+ FloatPoint m_destinationPosition;
+ FloatPoint m_currentPosition;
+
// FIXME: Should not have timer here, and instead use serviceAnimation().
RunLoop::Timer<ScrollAnimationSmooth> m_animationTimer;
+ RefPtr<TimingFunction> m_easeInOutTimingFunction;
};
} // namespace WebCore