[Python] FuncAnimation: Fade out at the end of animation affecting fade in animations at the...

Discussão em 'Python' iniciado por Stack, Julho 3, 2025.

  1. Stack

    Stack Membro Participativo

    In an attempt to fade latex text in and out of the screen using FuncAnimation, I have written the code below. In particular, I have written two methods fade_in and fade_out that fade an equation in and out respectively. When tested individually these methods work as expected, yet when tested together (see the bottom of the code below) an issue arises. If, after creating the objects x and y displaying a LaTeX x and y respectively, I

    1. Apply fade_in on x;
    2. Apply fade_in on y;
    3. Apply fade_out on y;

    then, during the first third of the animation, the symbol y is displayed on the screen. In contrast, I'd prefer if y is invisible during the first third of the animation, only to start appearing during its fade_in animation. Funny enough, If I merely apply steps (1) and (2), then y is indeed invisible during the first third of the animation.

    import matplotlib.pyplot as plt
    from matplotlib.font_manager import FontProperties
    from matplotlib.animation import FuncAnimation, FFMpegWriter

    def chain_animations(animations):
    # Extract all update functions and frame counts
    update_funcs = [a._func for a in animations]
    frame_counts = [a.frame_seq.__length_hint__() for a in animations]
    intervals = [a._interval for a in animations]
    figs = [a._fig for a in animations]

    assert all(interval == intervals[0] for interval in intervals), "All animations must have the same interval"
    assert all(fig is figs[0] for fig in figs), "All animations must use the same figure"

    total_frames = sum(frame_counts)
    cumulative = [0]
    for count in frame_counts:
    cumulative.append(cumulative[-1] + count)

    def update(frame):
    for i in range(len(frame_counts)):
    if cumulative <= frame < cumulative[i + 1]:
    local_frame = frame - cumulative
    return update_funcs(local_frame)
    return []

    return FuncAnimation(figs[0], update, frames=total_frames, interval=intervals[0], blit=True)


    class Symbol:
    def __init__(self, ax, letter, size=20, color='black', position=(0, 0), alpha=1.0, zorder=1, row = -1, col = -1):
    self.ax = ax
    self.letter = letter
    self.size = size
    self.color = color
    self.position = position
    self.alpha = alpha
    self.zorder = zorder
    self.font = FontProperties(family='cmr10')
    self.row = row
    self.col = col

    self.add_to_axes(ax)

    def add_to_axes(self, ax):
    self.ax = ax
    # Manually reduce font size slightly
    fontsize = self.size # scale down for visual match
    x, y = self.position
    self.patch = ax.text(
    x, y,
    self.letter,
    fontsize=fontsize,
    color=self.color,
    alpha=self.alpha,
    zorder=self.zorder,
    ha='left',
    va='baseline',
    math_fontfamily='cm',
    usetex=False
    )

    def update_position(self, new_pos):
    self.position = new_pos

    def fade_in(self, ax, time=1.0, fps=30):
    total_frames = max(1, int(time * fps)) # avoid divide by zero
    target_alpha = 1.0
    self.alpha = 0.0
    self.patch.set_alpha(0.0)

    def animate(frame):
    t = frame / (total_frames - 1) if total_frames > 1 else 1.0
    current_alpha = t * target_alpha
    self.alpha = current_alpha
    self.patch.set_alpha(current_alpha)
    return [self.patch]

    return FuncAnimation(ax.figure, animate, frames=total_frames, interval=1000/fps, blit=True)

    def fade_out(self, ax, time=1.0, fps=30):
    total_frames = max(1, int(time * fps)) # avoid divide by zero
    target_alpha = 1.0
    self.alpha = 1.0

    if self.patch is not None:
    self.patch.set_alpha(1.0)

    def animate(frame):
    t = frame / (total_frames - 1) if total_frames > 1 else 1.0
    current_alpha = (1 - t) * target_alpha
    self.alpha = current_alpha
    self.patch.set_alpha(current_alpha)
    return [self.patch]

    return FuncAnimation(ax.figure, animate, frames=total_frames, interval=1000/fps, blit=True)








    import matplotlib.pyplot as plt
    from Symbol import Symbol

    fig, ax = plt.subplots()
    ax.set_xlim(0, 200)
    ax.set_ylim(0, 200)

    # Create a Symbol instance
    x = Symbol(ax, letter='$x$', size=30, color='blue', position=(50, 50))
    y = Symbol(ax, letter='$y$', size=30, color='red', position=(55, 50))

    # Doesn't work: x fade in, y fade in, y fade out
    # Works: x fade in, y fade in, x fade out
    anim1 = x.fade_in(ax, time=1.0, fps=30)
    anim2 = y.fade_in(ax, time=1.0, fps=30)
    anim3 = y.fade_out(ax, time=1.0, fps=30)
    anim = chain_animations([anim1, anim2, anim3])
    anim.save("fade.mp4", writer=FFMpegWriter(fps=30))

    Continue reading...

Compartilhe esta Página