So I was just messing around trying to make a simple scroll bar. Basically, there are some buttons and a scroll bar. All I am trying to do is move the buttons up if scroll bar is moved down and move buttons down if scroll bar is moved up. Here is the code.
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()
The problem I am having is that while scrolling up and down, they gradually get misplaced. For example, here is how it starts. Notice how the bar on the left and the buttons start on the same level. 
And here is how it ends up after scrolling up and down a few times.

I figured that this was the problem:
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 changes based on how fast the mouse is moved, so the amount by which is changes each frame was inconsistent. To fix that I replaced that with
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]
This fixed that problem, but now the movement does not feel natural (either the scroll bar is moving way to slow or the buttons are moving way to fast).
So my question is: Is there a way for them to be consistent while also moving at different speeds?
Do not shift the position of the list elements, but compute the new position:
ys[i] -= offset
ys[i] = position
Compute the relative position of the bar:
scroll_height = (500 - 100) - bar.height
scroll_rel = (bar.y - 100) / scroll_height
Compute the offset for the list elements:
box_height = (500 - 100)
list_height = 25 * 101
offset = (list_height - box_height) * scroll_rel
Set the new position of the elements:
for i in range(len(ys)):
ys[i] = (i * 101) + 100 - offset
Changes:
while True:
# [...]
if scrollStart:
bar.y = max(100, min(500 - bar.height, mousePos[1]))
pygame.draw.rect(window, (255, 0, 0), bar)
scroll_height = (500 - 100) - bar.height
scroll_rel = (bar.y - 100) / scroll_height
box_height = (500 - 100)
list_height = 25 * 101
offset = (list_height - box_height) * scroll_rel
for i in range(len(ys)):
ys[i] = (i * 101) + 100 - offset
# [...]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With