JOrG
JOrG

Reputation: 49

Tkinter - How to move image from canvas in slow motion

guys. I am trying to create my own version of a card game. I got the following problem trying to move my cards to the center of the canvas on click event. Here is an example of my code

import tkinter as tk

class gui(tk.Frame):

def __init__(self, parent, *args, **kwargs):
    tk.Frame.__init__(self, parent, *args, **kwargs)
    self.canvas =  tk.Canvas(parent, bg="blue", highlightthickness=0)
    self.canvas.pack(fill="both", expand=True)
    self.img = PhotoImage(file="card.gif")
    self.card = self.canvas.create_image(10, 10, image=self.img)
    self.canvas.tag_bind(self.card, '<Button-1>', self.onObjectClick1)

def onObjectClick1(self, event):
    if self.canvas.find_withtag("current"):
        x = 400
        y = 400
        self.canvas.coords("current", x, y)
        self.canvas.tag_raise("current")

if __name__ == "__main__":
root = tk.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
gui(root)
root.mainloop()

What I want is to move my card but not just move from one coordinate to another but giving it in slow motion effect.

Upvotes: 1

Views: 1852

Answers (3)

user7711283
user7711283

Reputation:

This code below (ready for copy/paste and run as it is) gives a nice smooth motion on my box:

import tkinter as tk
import time

class gui(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.canvas =  tk.Canvas(parent, bg="blue", highlightthickness=0)
        self.canvas.pack(fill="both", expand=True)
        self.img = tk.PhotoImage(file="card.gif")
        self.card = self.canvas.create_image(10, 10, image=self.img)
        self.canvas.tag_bind(self.card, '<Button-1>', self.onObjectClick1)

    def onObjectClick1(self, event):
        if self.canvas.find_withtag("current"):
            x = 400
            y = 400
            self.canvas.coords("current", x, y)
            self.canvas.tag_raise("current")
            total_time = 500 #Time in milliseconds
        period = 400
        dx = 400/period
        dy = 400/period
        for i in range(period):
            self.canvas.move(self.card, dx, dy) # chosen_card
            time.sleep(0.01)
            # root.after(total_time/period) #Pause for time, creating animation effect
            root.update() #Update position of card on canvas

if __name__ == "__main__":
    root = tk.Tk()
    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    root.geometry("%dx%d+0+0" % (w, h))
    gui(root)
    root.mainloop()

Upvotes: 0

Bryan Oakley
Bryan Oakley

Reputation: 386210

The basic idea is to write a function that moves an object a small amount, and then schedules itself to be called again after a short delay. It does this until it reaches its destination.

Here is a very simple example that moves a couple items independently. You can adjust the speed by changing the speed parameter, or by changing the values of delta_x and delta_y.

This is a very simplistic algorithm that just increases the x and y coordinates by a fixed amount. You could instead calculate equally spaced points along a curve or straight line. Regardless, the animation technique remains the same.

import Tkinter as tk

def move_object(canvas, object_id, destination, speed=50):
    dest_x, dest_y = destination
    coords = canvas.coords(object_id)
    current_x = coords[0]
    current_y = coords[1]

    new_x, new_y = current_x, current_y
    delta_x = delta_y = 0
    if current_x < dest_x:
        delta_x = 1
    elif current_x > dest_x:
        delta_x = -1

    if current_y < dest_y:
        delta_y = 1
    elif current_y > dest_y:
        delta_y = -1

    if (delta_x, delta_y) != (0, 0):
        canvas.move(object_id, delta_x, delta_y)

    if (new_x, new_y) != (dest_x, dest_y):
        canvas.after(speed, move_object, canvas, object_id, destination, speed)

root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

item1 = canvas.create_rectangle(10, 10, 30, 30, fill="red")
item2 = canvas.create_rectangle(360, 10, 380, 30, fill="green")

move_object(canvas, item1, (200, 180), 25)
move_object(canvas, item2, (200, 220), 50)

root.mainloop()

Upvotes: 1

SneakyTurtle
SneakyTurtle

Reputation: 1857

In order to 'animate' your cards moving, a system of breaking down the total distance to be moved, and then moving/updating by smaller distances over a time-period would work.

For example, if you wish to move a card 400 units in x & y, something like this could work:

total_time = 500 #Time in milliseconds
period = 8
dx = 400/period
dy = 400/period

for i in range(period):
    self.canvas.move(chosen_card, dx, dy)
    root.after(total_time/period) #Pause for time, creating animation effect
    root.update() #Update position of card on canvas

This could be a basic premise for an animation. Of course you would need to edit the total_time and period variables in my example to create what you feel is right.

Upvotes: 1

Related Questions