Skip to main content
Engineering LibreTexts

6.16: Animating the Button Flash

  • Page ID
    14546
    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

        origSurf = DISPLAYSURF.copy()
        flashSurf = pygame.Surface((BUTTONSIZE, BUTTONSIZE))
        flashSurf = flashSurf.convert_alpha()
        r, g, b = flashColor
        sound.play()
        for start, end, step in ((0, 255, 1), (255, 0, -1)): # animation loop
            for alpha in range(start, end, animationSpeed * step):
                checkForQuit()
                DISPLAYSURF.blit(origSurf, (0, 0))
                flashSurf.fill((r, g, b, alpha))
                DISPLAYSURF.blit(flashSurf, rectangle.topleft)
                pygame.display.update()
                FPSCLOCK.tick(FPS)
        DISPLAYSURF.blit(origSurf, (0, 0))
    

    The process of animating the button flash is simple: On each frame of the animation, the normal board is drawn and then on top of that, the bright color version of the button that is flashing is drawn over the button. The alpha value of the bright color starts off at 0 for the first frame of animation, but then on each frame after the alpha value is slowly increased until it is fully opaque and the bright color version completely paints over the normal button color. This will make it look like the button is slowly brightening up.

    The brightening up is the first half of the animation. The second half is the button dimming. This is done with the same code, except that instead of the alpha value increasing for each frame, it will be decreasing. As the alpha value gets lower and lower, the bright color painted on top will become more and more invisible, until only the original board with the dull colors is visible.

    To do this in code, line 1 [168] creates a copy of the display Surface object and stores it in origSurf. Line 2 [169] creates a new Surface object the size of a single button and stores it in flashSurf. The convert_alpha() method is called on flashSurf so that the Surface object can have transparent colors drawn on it (otherwise, the alpha value in the Color objects we use will be ignored and automatically assumed to be 255). In your own game programs, if you are having trouble getting color transparency to work, make sure that you have called the convert_alpha() method on any Surface objects that have transparent colors painted on them.

    Line 4 [171] creates individual local variables named r, g, and b to store the individual RGB values of the tuple stored in flashColor. This is just some syntactic sugar that makes the rest of the code in this function easier to read. Before we begin animating the button flash, line 5 [172] will play the sound effect for that button. The program execution keeps going after the sound effect has started to play, so the sound will be playing during the button flash animation.

    Remember that to do the animation, we want to first draw the flashSurf with color that has increasing alpha values from 0 to 255 to do the brightening part of the animation. Then to do the dimming, we want the alpha value to go from 255 to 0. We could do that with code like this:

    for alpha in range(0, 255, animationSpeed): # brightening
        checkForQuit()
        DISPLAYSURF.blit(origSurf, (0, 0))
        flashSurf.fill((r, g, b, alpha))
        DISPLAYSURF.blit(flashSurf, rectangle.topleft)
        pygame.display.update()
        FPSCLOCK.tick(FPS)
    for alpha in range(255, 0, -animationSpeed): # dimming
        checkForQuit()
        DISPLAYSURF.blit(origSurf, (0, 0))
        flashSurf.fill((r, g, b, alpha))
        DISPLAYSURF.blit(flashSurf, rectangle.topleft)
        pygame.display.update()
        FPSCLOCK.tick(FPS)
    

    But notice that the code inside the for loops handles drawing the frame and are identical to each other. If we wrote the code like the above, then the first for loop would handle the brightening part of the animation (where the alpha value goes from 0 to 255) and the second for loop would handle the dimming part of the animation (where the alpha values goes from 255 to 0). Note that for the second for loop, the third argument to the range() call is a negative number.

    Whenever we have identical code like this, we can probably shorten our code so we don’t have to repeat it. This is what we do with the for loop on line 6 [173], which supplies different values for the range() call on line 7 [174].

    On the first iteration of line 6's [173] for loop, start is set to 0, end is set to 255, and step is set to 1. This way, when the for loop on line 7 [174] is executed, it is calling range(0, 255, animationSpeed). (Note that animationSpeed * 1 is the same as animationSpeed. Multiplying a number by 1 gives us the same number.)

    Line 7's [174] for loop then executes and performs the brightening animation.

    On the second iteration of line 6's [173] for loop (there are always two and only two iterations of this inner for loop), start is set to 255, end is set to 0, and step is set to -1. When the line 7's [174] for loop is executed, it is calling range(255, 0, -animationSpeed). (Note that animationSpeed * -1 evaluates to -animationSpeed, since multiplying any number by -1 returns the negative form of that same number.)

    This way, we don’t have to have two separate for loops and repeat all the code that is inside of them. Here’s the code again that is inside line 7's [174] for loop:

                checkForQuit()
                DISPLAYSURF.blit(origSurf, (0, 0))
                flashSurf.fill((r, g, b, alpha))
                DISPLAYSURF.blit(flashSurf, rectangle.topleft)
                pygame.display.update()
                FPSCLOCK.tick(FPS)
        DISPLAYSURF.blit(origSurf, (0, 0))
    

    We check for any QUIT events (in case the user tried to close the program during the animation), then blit the origSurf Surface to the display Surface. Then we paint the flashSurf Surface by calling fill() (supplying the r, g, b values of the color we got on line 4 [171] and the alpha value that the for loop sets in the alpha variable). Then the flashSurf Surface is blitted to the display Surface.

    Then, to make the display Surface appear on the screen, pygame.display.update() is called on line 12 [179]. To make sure the animation doesn’t play as fast as the computer can draw it, we add short pauses with a call to the tick() method. (If you want to see the flashing animation play very slowly, put a low number like 1 or 2 as the argument to tick() instead of FPS.)


    This page titled 6.16: Animating the Button Flash is shared under a CC BY-NC-SA 3.0 license and was authored, remixed, and/or curated by Al Sweigart via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.

    • Was this article helpful?