Skip to content

Instantly share code, notes, and snippets.

@raitonoberu
Last active March 24, 2021 20:40
Show Gist options
  • Save raitonoberu/0be1c97b63bd84878698150386e77855 to your computer and use it in GitHub Desktop.
Save raitonoberu/0be1c97b63bd84878698150386e77855 to your computer and use it in GitHub Desktop.
Clone of the Fluent UI ProgressRing (pygame)
import pygame
import pygame.draw
import pygame.locals
from math import sin, cos, pi
FILLED_COLOR = (108, 88, 123)
UNFILLED_COLOR = (204, 204, 204)
deg2Rad = (2 * pi) / 360
class ProgressBar(object):
def __init__(self, x, y, radius, thickness, low_speed=8, high_speed=12):
"""
I have to explain what low_speed & high_speed are.
These two dots "chase" each other.
One moves at a low speed, the other at a high speed.
Once the distance between them is 180 degrees (semicircle),
they change speeds with each other.
8 / 12 is one of the values that works fine.
Feel free to play around with it, maybe you will find a better one.
"""
self.x = x
self.y = y
self.radius = radius
self.thickness = thickness
self.speed1 = low_speed
self.speed2 = high_speed
self.d1 = 0 # degrees of 1st point (0..360)
self.d2 = 0 # degrees of 2nd point (0..360)
def draw_arc(self, surface):
if self.d2 > self.d1:
d1, d2 = self.d1, self.d2
else:
d2, d1 = self.d1, self.d2
pygame.draw.circle(surface, FILLED_COLOR, self.coords(d1), self.thickness // 2)
pygame.draw.arc(
surface,
FILLED_COLOR,
pygame.Rect(
self.x - self.radius,
self.y - self.radius,
self.radius * 2,
self.radius * 2,
),
(90 - d2) * deg2Rad,
(90 - d1) * deg2Rad,
self.thickness,
)
pygame.draw.circle(surface, FILLED_COLOR, self.coords(d2), self.thickness // 2)
def coords(self, degrees):
# returns the coordinates of a point at a certain number of degrees
rads = (180 - degrees) * deg2Rad
return (
self.x + int(sin(rads) * (self.radius - self.thickness // 2)),
self.y + int(cos(rads) * (self.radius - self.thickness // 2)),
)
def draw(self, surface):
# draw a white circle
pygame.draw.circle(
surface,
UNFILLED_COLOR,
(self.x, self.y),
self.radius,
self.thickness,
)
if self.d1 == self.d2 and self.d1 % 360 == 0:
# in the original animation, there is a single frame without an arc.
# you can delete it if you want
return
# draw a filled arc
self.draw_arc(surface)
def update(self):
self.d1 = self.d1 + self.speed1
self.d2 = self.d2 + self.speed2
if self.d1 > 360 and self.d2 > 360:
self.d1 -= 360
self.d2 -= 360
if abs(self.d1 - self.d2) >= 180:
self.speed1, self.speed2 = self.speed2, self.speed1
if __name__ == "__main__":
pygame.init()
clock = pygame.time.Clock()
display = pygame.display.set_mode((300, 200))
# indetermiate
bar = ProgressBar(100, 100, 30, 7)
# determinate (just for testing)
bar1 = ProgressBar(200, 100, 30, 7)
value = 0 # determinate value (0..100)
while True:
for event in pygame.event.get():
if event.type == pygame.locals.QUIT:
pygame.quit()
display.fill((255, 255, 255))
# indetermiate
bar.update()
bar.draw(display)
# determinate
bar1.d2 = value / 100 * 360
bar1.draw(display)
value += 1
if value > 100:
value = 0
pygame.display.update()
clock.tick(60)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment