Donner SVG 0.8.0
0.8.0 ยท ebf353960.8.0 main
SVG editor and embeddable C⁠+⁠+⁠20 engine.
Loading...
Searching...
No Matches
svg_animation.cc

Drive declarative SVG animation from a host clock.

Drive declarative SVG animation from a host clock Parses an SVG with <animate> and <set> elements, renders frames at chosen document times, starts a click-triggered animation, and lists the animation lifecycle boundaries. The program exits non-zero if the click starts no animation or the lifecycle boundaries differ from the ones the document declares.

bazel run //examples:svg_animation
/**
* @example svg_animation.cc Drive declarative SVG animation from a host clock
* @details Parses an SVG with `<animate>` and `<set>` elements, renders frames at chosen document
* times, starts a click-triggered animation, and lists the animation lifecycle boundaries. The
* program exits non-zero if the click starts no animation or the lifecycle boundaries differ from
* the ones the document declares.
*
* ```sh
* bazel run //examples:svg_animation
* ```
*/
#include <cstddef>
#include <iostream>
#include <optional>
#include <string_view>
#include <utility>
#include <vector>
#include "donner/svg/SVG.h"
int main() {
//! [animated_svg]
// The left square fades from red to blue over two seconds and keeps its final color. The right
// square turns green for one second after it is clicked.
constexpr std::string_view kSvg = R"(
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="32">
<rect width="32" height="32" fill="red">
<animate id="fade" attributeName="fill" from="red" to="blue" dur="2s" fill="freeze" />
</rect>
<rect id="button" x="32" width="32" height="32" fill="gray">
<set attributeName="fill" to="green" begin="button.click" dur="1s" />
</rect>
</svg>)";
//! [animated_svg]
if (maybeDocument.hasError()) {
std::cerr << "Parse Error: " << maybeDocument.error() << "\n";
return 1;
}
donner::svg::SVGDocument document = std::move(maybeDocument.result());
//! [set_time]
// Donner has no clock of its own: the host chooses each frame's document time, in seconds.
// Each draw() samples the animations at the document's current time.
for (const double seconds : {0.0, 1.0, 3.0}) {
document.setTime(seconds);
renderer.draw(document);
std::cout << "t=" << document.currentTime() << "s: " << frame.dimensions.x << "x"
<< frame.dimensions.y << " frame\n";
}
//! [set_time]
//! [dispatch_event]
// Pointer input stays with the host. Deliver a hit element's events to begin="id.click"
// triggers; the triggered interval starts at the current document time.
std::optional<donner::svg::SVGElement> button = document.querySelector("#button");
if (!button || !document.dispatchMouseEvent(donner::svg::EventType::Click, *button)) {
std::cerr << "The click did not start an animation\n";
return 1;
}
//! [dispatch_event]
//! [lifecycle_events]
// Lifecycle boundaries in the interval (from, to], for hosts that react to begin, repeat and
// end without polling every frame.
for (const donner::svg::SVGAnimationEvent& event : batch.events) {
const char* type = event.type == donner::svg::SVGAnimationEvent::Type::Begin ? "begin"
: "end";
std::cout << type << " at " << event.time << "s\n";
}
//! [lifecycle_events]
// The fade ends at 2s; the clicked square's interval runs from 3s to 4s.
const std::vector<std::pair<donner::svg::SVGAnimationEvent::Type, double>> expected = {
};
bool matches = batch.complete && batch.events.size() == expected.size();
for (std::size_t i = 0; matches && i < expected.size(); ++i) {
matches =
batch.events[i].type == expected[i].first && batch.events[i].time == expected[i].second;
}
if (!matches) {
std::cerr << "Unexpected animation lifecycle boundaries\n";
return 1;
}
//! [restart]
// Return to time zero and discard event-started intervals, as when replaying from the start.
document.restartAnimations();
//! [restart]
// The click-started interval is gone after the restart; only the authored fade's end remains.
const donner::svg::SVGAnimationEventBatch afterRestart = document.animationEvents(0.0, 5.0);
if (!afterRestart.complete || afterRestart.events.size() != 1 ||
afterRestart.events[0].time != 2.0 || document.currentTime() != 0.0) {
std::cerr << "Restart did not return to the authored timeline\n";
return 1;
}
return 0;
}
A parser result, which may contain a result of type T, or an error, or both.
Definition ParseResult.h:17
bool hasError() const noexcept
Returns true if this ParseResult contains an error.
Definition ParseResult.h:110
T & result() &
Returns the contained result.
Definition ParseResult.h:51
ParseDiagnostic & error() &
Returns the contained error.
Definition ParseResult.h:81
Collects parse warnings during parsing.
Definition ParseWarningSink.h:29
Backend-agnostic renderer that resolves to the active build backend (tiny-skia by default,...
Definition Renderer.h:82
RendererBitmap takeSnapshot() const override
Captures a CPU-readable snapshot of the current frame.
void draw(SVGDocument &document) override
Draws the SVG document using the active backend.
Represents a parsed SVG document containing a tree of SVGElement nodes.
Definition SVGDocument.h:106
bool dispatchMouseEvent(EventType type, const SVGElement &target)
Deliver a mouse event to declarative SVG animation begin/end triggers.
std::optional< SVGElement > querySelector(std::string_view selector)
Find the first descendant of the root <svg> element, in document order, that matches the given CSS se...
double currentTime() const
Get the current document time in seconds.
void restartAnimations()
Start a fresh animation session at time zero, including animations with restart="never".
SVGAnimationEventBatch animationEvents(double from, double to)
Query animation lifecycle boundaries in the half-open interval (from, to].
void setTime(double seconds)
Set the current document time for animations, in seconds from the document start.
static ParseResult< SVGDocument > ParseSVG(std::string_view source, ParseWarningSink &warningSink, Options options={}, SVGDocument::Settings settings={}) noexcept
Parses an SVG XML document from a string (typically the contents of a .svg file).
std::vector< SVGAnimationEvent > events
Boundaries sorted by document time.
Definition SVGDocument.h:55
bool complete
False for invalid bounds or budget exhaustion.
Definition SVGDocument.h:56
@ Click
A click (mousedown + mouseup on same element).
Definition Event.h:19
Bounded lifecycle query result. An incomplete query returns no partial events.
Definition SVGDocument.h:54
int main()
Run the rendering example and report its result.
Definition render_test.cc:34
T y
The y component of the vector.
Definition Vector2.h:18
T x
The x component of the vector.
Definition Vector2.h:17
CPU-readable bitmap produced by a renderer snapshot.
Definition RendererInterface.h:67
Vector2i dimensions
Pixel dimensions of the bitmap in device pixels.
Definition RendererInterface.h:69
A declarative animation boundary observed over a time interval.
Definition SVGDocument.h:40
@ Begin
The animation's active interval began.
Definition SVGDocument.h:43
@ Repeat
The animation started a new repeat iteration.
Definition SVGDocument.h:44
@ End
The animation's active interval ended.
Definition SVGDocument.h:45