Skip to content

Commit

Permalink
Transitions working for pseudo-classes. Working on element class.chan…
Browse files Browse the repository at this point in the history
…ges. Experimenting with reversal of transitions.
  • Loading branch information
mikke89 committed Aug 23, 2018
1 parent a9388f5 commit 23bdc17
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 92 deletions.
9 changes: 7 additions & 2 deletions Include/Rocket/Core/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,16 @@ class ROCKETCORE_API Element : public ScriptInterface
const Vector2f Project(const Vector2f& point) noexcept;

/// Start an animation of the given property on this element.
/// Target values must have the same unit as the property set on this element. Keywords are not supported.
/// If an animation of the same property name exists, the target value and duration will be added as a new animation key,
/// adding to its total duration. Then, num_iterations, alternate_direction and delay will be ignored.
/// If start_value is null, the current property value on this element is used.
/// @return True if a new animation or key was added.
bool Animate(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{}, int num_iterations = 1, bool alternate_direction = true, float delay = 0.0f, bool replace = false);
bool Animate(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{}, int num_iterations = 1, bool alternate_direction = true, float delay = 0.0f, const Property* start_value = nullptr);

/// Start a transition of the given property on this element.
/// If an animation exists for the property, the call will be ignored. If a transition exists for this property, it will be replaced.
/// @return True if the transition was added.
bool StartTransition(const Transition& transition, const Property& start_value, const Property& target_value);

/// Iterates over the properties defined on this element.
/// @param[inout] index Index of the property to fetch. This is incremented to the next valid index after the fetch. Indices are not necessarily incremental.
Expand Down
1 change: 1 addition & 0 deletions Include/Rocket/Core/Transition.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct Transition {
Tween tween;
float duration = 0.0f;
float delay = 0.0f;
float reverse_adjustment_factor = 0.0f;
};

struct TransitionList {
Expand Down
5 changes: 5 additions & 0 deletions Include/Rocket/Core/Tween.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ class Tween {
return t;
}

void reverse()
{
std::swap(type_in, type_out);
}

// Tweening functions below.
// Partly based on http://libclaw.sourceforge.net/tweeners.html

Expand Down
23 changes: 18 additions & 5 deletions Samples/basic/animation/data/animation.rml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{
max-width: 2000px;
max-height: 2000px;
width: 1400px;
width: 1600px;
height: 750px;
perspective: 3000px;
/*opacity: 0;*/
Expand Down Expand Up @@ -85,19 +85,28 @@
#text_align {
text-align: left;
}
/* -- TRANSITION TESTS */
#transition_test {
transition: padding-left 1.6s elastic-out, background-color 1.2s quadratic-out, transform 1.3s exponential-out;
/*transition: padding-left 1.6s elastic-in 1.0, background-color 1.2s quadratic-in 1.0, transform 1.3s exponential-in 1.0;*/
transition: padding-left background-color transform 1.6s elastic-out 0.0;
transform: scale(1.0);
}
#transition_test:hover {
/*transition: padding-left background-color transform 0.8s quadratic-out 1.0;*/
padding-left: 60px;
transform: scale(1.5);
}
#transition_class {
transition: margin-left 1s cubic-in-out;
}
#transition_class.blue {
margin-left: -50px;
}
</style>
</head>

<body template="window">
<div style="width: 30%; height: 80%; position: absolute;">
<div style="width: 20%; height: 80%; position: absolute;">
<div style="font-size: 0.85em; text-align: left;" id="fps"></div>
<button id="start_game">Start Game</button><br />
<button id="high_scores" onkeydown="hello">High Scores</button><br />
Expand All @@ -106,18 +115,22 @@
<button id="exit" onclick="exit">Exit</button>
</div>

<div style="width: 25%; height: 80%;position: absolute; left: 40%;" id="transform_tests">
<div style="width: 20%; height: 80%;position: absolute; left: 28%;" id="transform_tests">
<div style="font-size: 1.5em; text-align: left;">Test transform animations</div>
<div class="container"><div class="plain" id="generic">Generic form conversion.</div></div>
<div class="container"><div class="plain" id="combine">Match different transform primitive sizes</div></div>
<div class="container"><div class="plain" id="decomposition">Force full matrix decomposition</div></div>
</div>

<div style="width: 25%; height: 80%;position: absolute; left: 70%;" id="mixed_units_tests">
<div style="width: 20%; height: 80%;position: absolute; left: 52%;" id="mixed_units_tests">
<div style="font-size: 1.5em; text-align: left;">Mixed units tests</div>
<div class="container"><div class="plain" id="abs_rel">Pixel vs percentage.</div></div>
<div class="container"><div class="plain" id="abs_rel_transform">Pixel vs percentage transform.</div></div>
</div>
<div style="width: 20%; height: 80%;position: absolute; left: 75%;" id="transition_tests">
<div style="font-size: 1.5em; text-align: left;">Transition tests</div>
<div class="container"><div class="plain" id="transition_test">Transition test (hover)</div></div>
<div class="container"><div class="plain" id="transition_class" onclick="add_class">Transition class (click)</div></div>
</div>
</body>
</rml>
11 changes: 10 additions & 1 deletion Samples/basic/animation/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class DemoWindow
// Mixed units tests
{
auto el = document->GetElementById("abs_rel");
el->Animate("margin-left", Property(100.f, Property::PERCENT), 1.5f, Tween{}, -1, true);
el->Animate("margin-left", Property(50.f, Property::PERCENT), 1.5f, Tween{}, -1, true);
}
{
auto el = document->GetElementById("abs_rel_transform");
Expand Down Expand Up @@ -243,6 +243,15 @@ class Event : public Rocket::Core::EventListener
Rocket::Debugger::SetVisible(!Rocket::Debugger::IsVisible());
}
}
if (event == "click")
{
auto el = event.GetTargetElement();
if (el->GetId() == "transition_class")
{
// TODO: Doesn't seem to properly animate
el->SetClass("blue", !el->IsClassSet("blue"));
}
}
}

void OnDetach(Rocket::Core::Element* element) override { delete this; }
Expand Down
64 changes: 48 additions & 16 deletions Source/Core/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,11 +911,8 @@ const Vector2f Element::Project(const Vector2f& point) noexcept
}
}

bool Element::Animate(const String & property_name, const Property & target_value, float duration, Tween tween, int num_iterations, bool alternate_direction, float delay, bool replace)
bool Element::Animate(const String & property_name, const Property & target_value, float duration, Tween tween, int num_iterations, bool alternate_direction, float delay, const Property* start_value)
{
if (delay < 0.0f)
return false;

ElementAnimation* animation = nullptr;

for (auto& existing_animation : animations)
Expand All @@ -932,33 +929,68 @@ bool Element::Animate(const String & property_name, const Property & target_valu
if (!animation)
{
float start_time = Clock::GetElapsedTime() + delay;
const Property* property = GetProperty(property_name);
if (!property || !property->definition)
if(!start_value)
start_value = GetProperty(property_name);
if (!start_value || !start_value->definition)
return false;

animations.push_back(
ElementAnimation{ property_name, *property, start_time, duration, num_iterations, alternate_direction }
ElementAnimation{ property_name, *start_value, start_time, duration, num_iterations, alternate_direction, false }
);
animation = &animations.back();
}
else if (replace)
{
animation->ReverseAnimation();
animation = nullptr;
}
else
{
target_time += animation->GetDuration();
animation->SetDuration(target_time);
}

bool result = false;
bool result = animation->AddKey(target_time, target_value, *this, tween);

if(animation)
return result;
}

bool Element::StartTransition(const Transition & transition, const Property& start_value, const Property & target_value)
{
ElementAnimation* animation = nullptr;

for (auto& existing_animation : animations)
{
result = animation->AddKey(target_time, target_value, *this, tween);
if (existing_animation.GetPropertyName() == transition.name)
{
animation = &existing_animation;
break;
}
}


if (animation && !animation->IsTransition())
return false;

float duration = transition.duration;

if (!animation)
{
float start_time = Clock::GetElapsedTime() + transition.delay;

animations.push_back(
ElementAnimation{ transition.name, start_value, start_time, duration, 1, false, true }
);
animation = &animations.back();
}
else
{
float start_time = Clock::GetElapsedTime() + transition.delay;

// Compress the duration based on the progress of the current animation
float f = animation->GetInterpolationFactor();
f = 1.0f - (1.0f - f)*transition.reverse_adjustment_factor;
duration = duration * f;

*animation = ElementAnimation{ transition.name, start_value, start_time, duration, 1, false, true };
}

bool result = animation->AddKey(duration, target_value, *this, transition.tween);

return result;
}

Expand Down
79 changes: 46 additions & 33 deletions Source/Core/ElementAnimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,10 @@ static bool PrepareTransforms(std::vector<AnimationKey>& keys, Element& element,
}


ElementAnimation::ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction)
ElementAnimation::ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction, bool is_transition)
: property_name(property_name), duration(duration), num_iterations(num_iterations), alternate_direction(alternate_direction),
keys({ AnimationKey{0.0f, current_value, Tween{}} }),
last_update_world_time(start_world_time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false), reverse_animation(false)
last_update_world_time(start_world_time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false), is_transition(is_transition)
{
ROCKET_ASSERT(current_value.definition);
}
Expand Down Expand Up @@ -407,42 +407,13 @@ bool ElementAnimation::AddKey(float time, const Property & property, Element& el
return result;
}


Property ElementAnimation::UpdateAndGetProperty(float world_time, Element& element)
float ElementAnimation::GetInterpolationFactorAndKeys(int* out_key0, int* out_key1) const
{
if (animation_complete || world_time - last_update_world_time <= 0.0f)
return Property{};

const float dt = Math::Min(world_time - last_update_world_time, 0.1f);

last_update_world_time = world_time;
time_since_iteration_start += dt;

if (time_since_iteration_start >= duration)
{
// Next iteration
current_iteration += (reverse_animation ? -1 : 1);

if (num_iterations == -1 || (current_iteration >= 0 && current_iteration < num_iterations) )
{
time_since_iteration_start -= duration;

if (alternate_direction)
reverse_direction = !reverse_direction;
}
else
{
animation_complete = true;
time_since_iteration_start = duration;
}
}

float t = time_since_iteration_start;

if (reverse_direction)
t = duration - t;
if (reverse_animation)
t = duration - t;

int key0 = -1;
int key1 = -1;
Expand All @@ -458,7 +429,7 @@ Property ElementAnimation::UpdateAndGetProperty(float world_time, Element& eleme
}

if (key1 < 0) key1 = (int)keys.size() - 1;
key0 = (key1 == 0 ? 0 : key1 - 1 );
key0 = (key1 == 0 ? 0 : key1 - 1);
}

ROCKET_ASSERT(key0 >= 0 && key0 < (int)keys.size() && key1 >= 0 && key1 < (int)keys.size());
Expand All @@ -479,6 +450,48 @@ Property ElementAnimation::UpdateAndGetProperty(float world_time, Element& eleme

alpha = keys[key1].tween(alpha);

if (out_key0) *out_key0 = key0;
if (out_key1) *out_key1 = key1;

return alpha;
}



Property ElementAnimation::UpdateAndGetProperty(float world_time, Element& element)
{
if (animation_complete || world_time - last_update_world_time <= 0.0f)
return Property{};

const float dt = Math::Min(world_time - last_update_world_time, 0.1f);

last_update_world_time = world_time;
time_since_iteration_start += dt;

if (time_since_iteration_start >= duration)
{
// Next iteration
current_iteration += 1;

if (num_iterations == -1 || (current_iteration >= 0 && current_iteration < num_iterations) )
{
time_since_iteration_start -= duration;

if (alternate_direction)
reverse_direction = !reverse_direction;
}
else
{
animation_complete = true;
time_since_iteration_start = duration;
}
}

int key0 = -1;
int key1 = -1;

float alpha = GetInterpolationFactorAndKeys(&key0, &key1);

Property result = InterpolateProperties(keys[key0].property, keys[key1].property, alpha, element, keys[0].property.definition);

return result;
Expand Down
10 changes: 6 additions & 4 deletions Source/Core/ElementAnimation.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace Core {


struct AnimationKey {
float time;
float time; // Local animation time (Zero means the time when the animation iteration starts)
Property property;
Tween tween; // Tweening between the previous and this key. Ignored for the first animation key.
};
Expand All @@ -60,11 +60,12 @@ class ElementAnimation
bool reverse_direction;

bool animation_complete;
bool reverse_animation;
bool is_transition;

float GetInterpolationFactorAndKeys(int* out_key0, int* out_key1) const;
public:

ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction);
ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction, bool is_transition);

bool AddKey(float time, const Property& property, Element& element, Tween tween);

Expand All @@ -74,7 +75,8 @@ class ElementAnimation
float GetDuration() const { return duration; }
void SetDuration(float duration) { this->duration = duration; }
bool IsComplete() const { return animation_complete; }
void ReverseAnimation() { reverse_animation = !reverse_animation; }
bool IsTransition() const { return is_transition; }
float GetInterpolationFactor() const { return GetInterpolationFactorAndKeys(nullptr, nullptr); }
};


Expand Down
Loading

0 comments on commit 23bdc17

Please sign in to comment.