Thank You

Here is a simple assembly of a Thank You image.

image0

How it works:

The script uses two images with characters, and one with the bubble:

image1

image2

image3

The main logic is to use a sine function to calculate smooth movements. For frame i the coordinate x would be:

\[x = sin(\frac{i}{i_{max}} \cdot (end - start)) + start\]

At the end, the image is cropped, so that the logo parts seem to move in from the outside.

Installation

The script requires the OpenCV library for displaying the live animation and imageio for exporting animated GIFs.

pip install opencv-python
pip install imageio

The code

import numpy as np
import cv2
import math
import imageio
import time

BLACK = (0, 0, 0)

FRAME1 = 80
FRAME2 = 20
FRAME3 = 10

MAXX = 2000
MAXY = 800
YCHARS = 300
XCENTER = 1000 - 341 // 2

def sine_move(start, end, frames, wait_frames=0):
    """
    smooth movement from a to b using a sine function.
    remain in end position forever
    """
    for _ in range(wait_frames):
        yield start
    for i in range(frames):
        angle_rad = i / frames * math.pi / 2
        if end > start:
            value = int(math.sin(angle_rad) * (end - start) + start)
        else:
            value = start - int(math.sin(angle_rad) * (start - end))
        yield value
    while True:
        yield value


def create_text():
    text = np.zeros((100, 300, 3), np.uint8) + 255
    return cv2.putText(
        text,
        "Thank You",
        (10, 50),
        cv2.FONT_HERSHEY_TRIPLEX,
        fontScale=1.0,
        color=BLACK,
        thickness=2,
    )


background = np.zeros((MAXY, MAXX, 3), np.uint8) + 255
panda = cv2.imread("panda.png")[::2, ::2]
pingu = cv2.imread("pingu.png")[::2, ::2]
bubble = cv2.imread("bubble.png")[::2, ::2]
ysize, xsize, _ = panda.shape
text = create_text()

frames = []
xgen = sine_move(0, XCENTER, FRAME1)
ygen = sine_move(0, 170, FRAME2, wait_frames=FRAME1)
fade = sine_move(start=255, end=0, frames=50, wait_frames=FRAME1 + FRAME2 + 5)

while True:
    # display frames
    frame = background.copy()
    xpanda = next(xgen)
    xpingu = MAXX - pingu.shape[1] - xpanda
    frame[YCHARS: YCHARS + ysize, xpanda : xpanda + xsize] = panda
    frame[YCHARS: YCHARS + ysize, xpingu : xpingu + xsize] &= pingu
    frame[YCHARS-150: YCHARS-150 + bubble.shape[0], XCENTER: XCENTER + bubble.shape[1]] =\
         np.maximum(bubble, next(fade))
    ytext = next(ygen)
    frame[ytext: ytext + 100, XCENTER + 40:XCENTER + 340] &= text
    frame = frame[100:550, 500:-500]  # crop
    cv2.imshow("frame", frame)

    key = chr(cv2.waitKey(1) & 0xFF)
    if key == "q":
        break

    time.sleep(0.03)

    # shrink frame for using it on the web
    rgb = cv2.cvtColor(frame[::2, ::2], cv2.COLOR_BGR2RGB)
    frames.append(rgb)

cv2.destroyAllWindows()
# imageio.mimsave('thank_you_animation.gif', frames, fps=25)