2

Can someone explain how to animate (using tikz animation, chapter 26 in pgfmanual 3.1.9a) two small colored dots (small filled circles) moving slowly in different directions (along position coordinates), continuously joined by a colored straight line, built with

\documentclass[dvisvgm]{standalone}
\usepackage{tikz}

and the SVG to be created with either latex example.tex or dvilualatex example.tex plus dvisvgm example --font-format=woff and either ...

  • a) repeating the movement continuously
  • b) or with a separate "click me" node to start the animation

Despite the 85 examples, I am as yet unable to put this example together. I am just learning animation.

Finally, would you recommend the BEAMER package for 'speed and flexibilty' (I believe I do not use the shapes modules)?

2 Answers 2

3

With some experimentation I came up with the following answer. Although in the final use case the coordinates are almost a straight line, here I use three coordinates that form a large angle (for demonstration purposes).

Case a) repeating continuously

In the code I added a 3 second delay before each animation starts:

\usetikzlibrary {animations}
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (6,4);

\node at (0.5,1) :position = { 0s = "{(0.5,1)}", 3s = "{(0.5,1)}", 5s = "{(1,2.5)}" , 8s = "{(2,2)}", repeat }
[fill = blue!20, draw = blue, ultra thick, circle] (node1) {};

\node at (2,2) :position = { 0s = "{(2,2)}", 3s = "{(2,2)}", 5s = "{(4,3)}", 8s = "{(5,3.5)}", repeat }
[fill = red!20, draw = red, ultra thick, circle] (node2) {};

\draw [green] (node1) -- (node2) :path = { 0s = "{(node1) -- (node2)}", 3s = "{(node1) -- (node2)}",
5s = "{(1,2.5) -- (4,3)}", 8s = "{(2,2) -- (5,3.5)}", repeat };
\end{tikzpicture}

Here is the SVG (I hope this works):

Criticism: I do not like that the connecting line grows in length up to the center of both circles - I need to subtract the radii of the two ending circles.

Case b) with a start button

\usetikzlibrary {animations}
\begin{tikzpicture}
\draw (5,5) node[minimum height =0cm,draw,scale=1.5] (Go) {Go!};

\node at (0.5,1) :position = { 0s = "{(0.5,1)}", 2s = "{(1,2.5)}" , 5s = "{(2,2)}", begin on={click, of=Go} }
[fill = blue!20, draw = blue, ultra thick, circle] (node1) {};

\node at (2,2) :position = { 0s = "{(2,2)}", 2s = "{(4,3)}", 5s = "{(5,3.5)}", begin on={click, of=Go} }
[fill = red!20, draw = red, ultra thick, circle] (node2) {};

\draw [green] (node1) -- (node2) :path = { 0s = "{(node1) -- (node2)}", 2s = "{(1,2.5) -- (4,3)}",
5s = "{(2,2) -- (5,3.5)}", begin on={click, of=Go} };

\end{tikzpicture}

Here is the SVG (sorry, the button works in the created SVG though):

Criticism: if the node1 and node2 coordinates are extracted into a \begin{scope} [animate = { .... }] .... \end{scope} section, within the :position list of arguments begin on={click, of=Go} is ignored, i.e. the animation never starts.

Case a2) repeating continuously

The gradually extending line that connects the nodes (hard to see in the examples above) can be removed using phantom nodes. See: Phantom nodes for TikZ animation

\usetikzlibrary {animations}
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (6,4);

\node at (0.5,1) :position = { 0s = "{(0.5,1)}", 3s = "{(0.5,1)}", 5s = "{(1,2.5)}" , 8s = "{(2.5,1.5)}", 10s = "{(2.5,1.5)}", repeat }
[fill = blue!05, draw = blue, thick, circle, radius = 0.6] (node1) {};

\node at (2,2) :position = { 0s = "{(2,2)}", 3s = "{(2,2)}", 5s = "{(4,3)}", 8s = "{(5,0.5)}", 10s = "{(5,0.5)}", repeat }
[fill = red!05, draw = red, thick, circle, radius = 0.6] (node2) {};

\begin{lrbox}{0}
\node at (1,2.5) [draw, circle, radius = 0.6, opacity = 0.2] (n1) {};
\node at (2.5,1.5) [draw, circle, radius = 0.6, opacity = 0.2] (n2) {};
\node at (4,3) [draw, circle, radius = 0.6, opacity = 0.2] (n3) {};
\node at (5,0.5) [draw, circle, radius = 0.6, opacity = 0.2] (n4) {};
\end{lrbox}

\draw [brown] (node1) -- (node2) :path = { 0s = "{(node1) -- (node2)}", 3s = "{(node1) -- (node2)}", 5s = "{(n1) -- (n3)}", 8s = "{(n2) -- (n4)}", 10s = "{(n2) -- (n4)}", repeat };

\end{tikzpicture}

The SVG is here:

If you comment out the beginning and end of the {lrbox}, you see faint images of where the phantom nodes are placed:

This is a good solution, but cumbersome to code. I've since seen (on page 382 of the TikZ manual:

enter image description here

So, all in all, I'm satisfied with this solution, which will use Python to create the .tex file anyway. If anyone can improve on it, I'd be pleased to see your version.

4
  • 1
    Please always post complete compilable code, so that others can benefit from your great work in the future by direct copy paste. Commented Dec 11, 2021 at 10:38
  • Rather than worry about plotting the line between the edges of the nodes you can cheat and draw the nodes second so that the very ends of the lines are underneath
    – Willoughby
    Commented Dec 11, 2021 at 15:52
  • @hpekristiansen I was using the standard example style in "pgfmanual 3.1.9a". Okay, thanks, I'll post complete compilable code as well.
    – Aendie
    Commented Dec 12, 2021 at 13:48
  • @Willoughby That's exactly what I do when creating a PDF chart (an example I posted at the end here: tex.stackexchange.com/questions/625165/…). Apparently it's not possible with \usetikzlibrary {animations}.
    – Aendie
    Commented Dec 12, 2021 at 13:55
1

ADDENDUM to my previous answer

Apologies! To avoid clutter I am posting complete compilable code (as requested) here. Unlike GitHub, I am unable to upload a ZIP file.

This code example shows two animations side by side: the one on the left using phantom nodes, i.e. the "correct" example; the one on the right is the "incorrect" example I had initially (without phantom nodes) where the connecting line slowly grows from touching the circles to "circle mid-point to circle mid-point".

Why this happens is rather obvious: I begin by using the initial "rest position" nodes, which have a defined boundary. Lacking nodes in future animation-time, I switch to coordinates, i.e. the center of the circles. The following example shows this much more clearly than the original "Case a" I posted above.

Python code (tested in Windows 10 with Python 3.9.4 and MiKTeX 21.12):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Standard library imports
import os, sys, time

# ---------- DOCUMENT INITIALIZATION ----------
def beginSVG():
    tex = r"""\documentclass[dvisvgm]{standalone}
\usepackage{tikz}
\definecolor{auburn}{rgb}{0.43, 0.21, 0.1}
\begin{document}
"""
    return tex

# ---------- DOCUMENT FINALIZATION ----------
def endSVG():
    tex = r"""\end{document}
"""
    return tex

############ MAIN ############
if __name__ == '__main__':      # execute if run explicitly

    if sys.version_info[0] < 3:
        raise Exception("This runs with Python 3")

    args = "" if "-v" in set(sys.argv[1:]) else "-interaction=batchmode -halt-on-error "
    keeplog = True if "-log" in set(sys.argv[1:]) else False
    keeptex = True if "-tex" in set(sys.argv[1:]) else False
    modelua = True if "-lua" in set(sys.argv[1:]) else False

    filename = "example"
    texname = filename + ".tex"
    dviname = filename + ".dvi"
    svgname = filename + ".svg"

    if os.path.exists(svgname):
        try:
            os.remove(svgname)
        except PermissionError:
            print("\nERROR: please close '{}' so it can be re-created".format(svgname))
            sys.exit(0)
    if os.path.exists(texname):
        os.remove(texname)

    outfile = open(texname, mode="w", encoding="utf8")
    outfile.write(beginSVG())

    # TEST CASE: runs continuously
    tex = r"""\usetikzlibrary {animations}
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (10,4);

\node at (0.5,1) :position = { 0s = "{(0.5,1)}", 3s = "{(0.5,1)}", 5s = "{(1,2.5)}" , 8s = "{(2.5,1.5)}", 10s = "{(2.5,1.5)}", repeat }
[fill = blue!05, draw = blue, thick, circle, radius = 0.6] (node1) {};

\node at (2,2) :position = { 0s = "{(2,2)}", 3s = "{(2,2)}", 5s = "{(4,3)}", 8s = "{(5,0.5)}", 10s = "{(5,0.5)}", repeat }
[fill = red!05, draw = red, thick, circle, radius = 0.6] (node2) {};

\begin{lrbox}{0}
\node at (1,2.5) [draw, circle, radius = 0.6, opacity = 0.2] (n1) {};
\node at (2.5,1.5) [draw, circle, radius = 0.6, opacity = 0.2] (n2) {};
\node at (4,3) [draw, circle, radius = 0.6, opacity = 0.2] (n3) {};
\node at (5,0.5) [draw, circle, radius = 0.6, opacity = 0.2] (n4) {};
\end{lrbox}

\draw [auburn] (node1) -- (node2) :path = { 0s = "{(node1) -- (node2)}", 3s = "{(node1) -- (node2)}", 5s = "{(n1) -- (n3)}", 8s = "{(n2) -- (n4)}", 10s = "{(n2) -- (n4)}", repeat };

% second example on the right demonstrates the expanding line when no phantom nodes are used...

\node at (4.5,1) :position = { 0s = "{(4.5,1)}", 3s = "{(4.5,1)}", 5s = "{(5,2.5)}" , 8s = "{(6.5,1.5)}", 10s = "{(6.5,1.5)}", repeat }
[fill = blue!05, draw = blue, thick, circle, radius = 0.6] (node3) {};

\node at (6,2) :position = { 0s = "{(6,2)}", 3s = "{(6,2)}", 5s = "{(8,3)}", 8s = "{(9,0.5)}", 10s = "{(9,0.5)}", repeat }
[fill = red!05, draw = red, thick, circle, radius = 0.6] (node4) {};

\draw [auburn] (node3) -- (node4) :path = { 0s = "{(node3) -- (node4)}", 3s = "{(node3) -- (node4)}", 5s = "{(5,2.5) -- (8,3)}", 8s = "{(6.5,1.5) -- (9,0.5)}", 10s = "{(6.5,1.5) -- (9,0.5)}", repeat };

\end{tikzpicture}
"""

    outfile.write(tex)
    outfile.write(endSVG())

    outfile.close()
    if os.path.exists(svgname):
        os.remove(svgname)

# ----------- TEX -> DVI

    msg = " creating {}".format(dviname)
    if modelua:
        prg = "lualatex"
        command = 'lualatex --output-format=dvi {}'.format(args + texname)
    else:
        prg = "latex"
        command = 'latex {}'.format(args + texname)

    print()     # blank line before "This is pdfTeX, ..."
    os.system(command)
    print("'{}' finished".format(prg) + msg)

# ----------- DVI -> SVG

    time.sleep(1)   # unnecessary
    msg = " creating {}".format(svgname)
    command = 'dvisvgm {}'.format(filename)

    if args == "":
        os.system(command)
        print("finished" + msg)
    else:
        returned_value = os.system(command)
        if returned_value != 0:
            if msg != "":
                print("ERROR detected while creating SVG file")
            else:
                print("!!   ERROR detected while creating SVG file   !!")
                print("!! Append '-v' or '-log' for more information !!")
        else:
            print("finished" + msg)

    if not keeptex: os.remove(texname)
    if not keeplog:
        if os.path.isfile(filename + ".log"):
            os.remove(filename + ".log")
    if os.path.isfile(filename + ".aux"):
        os.remove(filename + ".aux")

One can use the following command line parameters:

  • -v verbose console output
  • -log preserve the log file
  • -tex preserve the tex file
  • -lua use Luatex instead of Latex to create the DVI file

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .