I’d love to generate a small video by creating an interpolation between two tikz figures, for instance to animate between these frames:
I recently discovered the nice animate package that allows the generation of quite advanced animation, but I can’t find how to animate nodes that are not positioned directly with coordinate, but using for instance fit,right=of, name intersection etc…
Ideally, I’d love a solution where I just provide 2 tikz pictures, letting a library to automatically calculate the interpolation, for instance based on the name of the element… If this does not exists, I’d be interested by ideas on how to implement it myself. For instance, how could I hook into the fit/right=of/name intersection… to compute the width and position of a node? (a general protocol more or less independent of the positioning strategy would be great!)
What I don’t want:
- a fully manual solution where I need to manually compute the absolute position of the node.
fit,right=of …,name intersection=…are great, and I’d like to use them. Ideally, I’d prefer to avoid defining intermediate nodes (e.g. a phantom node to precompute the position of the node…) but if I’ve no choice I might live with it.
What is fine:
- I don’t mind using intermediately another language, or even a non-tikz based approach, if the other language’s expressiveness is good enough (labels, …)
In my dream:
- this would also work to morph node shapes (e.g. to turn a square into a circle) etc… but it will certainly stay a dream.
\documentclass[aspectratio=169]{beamer}
% \usepackage[]{animate}
\usepackage{tikz}
\usetikzlibrary{positioning,fit}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\begin{document}
\begin{frame}
I would like a smooth interpolation (just as a list of pdf pages) between these images:\
\only<1>{
\begin{tikzpicture}
\node(abc) at (0,0) {ABC};
\node(def) at (1cm,.5cm){DEF};
\node(ghi) at (-1.5cm,.5cm){GHI};
\node[fill=green] at (0cm,-2cm){I should disappear, ideally smoothly via opacity.};
\begin{pgfonlayer}{background}
\node[fit=(abc)(ghi),fill=red,label={Hey},rounded corners] {};
\end{pgfonlayer}
\end{tikzpicture}}%
\only<2>{
\begin{tikzpicture}
\node(abc) at (0,0) {ABC};
\node(def) at (1cm,.5cm){DEF};
\node(ghi) at (-1.5cm,.5cm){GHI};
\node[fill=green,opacity=0] at (0cm,-2cm){I should disappear, ideally smoothly via opacity.};
\begin{pgfonlayer}{background}
\node[fit=(def)(abc),fill=red,label={Hey},rounded corners] {};
\end{pgfonlayer}
\end{tikzpicture}}%
\only<3>{
\begin{tikzpicture}
\node(abc) at (0,0) {ABC};
\node(def) at (1cm,.5cm){DEF};
\node(ghi) at (-1.5cm,.5cm){GHI};
\node[fill=green,opacity=0] at (0cm,-2cm){I should disappear, ideally smoothly via opacity.};
\begin{pgfonlayer}{background}
\node[right=of abc,minimum width=3cm,fill=red,label={Hey},rounded corners] {};
\end{pgfonlayer}
\end{tikzpicture}}
\end{frame}
\end{document}
% Local Variables:
% TeX-command-extra-options: "-shell-escape -halt-on-error"
% End:
-- EDIT --
I ended up coding my own custom code, this way I can type:
\begin{frame}
\begin{createNewLoop}{nb frames=25}
\only<\blenderpointCurrentframePlusOne>{
\begin{tikzpicture}
\node(abc) at (0,0) {ABC};
\node(def) at (2cm,.5cm){DEF};
\node(ghi) at (-1.5cm,.5cm){GHI};
\myAnimatedNode[fill=green,at={(0cm,-2cm)},opacity=1-\blenderpointAnimateFraction](smoothly){I should disappear, ideally smoothly via opacity.};
\begin{pgfonlayer}{background}
\myAnimatedNode[fill=red!\blenderpointAnimatePcInvert!purple,rounded corners][fit=(abc)(ghi)][fit=(def)(abc)][label={Hey},](mynode){};
\end{pgfonlayer}
\end{tikzpicture}%
}
\end{createNewLoop}
\end{frame}
to generate:
the idea is to draw automatically 2 phantom nodes (opacity=0), and add a third node between these two nodes. Since it’s annoying to create these two things, I created a new \node-like function that automatically create the 2 phantom nodes, and that accept the position of the first node and of the final node as an arbitrary style. It might be possible to avoid that by creating a style that automatically draws the nodes before the main one… but it’s a first try.
Advantage:
- the phantom nodes are taken into account when computing the bounding box, so the image does not look jump
- it is quite agnostic to how the nodes are placed
I still need to check how resilient it is to rotations, other node shapes, etc… and I’m curious to hear if other solution exists. So for now I will let this question open.
Full code:
\documentclass[aspectratio=169]{beamer}
% \usepackage[]{animate}
\usepackage{tikz}
\usetikzlibrary{positioning,fit,calc,math}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\begin{document}
% \myAnimatedNode[common code][code phantom 1 like fit=()][code phantom 2 like right=of ...][code in between, by default](myName){content}
\NewDocumentCommand{\myAnimatedNode}{O{}O{}O{}O{}R(){}m}{%
\message{TIKZTIKZ (XXXXXXXXXXXXXXXX \blenderpointAnimateFraction)}
% start hidden node
\node#1,#2,opacity=0{#6};%
% First hidden node
\node#1,#3,opacity=0{#6};%
% Last node to be drawn
\path let \p1=($(#5-first-hidden.west)-(#5-first-hidden.east)$),
\p2=($(#5-second-hidden.west)-(#5-second-hidden.east)$),
\n1 = {veclen(\p1)-\pgflinewidth},
\n2 = {veclen(\p2)-\pgflinewidth},
\n3 = {(1-\blenderpointAnimateFraction)\n1 + \blenderpointAnimateFraction\n2},
\p4=($(#5-first-hidden.south)-(#5-first-hidden.north)$),
\p5=($(#5-second-hidden.south)-(#5-second-hidden.north)$),
\n4 = {veclen(\p4)-\pgflinewidth},
\n5 = {veclen(\p5)-\pgflinewidth},
\n6 = {(1-\blenderpointAnimateFraction)\n4 + \blenderpointAnimateFraction\n5)} in
node#1,at={($(#5-first-hidden.center)!\blenderpointAnimateFraction!(#5-second-hidden.center)$)}, minimum width=\n3, minimum height=\n6,#4{#6};%
}
% Instead of writing a loop, we use a (more flexible) recursive function that might be practical later to,
% for instance, quit a loop before the end
\NewDocumentCommand{\createNewLoopAux}{mO{}m}{%
\pgfmathparse{int(\blenderpointCurrentframe < \blenderpointNbFrames)}%
\ifnum\pgfmathresult=1
\pgfmathparse{\blenderpointCurrentframe/(\blenderpointNbFrames-1)}%
\let\blenderpointAnimateFraction\pgfmathresult%
% It might be easier to specify stuff in % (0--100) like colors
\pgfmathparse{\blenderpointAnimateFraction*100}%
\let\blenderpointAnimatePc\pgfmathresult%
% It might be easier to invert it 100-…
\pgfmathparse{100-\blenderpointAnimatePc}%
\let\blenderpointAnimatePcInvert\pgfmathresult%
#3%
\pgfmathparse{int(\blenderpointCurrentframe + 1)}%
\let\blenderpointCurrentframe\pgfmathresult%
\pgfmathparse{int(\blenderpointCurrentframePlusOne + 1)}%
\let\blenderpointCurrentframePlusOne\pgfmathresult%
\createNewLoopAux{#1}[#2]{#3}%
\fi
}
\NewDocumentEnvironment{createNewLoop}{mO{}+b}{%
\pgfkeys{
/blenderpointAnimate/.cd,
% Make sure to include at least 2 frames
nb frames/.store in=\blenderpointNbFrames,
% Internals
current frame/.store in=\blenderpointCurrentframe,
current frame=0,
current frame plus 1/.store in=\blenderpointCurrentframePlusOne,
current frame plus 1=1,
#1
}%
\createNewLoopAux{#1}[#2]{#3}
}{}
\begin{frame}
\begin{createNewLoop}{nb frames=25}
\only<\blenderpointCurrentframePlusOne>{
\begin{tikzpicture}
\node(abc) at (0,0) {ABC};
\node(def) at (3cm,.5cm){DEF};
\node(ghi) at (-1.5cm,.5cm){GHI};
\myAnimatedNodefill=green,at={(0cm,-2cm)},opacity=1-\blenderpointAnimateFraction{I should disappear, ideally smoothly via opacity.};
\begin{pgfonlayer}{background}
\myAnimatedNode[fill=red!\blenderpointAnimatePcInvert!purple,rounded corners][fit=(abc)(ghi)][fit=(def)(abc)][label={Hey},](mynode){};
\end{pgfonlayer}
\end{tikzpicture}%
}
\end{createNewLoop}
\end{frame}
\end{document}



right offor eg, you may want to learn about thebasic layeroftikzandpgfhttps://tikz.dev/base-design – anis May 02 '23 at 16:13tikz-ducksin github https://github.com/samcarter/tikzducks – anis May 02 '23 at 16:14\tikzset{declare function = {blInterpolate(\x,\y) = (1-\blenderpointAnimateFraction)*\x+\blenderpointAnimateFraction*\y;}}I can even change parameters using for instancexshift/.evaluated={blInterpolate(0,-2.2cm)}. I still want some more advanced features, notably to chain more than one element, but it’s good enough for quick simple animations. – tobiasBora May 02 '23 at 20:11