Console Based Process Animation in PHP

Beschreibung

Die folgenden Klasse ermöglicht eine simple Prozessanimation auf der Konsole. Angezeigt wird ein rotierender Balken der ein Zeichen Platz einnimmt.


Motivation

Im Alltag bei der Entwicklung von consolenbasierten Programmen, benötige ich oft Zwischenausgaben, um Prozesse zu verfolgen. z.b. ob das Program noch arbeitet und das macht was es soll. Leider können manche Ausgaben über mehrere Zeilen gehen. ein klassisches Beipsile ist die Ausgabe von vielen tausend Aufzählungen. Statt nun den unschönen Wust einer langen Liste zu durchscrollen, will ich gern bestimmte Dinge auf einer Zeile ausgeben und ggf. auch überschreiben. Daher kam mir die Idee Prozesse in der Console zu animieren. Als ersten Schritt ist folgendes kleines Programm entstanden.


Ausblick

Im Laufe der Zeit werde ich die Klasse noch erweitern. z.b. sollen Prozessbars und andere Ausgaben animiert werden können.


Technisches

Das Grundprinzip der Animation beruht auf der Tatsache, dass mit Carriage Return ( in PHP durch Ausgabe von "\r" ) ein einzelnes Zeichen gelöscht werden kann ohne einen Zeilenumbruch zu verursachen. Dadurch ist es möglich nun beliebig Animatiuonen zu erzeugen, indem alte Zeichen gelöscht und wieder überschrieben werden. Das dieser Vorgang sehr schnell ausgeführt wird erscheint uns eine flüssige Animation.

Um die Frames zu erzeugen hab ich zwischen jedem Überschreiben der dargestellten Zeichen eine Pause von 50 ms eingestellt.

Funktion

Aktuell läuft die Animation 5 Sekunden lang und mit 20 Frames pro Sekunde

<?php

/* 
 * Animiert eine festgelegte Zeichenfolge auf der Console
 */

class ConsoleAnimate{
    
    private $periot_time_ms = 50;
    private $time_counter = 0;
    private $frame_step = 0;
    
    private $playtime = 5;
    private $playbegin = 0;
    private $animate_flag = false;


    private $frameset_rotate = [
        '-' , '\\' , '|' , '/'  
    ];
    

    public function __construct() {
        
        $this -> playbegin = time();
        $this -> animate_flag = true;
        
        # erste Ausgabe
        echo $this -> frameset_rotate[ 0 ];
        
    }

    /**
     * Zeige aktuellen Frame
     */
    public function show(){
        
        # Jetzt
        $now = microtime( true );
        
        # prüfen ob Zeit bis zum nächsten Frame abgelaufen
        if( $this -> time_counter + ( $this -> periot_time_ms / 1000 ) <= $now ){

            # Zeit zurücksetzen
            $this -> time_counter = $now;
            $this -> step_next();
        }
     
        echo "\r" . $this -> frameset_rotate[ $this -> frame_step ];
        
        # Wenn Abspielzeit abgelaufen ist, signalisiere durch FALSE das Ende        
        return ( $this -> playbegin + $this -> playtime > $now ) ? true : false;
        
    }    
    
    /**
     * rotiert durch Frames 0 - x und beginnt wieder von vorn
     */
    private function step_next(){
        
        if( count( $this -> frameset_rotate ) > ( $this -> frame_step + 1 ) ){
            $this -> frame_step++;
        } else {
            $this -> frame_step = 0;
        }
        
    }

    /**
     * Periodendauer pro Frame
     * 
     * @param int $time_in_ms
     */
    public function set_periot( $time_in_ms ){
       
        $this -> periot_time_ms = $time_in_ms;
       
    }
    
    /**
     * Zeitzähler für eine Perioode
     * @return type
     */
    public function get_time_counter(){
        return $this -> time_counter;
    }
    
    /**
     * Frameposition aus dem Frameset
     * @return type
     */
    public function get_frame_step(){
        return $this -> frame_step;
    }
        
    /**
     * Timestamp
     * @return float
     */
    public function get_playtime(){
        return $this -> playtime;
    }
    
    /**
     * Begin der Animation
     * @return float
     */
    public function get_playbegin(){
        return $this -> playbegin;
    }
    
    /**
     * Flagsignal wenn Animationszeit 
     * @return bool
     */
    public function get_animate_flag(){
        return $this -> animate_flag;
    }
    
    /**
     * Alle Klassenvariablen ausgeben
     * @return array
     */
    public function get_status(){
        
        return[
            "animate_flag" => $this -> animate_flag,
            "frame_step" => $this -> frame_step,
            "frameset_rotate" => $this -> frameset_rotate,
            "periot_time_ms" => $this -> periot_time_ms,
            "playbegin" => $this -> playbegin,
            "playtime" => $this -> playtime,
            "time_counter" => $this -> time_counter
        ];
        
    }
    
    
}


$r = new ConsoleAnimate();
while( $r-> show() ){}