User:DressyPear4/TextoAnimado

From OpenStreetMap Wiki
Jump to navigation Jump to search

Texto animado

Criar uma janela com texto personalizado e comemorativo, pode deixar em full ou antes de tutoriais

Demonstração

.png

Código

from javax.swing import JFrame, JPanel, Timer
from java.awt.image import BufferedImage
from java.awt import Color, Font, Dimension, BorderLayout, RenderingHints
from java.awt.event import MouseAdapter, ActionListener
import math
import random

def safeColor(r, g, b, a=255):
    clamp = lambda v: max(0, min(255, int(v)))
    return Color(clamp(r), clamp(g), clamp(b), clamp(a))

class FireworkParticle:
    def __init__(self, x, y, vx, vy, color, life=60, gravity=0.15, fade=True):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.max_life = float(life)
        self.life = life
        self.color = color
        self.gravity = gravity
        self.fade = fade

    def animate(self):
        self.x += self.vx
        self.y += self.vy
        self.vy += self.gravity 
        if self.fade:
            self.life -= 1

    def draw(self, g):
        if self.fade and self.life <= 0: return
        alpha = int(255 * (self.life / self.max_life)) if self.fade else 255
        g.setColor(safeColor(self.color.getRed(), self.color.getGreen(), self.color.getBlue(), alpha))
        g.fillOval(int(self.x), int(self.y), 3, 3)

class FireworksPanel(JPanel):
    def __init__(self):
        JPanel.__init__(self)
        self.particles = []      
        self.textParticles = []  
        self.textCoords = []     
        self.textToDisplay = "FELIZ 2026"
        self.hasExplodedText = False
        self.lastW, self.lastH = 0, 0 # Para detectar redimensionamento

        class ClickHandler(MouseAdapter):
            def __init__(self, outer): self.outer = outer
            def mousePressed(self, e): self.outer.explode(e.getX(), e.getY())
        self.addMouseListener(ClickHandler(self))

    def explodeText(self, w, h):
        """Recalcula o texto baseado no tamanho atual da tela"""
        self.textParticles = []
        self.textCoords = []
        
        # Ajusta o tamanho da fonte proporcional à largura da tela
        fontSize = int(w / 7.5) 
        font = Font("Serif", Font.BOLD, fontSize)
        
        img = BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB)
        g2d = img.createGraphics()
        g2d.setFont(font)
        metrics = g2d.getFontMetrics(font)
        tx = (w - metrics.stringWidth(self.textToDisplay)) / 2
        ty = (h / 2) + (metrics.getAscent() / 4)
        
        g2d.setColor(Color.WHITE)
        g2d.drawString(self.textToDisplay, int(tx), int(ty))
        g2d.dispose()

        baseColor = Color(255, 215, 0)
        for py in range(0, h, 4):
            for px in range(0, w, 4):
                if (img.getRGB(px, py) >> 24) & 0xFF > 0:
                    self.textCoords.append((px, py))
                    self.textParticles.append(FireworkParticle(px, py, 0, 0, baseColor, gravity=0, fade=False))

    def explode(self, x, y):
        tipo = random.choice(["circle", "double_ring", "heart", "fountain"])
        baseColor = safeColor(random.randint(100,255), random.randint(100,255), random.randint(100,255))
        
        if tipo == "circle":
            for i in range(60):
                ang = random.uniform(0, 2 * math.pi)
                spd = random.uniform(2, 6)
                self.particles.append(FireworkParticle(x, y, math.cos(ang)*spd, math.sin(ang)*spd, baseColor))
        elif tipo == "double_ring":
            cor2 = safeColor(random.randint(100,255), 255, 255)
            for i in range(40):
                ang = (2 * math.pi / 40) * i
                self.particles.append(FireworkParticle(x, y, math.cos(ang)*3, math.sin(ang)*3, baseColor))
                self.particles.append(FireworkParticle(x, y, math.cos(ang)*6, math.sin(ang)*6, cor2))
        elif tipo == "heart":
            for i in range(60):
                t = random.uniform(0, 2 * math.pi)
                vx = 16 * math.sin(t)**3
                vy = -(13 * math.cos(t) - 5 * math.cos(2*t) - 2 * math.cos(3*t) - math.cos(4*t))
                self.particles.append(FireworkParticle(x, y, vx*0.25, vy*0.25, Color.RED, life=80))
        elif tipo == "fountain":
            cor = safeColor(200, 255, 255)
            for i in range(40):
                self.particles.append(FireworkParticle(x, y, random.uniform(-1.5, 1.5), random.uniform(-5, -9), cor, life=70))

    def step(self):
        w, h = self.getWidth(), self.getHeight()
        if w <= 0 or h <= 0: return

        # Se a tela mudou de tamanho ou o texto ainda não explodiu
        if not self.hasExplodedText or w != self.lastW or h != self.lastH:
            self.explodeText(w, h)
            self.hasExplodedText = True
            self.lastW, self.lastH = w, h

        # Cascata infinita
        if self.textCoords:
            for _ in range(12):
                cx, cy = random.choice(self.textCoords)
                self.particles.append(FireworkParticle(cx, cy, 0, random.uniform(0.5, 1.5), Color(255,215,0, 150), life=35, gravity=0.08))

        # Fogos aleatórios na tela toda
        if random.random() < 0.05:
            self.explode(random.randint(50, w-50), random.randint(50, h-100))

        for p in self.particles[:]:
            p.animate()
            if p.life <= 0: self.particles.remove(p)
        self.repaint()

    def paintComponent(self, g):
        # Background com rastro
        g.setColor(Color(0, 0, 0, 50)) 
        g.fillRect(0, 0, self.getWidth(), self.getHeight())
        
        for p in self.textParticles: p.draw(g)
        for p in self.particles: p.draw(g)

class App(object):
    def __init__(self):
        self.frame = JFrame("Show de Fogos 2026")
        self.frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
        self.frame.setLayout(BorderLayout())
        
        self.firePanel = FireworksPanel()
        self.firePanel.setPreferredSize(Dimension(800, 600))
        
        self.frame.add(self.firePanel, BorderLayout.CENTER)
        self.frame.pack()
        self.frame.setLocationRelativeTo(None)
        self.frame.setVisible(True)

        class TimerListener(ActionListener):
            def __init__(self, panel): self.panel = panel
            def actionPerformed(self, e): self.panel.step()
        self.timer = Timer(30, TimerListener(self.firePanel))
        self.timer.start()

if __name__ in ['__main__', 'main']:
    App()