« Progress bar » : différence entre les versions

De Wiki Let's Role
Neo-Teyrall (discussion | contributions)
premier jet pour la progress bar
 
Neo-Teyrall (discussion | contributions)
petit ajustement et correction de certain bug
Ligne 1 : Ligne 1 :


= Progress Bars =
A progress bar is a visual element that can be used to display, in a more playful way, the number of hit points, mana, or any other resource of your character sheet.
A progress bar is a visual element that can be used to display, in a more playful way, the number of hit points, mana, or any other resource of your character sheet.


Ligne 13 : Ligne 12 :
The bar works in a very simple way:
The bar works in a very simple way:


* The '''filled portion''' of the bar is displayed using one symbol (for example, a filled square).
*The '''filled portion''' of the bar is displayed using one symbol (for example, a [https://game-icons.net/1x1/delapouite/plain-square.html ga_plain-square]).
* The '''empty portion''' is displayed using another symbol (or left blank).
* The '''empty portion''' is displayed using another symbol (for example a [https://game-icons.net/1x1/delapouite/square.html ga_square] or   a space " ").


⚠️ The drawback is that this bar is '''not interactive'''. To change its value, you’ll need to add [+] and [–] buttons or directly edit the NumberInput components.
⚠️ The drawback is that this bar is '''not interactive'''. To change its value, you’ll need to add [+] and [–] [[Button|buttons]] or directly edit the NumberInput components.
[[Fichier:Tuto progress bar1 ex.png|centré|700x700px]]
[[Fichier:Tuto_progress_bar1_ex.png|centré|700x700px]]
----
----


Ligne 23 : Ligne 22 :
You need '''3 components''':
You need '''3 components''':


* A '''NumberInput''' for the current value → <code>"bar1_current_value"</code>
<li>A '''NumberInput''' for the current value → <code>"bar1_current_value"</code></li>
* A '''NumberInput''' for the maximum value → <code>"bar1_max_value"</code>
<li>A '''NumberInput''' for the maximum value → <code>"bar1_max_value"</code></li>
* A '''Label''' that will display the progress bar → <code>"bar1_progress"</code>
<li>A '''Label''' that will display the progress bar → <code>"bar1_progress"</code></li>
[[Fichier:Tuto progress bar1.png|centré|700x700px]]
[[Fichier:Tuto_progress_bar1.png|centré|700x700px]]
  init = function(sheet){
  init = function(sheet){
     initProgressBar(sheet,"bar1_progress", "bar1_current_value",
     initProgressBar(sheet,"bar1_progress", "bar1_current_value",
Ligne 32 : Ligne 31 :
                     ":ga_square:",10);
                     ":ga_square:",10);
  }
  }
  const initProgressBar = function(sheet,bar_id, current_value_id, max_value_id, fill_icon, empty_icon, line_length ){
  const initProgressBar = function(sheet,bar_id, current_value_id, max_value_id, fill_icon, empty_icon, line_length ){
      let current = sheet.get(current_value_id);
      let current = sheet.get(current_value_id);
Ligne 61 : Ligne 61 :
✨ You can modify its appearance by:
✨ You can modify its appearance by:


* Changing the classes of <code>"bar1_progress"</code>.
<ul><li> Changing the classes of <code>"bar1_progress"</code> by changing the [[Styling character sheets with classes|color or the size]].</li>
* Adjusting the line length argument.
<li>Adjusting the line length argument.</li>
* Using any available icon in LetsRole.
<li>Using any available icon in LetsRole ([https://fontawesome.com/v5/search?o=r&m=free&s=solid list1] [https://yesicon.app/game-icons liste2]).</li></ul>


== Two-Color Progress Bar ==
== Two-Color Progress Bar ==
This progress bar is an upgrade of the first one: it differentiates the '''filled''' and '''empty''' portions with '''two different colors'''.
This progress bar is an upgrade of the first one: it differentiates the '''filled''' and '''empty''' portions with '''two different colors'''.
[[Fichier:Tuto progress bar2 ex.png|centré|700x700px]]
[[Fichier:Tuto_progress_bar2_ex.png|centré|700x700px]]
----
----


=== 🔧 How to Build It ===
=== 🔧 How to Build It ===
You’ll need '''5 components''':
<p>You’ll need '''5 components''':</p>


* A '''NumberInput''' for the current value → <code>"bar2_current_value"</code>
<li>A '''NumberInput''' for the current value → <code>"bar2_current_value"</code></li>
* A '''NumberInput''' for the maximum value → <code>"bar2_max_value"</code>
<li>A <b>NumberInput</b> for the maximum value → <code>"bar2_max_value"</code></li>
* Two '''Labels side by side''' → <code>"bar3_progress_middle_c1"</code> and <code>"bar3_progress_middle_c2"</code> (these form the mixed line with the last filled + empty parts)
*Two <b>Labels side by side</b> → <code>"bar3_progress_middle_c1"</code> and <code>"bar3_progress_middle_c2"</code> (these form the mixed line with the last filled + empty parts)
* A '''Label above''' for fully filled lines → <code>"bar3_progress_c1"</code>
<li>A '''Label above''' for fully filled lines → <code>"bar3_progress_c1"</code></li>
* A '''Label below''' for fully empty lines → <code>"bar3_progress_c2"</code>
*A '''Label below''' for fully empty lines → <code>"bar3_progress_c2"</code>
[[Fichier:Tuto progress bar2.png|centré|700x700px]]
[[Fichier:Tuto_progress_bar2.png|centré|700x700px]]
  init = function(sheet){
  init = function(sheet){
      initProgressBarTwoColor(sheet, "bar3_progress_c1", "bar3_progress_middle_c1",
      initProgressBarTwoColor(sheet, "bar2_progress_c1", "bar2_progress_middle_c1",
                              "bar3_progress_c2", "bar3_progress_middle_c2",
                              "bar2_progress_c2", "bar2_progress_middle_c2",
                              "bar3_current_value", "bar3_max_value",
                              "bar2_current_value", "bar2_max_value",
                              ":ga_plain-square:", ":ga_plain-square:",10);  
                              ":ga_plain-square:", ":ga_plain-square:",10);  
  }
  }
  const initProgressBarTwoColor = function(sheet,sub_bar_c1_id, bar_c1_id,  
const initProgressBarTwoColor = function(sheet,sub_bar_c1_id, bar_c1_id,  
                                           sub_bar_c2_id,bar_c2_id,  
                                           sub_bar_c2_id,bar_c2_id,  
                                           current_value_id, max_value_id,
                                           current_value_id, max_value_id,
Ligne 126 : Ligne 127 :
  }
  }


✨ Like the first version, it’s fully customizable — but with an '''extra color''' to highlight different resources.
✨ Like the first version, it’s easily customizable — but with an '''extra color''' to highlight different resources.
----
----


Ligne 134 : Ligne 135 :
⚠️ Drawbacks:
⚠️ Drawbacks:


* More difficult and time-consuming to set up.
*More difficult and time-consuming to set up.
* The maximum value must be fixed when designing the sheet (not dynamic).
*The maximum value must be fixed when designing the sheet (not dynamic).
*Each icon can display a différent icon.


Here, the '''NumberInputs can be hidden''' so only the bar is visible.
Here, the '''NumberInputs can be hidden''' so only the bar is visible.


[[Fichier:Tuto progress bar3 ex.png|700x700px]]
[[Fichier:Tuto_progress_bar3_ex.png|700x700px]]
----
<hr />


=== 🔧 How to Build It ===
=== 🔧 How to Build It ===
You’ll need:
You’ll need:


* A '''NumberInput''' for the current value → <code>"bar4_current_value"</code>
<ul><li> A '''NumberInput''' for the current value → <code>"bar4_current_value"</code></li>
* A '''NumberInput''' for the maximum value → <code>"bar4_max_value"</code>
<li>A <b>NumberInput</b> for the maximum value → <code>"bar4_max_value"</code></li>
* A '''series of Labels or Icons''' equal to the max size (e.g., <code>bar4_0</code>, <code>bar4_1</code>, ..., <code>bar4_30</code>)
<li>A '''series of Labels or Icons''' equal to the max size whithe precise id name (e.g., <code resource="Fichier:Tuto progress bar3.png" height width>bar4_0</code>, <code>bar4_1</code>, ..., <code>bar4_30</code>, ...)</li></ul>
[[Fichier:Tuto progress bar3.png|700x700px]]
[[Fichier:Tuto_progress_bar3.png|700x700px]]
  init = function(sheet){
  init = function(sheet){
      initButtonProgressBar(sheet,"bar4_", 20, "bar4_current_value", "bar4_max_value", "text-success ", "text-danger opacity-50" , "d-none");
      initButtonProgressBar(sheet,"bar4_", 20, "bar4_current_value", "bar4_max_value", "text-success ", "text-danger opacity-50" , "d-none");
  }
  }
  const initButtonProgressBar = function(sheet,bar_prefix, bar_nb_element, current_value_id, max_value_id, fill_class,  empty_class, disable_class ){
  const initButtonProgressBar = function(sheet,bar_prefix, bar_nb_element, current_value_id, max_value_id, fill_class,  empty_class, disable_class ){
    let current= sheet.get(current_value_id);
    let current= sheet.get(current_value_id);
    let max = sheet.get(max_value_id);
    let max = sheet.get(max_value_id);
      let empty_class_list = empty_class.split(" ");
    let button_container = sheet.get(button_container_id);
    let fill_class_list = fill_class.split(" ");
   
      const addClasses = function(cmp, class_list){
    let empty_class_list = empty_class.split(" ");
        for(let c of class_list){ cmp.addClass(c); }
    let fill_class_list = fill_class.split(" ");
    }
   
      const removeClasses = function(cmp, class_list){
    const addClasses = function(cmp, class_liste){
        for(let c of class_list){ cmp.removeClass(c); }
        for(let i = 0; i< class_liste.length; i++){
    }
            cmp.addClass(class_liste[i]);
      const updateBarOnButton = function(progress){
        }
        for (let i = 0; i < bar_nb_element; i++){
    }
            let button_i = sheet.get(bar_prefix + i);
   
            if (i <= progress){
    const removeClasses = function(cmp, class_liste){
                removeClasses(button_i, empty_class_list);
        for(let i = 0; i< class_liste.length; i++){
                addClasses(button_i, fill_class_list);
        cmp.removeClass(class_liste[i]);
            } else {
        }
                removeClasses(button_i, fill_class_list);
    }
                addClasses(button_i, empty_class_list);
   
            }
    const updateBarOnButton = function(progress){
        }
        let i = 0;
    }
        while (i < bar_nb_element){
      const updateMax = function(){
            let button_i = sheet.get(bar_prefix + i);
        let max_value = max.value()-1;
            if ( i <= progress){
        for (let i = 0; i < bar_nb_element; i++){
                removeClasses(button_i, empty_class_list);
            let button_i = sheet.get(bar_prefix + i);
                addClasses(button_i, fill_class_list);
            if ( i <= max_value){
            } else {
                button_i.removeClass(disable_class);
                removeClasses(button_i, fill_class_list);
            } else {
                addClasses(button_i, empty_class_list);
                  button_i.addClass(disable_class);
            }
            }
            i++;
        }
        }
    }
    }
      const init_a_button = function(n){
   
        let button_i = sheet.get(bar_prefix + n);
    const updateMax = function(){
        button_i.on("click", function(){
        let i = 0;
            current.value(n);
        let max_value = max.value()-1;
            updateBarOnButton(n);
        while (i < bar_nb_element){
        });
            let button_i = sheet.get(bar_prefix + i);
    }
            if ( i <= max_value){
      for(let i = 0 ; i < bar_nb_element ; i++){
                button_i.removeClass(disable_class);
        init_a_button(i);
            } else {
    }
                button_i.addClass(disable_class);
    max.on("update",updateMax);
            }
    current.on("update",function(){updateBarOnButton(current.value()-1)});
        i++;
    updateBarOnButton(current.value()-1);
        }
    updateMax();
    }
   
    const barClicked = function(btn){
        if(btn.id().includes(bar_prefix)){
            let n = parseInt(btn.id().split(bar_prefix)[1]);
            current.value(n);
            updateBarOnButton(n);
        }
    }
   
    button_container.on("click",".label",barClicked);  
    max.on("update",updateMax);
    current.on("update",function(){updateBarOnButton(current.value()-1)});
   
    updateBarOnButton(current.value()-1);
    updateMax();
  }
  }


Ligne 205 : Ligne 223 :


Also, the buttons don’t need perfect formatting — you can arrange them however you like.
Also, the buttons don’t need perfect formatting — you can arrange them however you like.
[[Fichier:Tuto progress bar3 ex2.png|centré|700x700px]]
[[Fichier:Tuto_progress_bar3_ex2.png|centré|700x700px]]

Version du 10 septembre 2025 à 12:53

A progress bar is a visual element that can be used to display, in a more playful way, the number of hit points, mana, or any other resource of your character sheet.

There are several methods to build a progress bar. In this page, we’ll cover four different approaches, each with its own advantages and disadvantages.


Dynamic Progress Bar

Let’s start with the simplest method. The dynamic progress bar makes it easy to manage large ranges of values and is highly customizable, since its appearance can be modified in just a few clicks.

With some coding skill, you can even make progress bars that players themselves can personalize.

The bar works in a very simple way:

  • The filled portion of the bar is displayed using one symbol (for example, a ga_plain-square).
  • The empty portion is displayed using another symbol (for example a ga_square or a space " ").

⚠️ The drawback is that this bar is not interactive. To change its value, you’ll need to add [+] and [–] buttons or directly edit the NumberInput components.


🔧 How to Build It

You need 3 components:

  • A NumberInput for the current value → "bar1_current_value"
  • A NumberInput for the maximum value → "bar1_max_value"
  • A Label that will display the progress bar → "bar1_progress"
  • init = function(sheet){
       initProgressBar(sheet,"bar1_progress", "bar1_current_value",
                       "bar1_max_value", ":ga_plain-square:",
                       ":ga_square:",10);
    }
    
    const initProgressBar = function(sheet,bar_id, current_value_id, max_value_id, fill_icon, empty_icon, line_length ){
        let current = sheet.get(current_value_id);
        let max  = sheet.get(max_value_id);
        let bar = sheet.get(bar_id);
        const updateBar = function(){
            let current_value = current.value();
            let max_value = max.value();
            let bar_value = "";
            for(let i = 0; i < max_value; i++) {
                if (i != 0 && i%line_length==0){
                    bar_value += "\n";
                }
                if ( i < current_value){
                    bar_value += fill_icon;
                } else {
                    bar_value += empty_icon;
                }
            }
            if (max_value < current_value){
                current.value(max_value);
            }
            bar.value(bar_value);
        }
        current.on("update", updateBar);
        max.on("update", updateBar);
    }
    

    ✨ You can modify its appearance by:

    • Changing the classes of "bar1_progress" by changing the color or the size.
    • Adjusting the line length argument.
    • Using any available icon in LetsRole (list1 liste2).

    Two-Color Progress Bar

    This progress bar is an upgrade of the first one: it differentiates the filled and empty portions with two different colors.


    🔧 How to Build It

    You’ll need 5 components:

  • A NumberInput for the current value → "bar2_current_value"
  • A NumberInput for the maximum value → "bar2_max_value"
    • Two Labels side by side"bar3_progress_middle_c1" and "bar3_progress_middle_c2" (these form the mixed line with the last filled + empty parts)
  • A Label above for fully filled lines → "bar3_progress_c1"
    • A Label below for fully empty lines → "bar3_progress_c2"
    init = function(sheet){
        initProgressBarTwoColor(sheet, "bar2_progress_c1", "bar2_progress_middle_c1",
                                "bar2_progress_c2", "bar2_progress_middle_c2",
                                "bar2_current_value", "bar2_max_value",
                                ":ga_plain-square:", ":ga_plain-square:",10); 
    }
    
    const initProgressBarTwoColor = function(sheet,sub_bar_c1_id, bar_c1_id, 
                                             sub_bar_c2_id,bar_c2_id, 
                                             current_value_id, max_value_id,
                                             fill_icon, empty_icon, line_length){
        let current = sheet.get(current_value_id);
        let max  = sheet.get(max_value_id);
        let sub_bar1 = sheet.get(sub_bar_c1_id);
        let bar1 = sheet.get(bar_c1_id);
        let sub_bar2 = sheet.get(sub_bar_c2_id);
        let bar2 = sheet.get(bar_c2_id);
        const stringMul = function(string, mul){
            let out_string = "";
            for(let i = 0 ; i < mul ; i++){
                if(i != 0 && i % line_length == 0){
                    out_string += "\n";
                } 
                out_string += string;
            }
            return out_string;
         }
        const updateBar = function(){
            let current_value = current.value();
            let max_value = max.value();
            if (max_value < current_value){
                current.value(max_value);
                current_value = max_value;
            }
            let left = max_value - current_value; 
            let on_top_bar = current_value - (current_value % line_length);
            let on_midle_bar1 = current_value % line_length;
            let on_midle_bar2 = (left < line_length - on_midle_bar1) ? left : line_length - on_midle_bar1;
            let on_bot_bar = max_value - current_value - on_midle_bar2;
            sub_bar1.value(stringMul(fill_icon, on_top_bar));
            bar1.value(stringMul(fill_icon, on_midle_bar1));
            bar2.value(stringMul(empty_icon, on_midle_bar2));
            sub_bar2.value(stringMul(empty_icon , on_bot_bar));
        }
        current.on("update", updateBar);
        max.on("update", updateBar);
    }
    

    ✨ Like the first version, it’s easily customizable — but with an extra color to highlight different resources.


    Clickable Progress Bar

    This progress bar is interactive: players can click on it to change its value.

    ⚠️ Drawbacks:

    • More difficult and time-consuming to set up.
    • The maximum value must be fixed when designing the sheet (not dynamic).
    • Each icon can display a différent icon.

    Here, the NumberInputs can be hidden so only the bar is visible.


    🔧 How to Build It

    You’ll need:

    • A NumberInput for the current value → "bar4_current_value"
    • A NumberInput for the maximum value → "bar4_max_value"
    • A series of Labels or Icons equal to the max size whithe precise id name (e.g., bar4_0, bar4_1, ..., bar4_30, ...)

    init = function(sheet){
        initButtonProgressBar(sheet,"bar4_", 20, "bar4_current_value", "bar4_max_value", "text-success ", "text-danger opacity-50" , "d-none");
    }
    
    const initButtonProgressBar = function(sheet,bar_prefix, bar_nb_element, current_value_id, max_value_id, fill_class,  empty_class, disable_class ){
        let current= sheet.get(current_value_id);
        let max = sheet.get(max_value_id);
        let button_container = sheet.get(button_container_id);
    
        let empty_class_list = empty_class.split(" ");
        let fill_class_list = fill_class.split(" ");
    
        const addClasses = function(cmp, class_liste){
            for(let i = 0; i< class_liste.length; i++){
                cmp.addClass(class_liste[i]);
            }
        }
    
        const removeClasses = function(cmp, class_liste){
            for(let i = 0; i< class_liste.length; i++){
            cmp.removeClass(class_liste[i]);
            }
        }
    
        const updateBarOnButton = function(progress){
            let i = 0;
            while (i < bar_nb_element){
                let button_i = sheet.get(bar_prefix + i);
                if ( i <= progress){
                    removeClasses(button_i, empty_class_list);
                    addClasses(button_i, fill_class_list);
                } else {
                    removeClasses(button_i, fill_class_list);
                    addClasses(button_i, empty_class_list);
                }
                i++;
            }
        }
    
        const updateMax = function(){
            let i = 0;
            let max_value = max.value()-1;
            while (i < bar_nb_element){
                let button_i = sheet.get(bar_prefix + i);
                if ( i <= max_value){
                    button_i.removeClass(disable_class);
                } else {
                    button_i.addClass(disable_class);
                }
            i++;
            }
        }
    
        const barClicked = function(btn){
            if(btn.id().includes(bar_prefix)){
                let n = parseInt(btn.id().split(bar_prefix)[1]);
                current.value(n);
                updateBarOnButton(n);
            }
        }
    
        button_container.on("click",".label",barClicked); 
        max.on("update",updateMax);
        current.on("update",function(){updateBarOnButton(current.value()-1)});
       
        updateBarOnButton(current.value()-1);
        updateMax();
    }
    

    ✨ Although harder to implement, this bar is very user-friendly since players can simply click on it.

    Also, the buttons don’t need perfect formatting — you can arrange them however you like.