Tkinter - 如何以慢动作从 canvas 移动图像

Tkinter - How to move image from canvas in slow motion

伙计们。我正在尝试创建自己的纸牌游戏版本。我在尝试将我的卡片移动到 canvas 单击事件的中心时遇到以下问题。这是我的代码示例

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()

我想要的是移动我的卡片,而不是仅仅从一个坐标移动到另一个坐标,而是给它慢动作效果。

为了 'animate' 你的卡片移动,一个系统会分解要移动的总距离,然后 moving/updating 在一段时间内按较小的距离进行分解。

例如,如果您希望将卡片在 x 和 y 方向移动 400 个单位,则可以使用以下方法:

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

这可能是动画的基本前提。当然,您需要编辑我示例中的 total_timeperiod 变量来创建您认为正确的内容。

基本思想是编写一个函数,将对象移动少量,然后安排自身在短暂延迟后再次调用。它会这样做,直到到达目的地。

这是一个非常简单的示例,可以独立移动几个项目。您可以通过更改 speed 参数或更改 delta_xdelta_y 的值来调整速度。

这是一个非常简单的算法,只是将 x 和 y 坐标增加一个固定的量。您可以改为计算沿曲线或直线的等距点。无论如何,动画技术保持不变。

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()

下面的这段代码(为 copy/paste 和 运行 准备就绪)在我的盒子上提供了一个很好的平滑运动:

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()