TimingFunction.cpp [plain text]
#include "config.h"
#include "TimingFunction.h"
#include "CSSTimingFunctionValue.h"
#include "SpringSolver.h"
#include "StyleProperties.h"
#include "UnitBezier.h"
#include <wtf/text/StringConcatenateNumbers.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
TextStream& operator<<(TextStream& ts, const TimingFunction& timingFunction)
{
switch (timingFunction.type()) {
case TimingFunction::LinearFunction:
ts << "linear";
break;
case TimingFunction::CubicBezierFunction: {
auto& function = downcast<CubicBezierTimingFunction>(timingFunction);
ts << "cubic-bezier(" << function.x1() << ", " << function.y1() << ", " << function.x2() << ", " << function.y2() << ")";
break;
}
case TimingFunction::StepsFunction: {
auto& function = downcast<StepsTimingFunction>(timingFunction);
ts << "steps(" << function.numberOfSteps() << ", " << (function.stepAtStart() ? "start" : "end") << ")";
break;
}
case TimingFunction::SpringFunction: {
auto& function = downcast<SpringTimingFunction>(timingFunction);
ts << "spring(" << function.mass() << " " << function.stiffness() << " " << function.damping() << " " << function.initialVelocity() << ")";
break;
}
}
return ts;
}
double TimingFunction::transformTime(double inputTime, double duration, bool before) const
{
switch (m_type) {
case TimingFunction::CubicBezierFunction: {
auto& function = downcast<CubicBezierTimingFunction>(*this);
if (function.isLinear())
return inputTime;
auto epsilon = 1.0 / (1000.0 * duration);
return UnitBezier(function.x1(), function.y1(), function.x2(), function.y2()).solve(inputTime, epsilon);
}
case TimingFunction::StepsFunction: {
auto& function = downcast<StepsTimingFunction>(*this);
auto steps = function.numberOfSteps();
auto currentStep = std::floor(inputTime * steps);
if (function.stepAtStart())
currentStep++;
if (before && !fmod(inputTime * steps, 1))
currentStep--;
if (inputTime >= 0 && currentStep < 0)
currentStep = 0;
if (inputTime <= 1 && currentStep > steps)
currentStep = steps;
return currentStep / steps;
}
case TimingFunction::SpringFunction: {
auto& function = downcast<SpringTimingFunction>(*this);
return SpringSolver(function.mass(), function.stiffness(), function.damping(), function.initialVelocity()).solve(inputTime * duration);
}
case TimingFunction::LinearFunction:
return inputTime;
}
ASSERT_NOT_REACHED();
return 0;
}
ExceptionOr<RefPtr<TimingFunction>> TimingFunction::createFromCSSText(const String& cssText)
{
StringBuilder cssString;
cssString.append(getPropertyNameString(CSSPropertyAnimationTimingFunction));
cssString.appendLiteral(": ");
cssString.append(cssText);
auto styleProperties = MutableStyleProperties::create();
styleProperties->parseDeclaration(cssString.toString(), CSSParserContext(HTMLStandardMode));
if (auto cssValue = styleProperties->getPropertyCSSValue(CSSPropertyAnimationTimingFunction)) {
if (auto timingFunction = createFromCSSValue(*cssValue.get()))
return timingFunction;
}
return Exception { TypeError };
}
RefPtr<TimingFunction> TimingFunction::createFromCSSValue(const CSSValue& value)
{
if (is<CSSPrimitiveValue>(value)) {
switch (downcast<CSSPrimitiveValue>(value).valueID()) {
case CSSValueLinear:
return LinearTimingFunction::create();
case CSSValueEase:
return CubicBezierTimingFunction::create();
case CSSValueEaseIn:
return CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseIn);
case CSSValueEaseOut:
return CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseOut);
case CSSValueEaseInOut:
return CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseInOut);
case CSSValueStepStart:
return StepsTimingFunction::create(1, true);
case CSSValueStepEnd:
return StepsTimingFunction::create(1, false);
default:
return nullptr;
}
}
if (is<CSSCubicBezierTimingFunctionValue>(value)) {
auto& cubicTimingFunction = downcast<CSSCubicBezierTimingFunctionValue>(value);
return CubicBezierTimingFunction::create(cubicTimingFunction.x1(), cubicTimingFunction.y1(), cubicTimingFunction.x2(), cubicTimingFunction.y2());
}
if (is<CSSStepsTimingFunctionValue>(value)) {
auto& stepsTimingFunction = downcast<CSSStepsTimingFunctionValue>(value);
return StepsTimingFunction::create(stepsTimingFunction.numberOfSteps(), stepsTimingFunction.stepAtStart());
}
if (is<CSSSpringTimingFunctionValue>(value)) {
auto& springTimingFunction = downcast<CSSSpringTimingFunctionValue>(value);
return SpringTimingFunction::create(springTimingFunction.mass(), springTimingFunction.stiffness(), springTimingFunction.damping(), springTimingFunction.initialVelocity());
}
return nullptr;
}
String TimingFunction::cssText() const
{
if (m_type == TimingFunction::CubicBezierFunction) {
auto& function = downcast<CubicBezierTimingFunction>(*this);
if (function.x1() == 0.25 && function.y1() == 0.1 && function.x2() == 0.25 && function.y2() == 1.0)
return "ease";
if (function.x1() == 0.42 && !function.y1() && function.x2() == 1.0 && function.y2() == 1.0)
return "ease-in";
if (!function.x1() && !function.y1() && function.x2() == 0.58 && function.y2() == 1.0)
return "ease-out";
if (function.x1() == 0.42 && !function.y1() && function.x2() == 0.58 && function.y2() == 1.0)
return "ease-in-out";
return makeString("cubic-bezier(", function.x1(), ", ", function.y1(), ", ", function.x2(), ", ", function.y2(), ')');
}
if (m_type == TimingFunction::StepsFunction) {
auto& function = downcast<StepsTimingFunction>(*this);
if (!function.stepAtStart())
return makeString("steps(", function.numberOfSteps(), ')');
}
TextStream stream;
stream << *this;
return stream.release();
}
}