所以我只是在胡乱尝试做一个简单的滚动条。基本上,有一些按钮和一个滚动条。我所做的就是,如果滚动条向下移动,则将按钮向上移动;如果滚动条向上移动,则将按钮向下移动。这是代码。
import pygame import time pygame.init() window = pygame.display.set_mode((1200, 600)) font = pygame.font.Font(pygame.font.get_default_font(), 30) ys = [] ##stores y-positions for the buttons for y in range(25): ys.append((y * 101) + 100) bar = pygame.Rect(550, 100, 10, (1 / len(ys) if len(ys) > 0 else 1) * 500) scrollStart = False updateButtons_Y = False while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() mousePressed = pygame.mouse.get_pressed()[0] mousePos = pygame.mouse.get_pos() window.fill((255, 255, 255)) if (bar.y < 100): bar.y = 100 if (bar.y + bar.height) > 500: bar.y = 500 - bar.height pygame.draw.rect(window, (0, 0, 0), bar) if mousePressed and bar.collidepoint(mousePos) and not scrollStart: scrollStart = True scrollPosY = mousePos[1] if scrollStart: pygame.draw.rect(window, (255, 0, 0), bar) bar.y += mousePos[1] - scrollPosY if bar.y > 100 and bar.y + bar.height < 500: for i in range(len(ys)): ys[i] -= (mousePos[1] - scrollPosY) * len(ys) * 0.3 scrollPosY = mousePos[1] if not mousePressed and scrollStart: scrollStart = False for i, y in enumerate(ys): pygame.draw.rect(window, (0, 0, 0), (10, y, 500, 100), 3) surf = font.render(f'{i}', True, (0, 0, 0)) window.blit(surf, (100, y + 40)) pygame.display.flip()
我遇到的问题是,在上下滚动时,它们会逐渐错位。例如,这是它的开始方式。请注意左侧的栏和按钮如何在同一级别上开始。
以下是上下滚动几次后的结果。
我认为这就是问题所在:
if scrollStart: pygame.draw.rect(window, (255, 0, 0), bar) bar.y += mousePos[1] - scrollPosY if bar.y > 100 and bar.y + bar.height < 500: for i in range(len(ys)): ys[i] -= (mousePos[1] - scrollPosY) * len(ys) * 0.3 scrollPosY = mousePos[1]
mousePos[1] - scrollPosY根据鼠标移动的速度而变化,因此每帧的变化量不一致。为了解决这个问题,我将其替换为
mousePos[1] - scrollPosY
if scrollStart: pygame.draw.rect(window, (255, 0, 0), bar) if mousePos[1] - scrollPosY != 0: speed = 1 direction = math.copysign(speed, mousePos[1] - scrollPosY) bar.y += direction if bar.y > 100 and bar.y + bar.height < 500: for i in range(len(ys)): ys[i] -= direction scrollPosY = mousePos[1]
这解决了该问题,但现在移动感觉不自然(滚动条移动得太慢或按钮移动得太快)。
所以我的问题是:有没有办法让它们保持一致,同时以不同的速度移动?
你的问题确实出在滚动时位置计算的精确度上,特别是在将鼠标位置的变化量直接应用到按钮 ys 列表中的偏移值时。这种方法可能会由于累积误差和非线性缩放而导致错位。
ys
为了实现一致且自然的滚动效果,同时支持不同速度的滚动条和内容移动,你可以将滚动条的位置和按钮位置的映射分开,并通过一种比例关系来连接它们。
核心思想是基于滚动条的位置百分比来计算按钮的整体偏移量。这样,滚动条和按钮的移动保持一致且无累积误差。
以下是改进后的代码,其中滚动条和按钮之间有一个固定的比例关系:
import pygame pygame.init() window = pygame.display.set_mode((1200, 600)) font = pygame.font.Font(pygame.font.get_default_font(), 30) # Initialize button positions ys = [(y * 101) + 100 for y in range(25)] # Scrollbar properties bar = pygame.Rect(550, 100, 10, max(500 // len(ys), 20)) scroll_area_top = 100 scroll_area_bottom = 500 scrollStart = False # Total scrollable height (extra space occupied by all buttons) content_height = len(ys) * 101 scroll_ratio = (content_height - (scroll_area_bottom - scroll_area_top)) / (scroll_area_bottom - scroll_area_top) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() mousePressed = pygame.mouse.get_pressed()[0] mousePos = pygame.mouse.get_pos() window.fill((255, 255, 255)) # Clamp scrollbar position bar.y = max(scroll_area_top, min(bar.y, scroll_area_bottom - bar.height)) pygame.draw.rect(window, (0, 0, 0), bar) # Handle mouse drag if mousePressed and bar.collidepoint(mousePos) and not scrollStart: scrollStart = True scrollPosY = mousePos[1] if scrollStart: pygame.draw.rect(window, (255, 0, 0), bar) delta = mousePos[1] - scrollPosY bar.y += delta scrollPosY = mousePos[1] if not mousePressed and scrollStart: scrollStart = False # Calculate button offset based on scrollbar position scroll_percent = (bar.y - scroll_area_top) / (scroll_area_bottom - scroll_area_top - bar.height) offset = -scroll_percent * (content_height - (scroll_area_bottom - scroll_area_top)) # Draw buttons for i, y in enumerate(ys): button_y = y + offset if scroll_area_top - 100 < button_y < scroll_area_bottom: # Only draw visible buttons pygame.draw.rect(window, (0, 0, 0), (10, button_y, 500, 100), 3) surf = font.render(f'{i}', True, (0, 0, 0)) window.blit(surf, (100, button_y + 40)) pygame.display.flip()
scroll_percent
滚动条移动的 y 值被线性映射到按钮的偏移量范围。
y
偏移计算与按钮位置更新分离
这样,不直接修改 ys 列表,避免了累积误差。
可见性优化
只绘制当前在滚动区域内的按钮,提高性能。
滚动条最小高度
一致性 滚动条和按钮移动的速度通过比例关系保持一致,无需手动调整滚动速度。
无累积误差 每次绘制时都重新计算按钮位置,避免误差积累。
自然的滚动体验 滚动条移动时,按钮会以固定比例偏移,滚动行为平滑且易于控制。
鼠标滚轮支持 可以捕获 pygame.MOUSEWHEEL 事件,让用户通过滚轮进行滚动。
pygame.MOUSEWHEEL
更灵活的滚动条宽度和高度 滚动条的大小可以动态调整以适配内容的总高度。
运行以上代码后,你会发现滚动行为更加直观,按钮和滚动条的位置始终保持同步,滚动感觉也更流畅。