Changing the width of a centered progress bar

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite
2












I am making a game in which the player has a certain amount of available boost. To show this, I am using a blue bar inside a black frame. The bar can grow and shrink at different rates as available boost goes up and down. It works well and looks nice, but it almost halves my FPS.



The relevant snippet:






var boostStyle = document.getElementById("available-boost").style;
var currBoost = 100;
var maxBoost = 100;

function setBoost(boostAmount)
boostStyle.width = (boostAmount / maxBoost) * 100 + "%";
;

function demonstrateBoostChange()
currBoost -= 1;
setBoost(currBoost);
if (currBoost < 1)
currBoost = maxBoost;

requestAnimationFrame(demonstrateBoostChange);

requestAnimationFrame(demonstrateBoostChange);

#boost-bar 
bottom: 10px;
position: absolute;
right: 20%;
left: 20%;
height: 20px;
border-radius: 12px;
border-style: solid;
border-width: 2px;
border-color: rgb(0, 0, 0);


#available-boost
margin: 0 auto;
height: 20px;
width: 100%;
text-align: center;
font-weight: bold;
border-radius: 10px;
background-color: rgb(0, 225, 255);

<div id="boost-bar">
<div id="available-boost">Boost</div>
</div>




(I see the slight glitch where the text moves when very little boost remains, but I can solve that later. )

The performance hit is probably not noticeable since it is the only thing running, but I have used Chrome DevTools to see that it is the slowest part of my code. I think this is because changing the width is triggering a reflow.



How can I improve the performance of the above code?



Some considerations:



  • I am already using a canvas for some other things, would it be faster to just put the bar on that?

  • The boost can change at different rates, depending on the maxBoost.






share|improve this question



























    up vote
    3
    down vote

    favorite
    2












    I am making a game in which the player has a certain amount of available boost. To show this, I am using a blue bar inside a black frame. The bar can grow and shrink at different rates as available boost goes up and down. It works well and looks nice, but it almost halves my FPS.



    The relevant snippet:






    var boostStyle = document.getElementById("available-boost").style;
    var currBoost = 100;
    var maxBoost = 100;

    function setBoost(boostAmount)
    boostStyle.width = (boostAmount / maxBoost) * 100 + "%";
    ;

    function demonstrateBoostChange()
    currBoost -= 1;
    setBoost(currBoost);
    if (currBoost < 1)
    currBoost = maxBoost;

    requestAnimationFrame(demonstrateBoostChange);

    requestAnimationFrame(demonstrateBoostChange);

    #boost-bar 
    bottom: 10px;
    position: absolute;
    right: 20%;
    left: 20%;
    height: 20px;
    border-radius: 12px;
    border-style: solid;
    border-width: 2px;
    border-color: rgb(0, 0, 0);


    #available-boost
    margin: 0 auto;
    height: 20px;
    width: 100%;
    text-align: center;
    font-weight: bold;
    border-radius: 10px;
    background-color: rgb(0, 225, 255);

    <div id="boost-bar">
    <div id="available-boost">Boost</div>
    </div>




    (I see the slight glitch where the text moves when very little boost remains, but I can solve that later. )

    The performance hit is probably not noticeable since it is the only thing running, but I have used Chrome DevTools to see that it is the slowest part of my code. I think this is because changing the width is triggering a reflow.



    How can I improve the performance of the above code?



    Some considerations:



    • I am already using a canvas for some other things, would it be faster to just put the bar on that?

    • The boost can change at different rates, depending on the maxBoost.






    share|improve this question























      up vote
      3
      down vote

      favorite
      2









      up vote
      3
      down vote

      favorite
      2






      2





      I am making a game in which the player has a certain amount of available boost. To show this, I am using a blue bar inside a black frame. The bar can grow and shrink at different rates as available boost goes up and down. It works well and looks nice, but it almost halves my FPS.



      The relevant snippet:






      var boostStyle = document.getElementById("available-boost").style;
      var currBoost = 100;
      var maxBoost = 100;

      function setBoost(boostAmount)
      boostStyle.width = (boostAmount / maxBoost) * 100 + "%";
      ;

      function demonstrateBoostChange()
      currBoost -= 1;
      setBoost(currBoost);
      if (currBoost < 1)
      currBoost = maxBoost;

      requestAnimationFrame(demonstrateBoostChange);

      requestAnimationFrame(demonstrateBoostChange);

      #boost-bar 
      bottom: 10px;
      position: absolute;
      right: 20%;
      left: 20%;
      height: 20px;
      border-radius: 12px;
      border-style: solid;
      border-width: 2px;
      border-color: rgb(0, 0, 0);


      #available-boost
      margin: 0 auto;
      height: 20px;
      width: 100%;
      text-align: center;
      font-weight: bold;
      border-radius: 10px;
      background-color: rgb(0, 225, 255);

      <div id="boost-bar">
      <div id="available-boost">Boost</div>
      </div>




      (I see the slight glitch where the text moves when very little boost remains, but I can solve that later. )

      The performance hit is probably not noticeable since it is the only thing running, but I have used Chrome DevTools to see that it is the slowest part of my code. I think this is because changing the width is triggering a reflow.



      How can I improve the performance of the above code?



      Some considerations:



      • I am already using a canvas for some other things, would it be faster to just put the bar on that?

      • The boost can change at different rates, depending on the maxBoost.






      share|improve this question













      I am making a game in which the player has a certain amount of available boost. To show this, I am using a blue bar inside a black frame. The bar can grow and shrink at different rates as available boost goes up and down. It works well and looks nice, but it almost halves my FPS.



      The relevant snippet:






      var boostStyle = document.getElementById("available-boost").style;
      var currBoost = 100;
      var maxBoost = 100;

      function setBoost(boostAmount)
      boostStyle.width = (boostAmount / maxBoost) * 100 + "%";
      ;

      function demonstrateBoostChange()
      currBoost -= 1;
      setBoost(currBoost);
      if (currBoost < 1)
      currBoost = maxBoost;

      requestAnimationFrame(demonstrateBoostChange);

      requestAnimationFrame(demonstrateBoostChange);

      #boost-bar 
      bottom: 10px;
      position: absolute;
      right: 20%;
      left: 20%;
      height: 20px;
      border-radius: 12px;
      border-style: solid;
      border-width: 2px;
      border-color: rgb(0, 0, 0);


      #available-boost
      margin: 0 auto;
      height: 20px;
      width: 100%;
      text-align: center;
      font-weight: bold;
      border-radius: 10px;
      background-color: rgb(0, 225, 255);

      <div id="boost-bar">
      <div id="available-boost">Boost</div>
      </div>




      (I see the slight glitch where the text moves when very little boost remains, but I can solve that later. )

      The performance hit is probably not noticeable since it is the only thing running, but I have used Chrome DevTools to see that it is the slowest part of my code. I think this is because changing the width is triggering a reflow.



      How can I improve the performance of the above code?



      Some considerations:



      • I am already using a canvas for some other things, would it be faster to just put the bar on that?

      • The boost can change at different rates, depending on the maxBoost.





      var boostStyle = document.getElementById("available-boost").style;
      var currBoost = 100;
      var maxBoost = 100;

      function setBoost(boostAmount)
      boostStyle.width = (boostAmount / maxBoost) * 100 + "%";
      ;

      function demonstrateBoostChange()
      currBoost -= 1;
      setBoost(currBoost);
      if (currBoost < 1)
      currBoost = maxBoost;

      requestAnimationFrame(demonstrateBoostChange);

      requestAnimationFrame(demonstrateBoostChange);

      #boost-bar 
      bottom: 10px;
      position: absolute;
      right: 20%;
      left: 20%;
      height: 20px;
      border-radius: 12px;
      border-style: solid;
      border-width: 2px;
      border-color: rgb(0, 0, 0);


      #available-boost
      margin: 0 auto;
      height: 20px;
      width: 100%;
      text-align: center;
      font-weight: bold;
      border-radius: 10px;
      background-color: rgb(0, 225, 255);

      <div id="boost-bar">
      <div id="available-boost">Boost</div>
      </div>





      var boostStyle = document.getElementById("available-boost").style;
      var currBoost = 100;
      var maxBoost = 100;

      function setBoost(boostAmount)
      boostStyle.width = (boostAmount / maxBoost) * 100 + "%";
      ;

      function demonstrateBoostChange()
      currBoost -= 1;
      setBoost(currBoost);
      if (currBoost < 1)
      currBoost = maxBoost;

      requestAnimationFrame(demonstrateBoostChange);

      requestAnimationFrame(demonstrateBoostChange);

      #boost-bar 
      bottom: 10px;
      position: absolute;
      right: 20%;
      left: 20%;
      height: 20px;
      border-radius: 12px;
      border-style: solid;
      border-width: 2px;
      border-color: rgb(0, 0, 0);


      #available-boost
      margin: 0 auto;
      height: 20px;
      width: 100%;
      text-align: center;
      font-weight: bold;
      border-radius: 10px;
      background-color: rgb(0, 225, 255);

      <div id="boost-bar">
      <div id="available-boost">Boost</div>
      </div>








      share|improve this question












      share|improve this question




      share|improve this question








      edited Aug 2 at 17:13
























      asked Aug 2 at 17:06









      pydude

      1825




      1825




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          2
          down vote



          accepted










          Best performance




          I am already using a canvas for some other things, would it be faster to just put the bar on that?




          If you are already rendering to a canvas that is under the boost bar the answer is...



          • Yes much faster to draw the boost to the canvas.

          • No if text render quality is more important than frame rate.

          Simple render function



          You can either render the whole boarder, background, bar and text each frame, and still have a performance increase over HTML reflows






          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          There are however some problems with this. The canvas text quality is very poor compared to the DOM, and if you are rendering text elsewhere using another font, switching fonts means that the code is not optimal.



          The following addresses rendering issues.



          Sprite sheet



          Sprites are fast. Even a medium level device can render 400+ scaled rotated, non uniformly scaled, alpha faded sprites per frame @ 60FPS if done correctly, a medium Laptop 1000+ and top end machines, well I have got 64000+ @ 60FPS



          Ideally for the best performance and quality you should have the (boost bar) background and text pre-rendered. Either as a loaded image (PNG) or (SVG) that you scale and render once to an offscreen canvas. Or render them with code to the offscreen canvas.



          You should use a single offscreen canvas to group related sprites. As an example using only the background and text create a canvas that is twice the height. At game start render the background in the top half and the text in the bottom.



          The code to render at run time then becomes






           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support






          Text quality & performance



          Interactive full screen canvas apps can have many (100+) widgets rendered using the 2D API. The more you need the more favorably it is to render direct to canvas.



          But...



          Canvas text is notoriously poor quality and a good reason to take the DOM performance hit in favor of quality.



          If you are already rendering text to the canvas and find it acceptable then for consistency, render all text the same way.



          Sprite sheet



          As outlined above pre rendering text to an offscreen canvas sprite sheet will have improvements in performance. Quality can be improved using some simple text rendering techniques.



          Production render



          You can render text in production using Photoshop or like software and load as a PNG



          Using the above two methods has some benifits



          • There is no performance penalty for non uniform scaling, rotation, and skews.

          • Fades are also easy using ctx.globalAlpha

          • Glows, negatives, jell like pixel FXs can be achieve using the ctx.globalCompositeOperation

          No text at all



          Text is language dependent, fonts rendering is inconstant from browser to browser, browser version, OS version, GPU setup. The best option is in fact to not use any text at all, or minimize text use in favor of icons.



          Sub pixel rendering.



          You can get very high quality pre-rendered text using sub pixel rendering (AKA True Type) Note however it is background color dependent and thus for the progress bar will need two sprites, one for text over background, and one for text over the bar. You then render both text sprites, clipping them to as needed.



          Caveats



          One frame per frame. Dont use more than one call torequestAnimationFrame` per display frame. Multi frames calls have many problems. Render all content from one main loop function.



          Changing fonts during a frame render should be minimized, this includes the font size. The best is that you use only one font, set at a fixed resolution. For example set font once at start ctx.font = "32px Arial"; and then use the transform to size it. ctx.setTransform(fontSize / 32, 0, 0, fontSize / 32, textX, textY)



          Warning: Avoid the following as they are frame rate killers and can make a 60FPS visualization drop to only a few frames a second.




          • ctx.filter Only for static renders

          • Shadows. Maybe if the blur is small and its one or two items, but it adds an order of magnitude to rendering time for each change to any shadow settings that are rendered.


          • Dashed lines (on Chrome) ctx.strokeText the number of dashes have a one time performance cost. Calling ctx.stroke with dashed line ctx.setLineDash(, or after changing the transform while there is an active line dash, there is performance hit directly related to the number of dashes drawn (including clipped strokes not rendered).



            In previous version of this answer I said "even if not rendered" referring to the performance hit of dashed strokes, that was incorrect. There is no cost until you call ctx.stroke







          share|improve this answer























          • Thanks for these tips. I will probably render on the canvas then!
            – pydude
            Aug 3 at 14:03

















          up vote
          2
          down vote













          You may put your text "Boost" in a seperate element and position it.
          Check this:






          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          Note how position: absolute; behaves inside position: relative;.




          After all I suggest you to drop this part and code it with canvas. Even CSS animations will outperform requestAnimationFrame.



          Example with CSS animations:




          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>








          share|improve this answer























          • I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
            – pydude
            Aug 2 at 20:20











          • @pydude, sure, it will work just fine.
            – sineemore
            Aug 2 at 20:25










          • How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
            – pydude
            Aug 2 at 20:35










          • @pydude, I mean canvas will work just fine (:
            – sineemore
            Aug 3 at 10:54










          • oh, okay. Thanks for the tips.
            – pydude
            Aug 3 at 13:58










          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f200836%2fchanging-the-width-of-a-centered-progress-bar%23new-answer', 'question_page');

          );

          Post as a guest






























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          2
          down vote



          accepted










          Best performance




          I am already using a canvas for some other things, would it be faster to just put the bar on that?




          If you are already rendering to a canvas that is under the boost bar the answer is...



          • Yes much faster to draw the boost to the canvas.

          • No if text render quality is more important than frame rate.

          Simple render function



          You can either render the whole boarder, background, bar and text each frame, and still have a performance increase over HTML reflows






          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          There are however some problems with this. The canvas text quality is very poor compared to the DOM, and if you are rendering text elsewhere using another font, switching fonts means that the code is not optimal.



          The following addresses rendering issues.



          Sprite sheet



          Sprites are fast. Even a medium level device can render 400+ scaled rotated, non uniformly scaled, alpha faded sprites per frame @ 60FPS if done correctly, a medium Laptop 1000+ and top end machines, well I have got 64000+ @ 60FPS



          Ideally for the best performance and quality you should have the (boost bar) background and text pre-rendered. Either as a loaded image (PNG) or (SVG) that you scale and render once to an offscreen canvas. Or render them with code to the offscreen canvas.



          You should use a single offscreen canvas to group related sprites. As an example using only the background and text create a canvas that is twice the height. At game start render the background in the top half and the text in the bottom.



          The code to render at run time then becomes






           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support






          Text quality & performance



          Interactive full screen canvas apps can have many (100+) widgets rendered using the 2D API. The more you need the more favorably it is to render direct to canvas.



          But...



          Canvas text is notoriously poor quality and a good reason to take the DOM performance hit in favor of quality.



          If you are already rendering text to the canvas and find it acceptable then for consistency, render all text the same way.



          Sprite sheet



          As outlined above pre rendering text to an offscreen canvas sprite sheet will have improvements in performance. Quality can be improved using some simple text rendering techniques.



          Production render



          You can render text in production using Photoshop or like software and load as a PNG



          Using the above two methods has some benifits



          • There is no performance penalty for non uniform scaling, rotation, and skews.

          • Fades are also easy using ctx.globalAlpha

          • Glows, negatives, jell like pixel FXs can be achieve using the ctx.globalCompositeOperation

          No text at all



          Text is language dependent, fonts rendering is inconstant from browser to browser, browser version, OS version, GPU setup. The best option is in fact to not use any text at all, or minimize text use in favor of icons.



          Sub pixel rendering.



          You can get very high quality pre-rendered text using sub pixel rendering (AKA True Type) Note however it is background color dependent and thus for the progress bar will need two sprites, one for text over background, and one for text over the bar. You then render both text sprites, clipping them to as needed.



          Caveats



          One frame per frame. Dont use more than one call torequestAnimationFrame` per display frame. Multi frames calls have many problems. Render all content from one main loop function.



          Changing fonts during a frame render should be minimized, this includes the font size. The best is that you use only one font, set at a fixed resolution. For example set font once at start ctx.font = "32px Arial"; and then use the transform to size it. ctx.setTransform(fontSize / 32, 0, 0, fontSize / 32, textX, textY)



          Warning: Avoid the following as they are frame rate killers and can make a 60FPS visualization drop to only a few frames a second.




          • ctx.filter Only for static renders

          • Shadows. Maybe if the blur is small and its one or two items, but it adds an order of magnitude to rendering time for each change to any shadow settings that are rendered.


          • Dashed lines (on Chrome) ctx.strokeText the number of dashes have a one time performance cost. Calling ctx.stroke with dashed line ctx.setLineDash(, or after changing the transform while there is an active line dash, there is performance hit directly related to the number of dashes drawn (including clipped strokes not rendered).



            In previous version of this answer I said "even if not rendered" referring to the performance hit of dashed strokes, that was incorrect. There is no cost until you call ctx.stroke







          share|improve this answer























          • Thanks for these tips. I will probably render on the canvas then!
            – pydude
            Aug 3 at 14:03














          up vote
          2
          down vote



          accepted










          Best performance




          I am already using a canvas for some other things, would it be faster to just put the bar on that?




          If you are already rendering to a canvas that is under the boost bar the answer is...



          • Yes much faster to draw the boost to the canvas.

          • No if text render quality is more important than frame rate.

          Simple render function



          You can either render the whole boarder, background, bar and text each frame, and still have a performance increase over HTML reflows






          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          There are however some problems with this. The canvas text quality is very poor compared to the DOM, and if you are rendering text elsewhere using another font, switching fonts means that the code is not optimal.



          The following addresses rendering issues.



          Sprite sheet



          Sprites are fast. Even a medium level device can render 400+ scaled rotated, non uniformly scaled, alpha faded sprites per frame @ 60FPS if done correctly, a medium Laptop 1000+ and top end machines, well I have got 64000+ @ 60FPS



          Ideally for the best performance and quality you should have the (boost bar) background and text pre-rendered. Either as a loaded image (PNG) or (SVG) that you scale and render once to an offscreen canvas. Or render them with code to the offscreen canvas.



          You should use a single offscreen canvas to group related sprites. As an example using only the background and text create a canvas that is twice the height. At game start render the background in the top half and the text in the bottom.



          The code to render at run time then becomes






           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support






          Text quality & performance



          Interactive full screen canvas apps can have many (100+) widgets rendered using the 2D API. The more you need the more favorably it is to render direct to canvas.



          But...



          Canvas text is notoriously poor quality and a good reason to take the DOM performance hit in favor of quality.



          If you are already rendering text to the canvas and find it acceptable then for consistency, render all text the same way.



          Sprite sheet



          As outlined above pre rendering text to an offscreen canvas sprite sheet will have improvements in performance. Quality can be improved using some simple text rendering techniques.



          Production render



          You can render text in production using Photoshop or like software and load as a PNG



          Using the above two methods has some benifits



          • There is no performance penalty for non uniform scaling, rotation, and skews.

          • Fades are also easy using ctx.globalAlpha

          • Glows, negatives, jell like pixel FXs can be achieve using the ctx.globalCompositeOperation

          No text at all



          Text is language dependent, fonts rendering is inconstant from browser to browser, browser version, OS version, GPU setup. The best option is in fact to not use any text at all, or minimize text use in favor of icons.



          Sub pixel rendering.



          You can get very high quality pre-rendered text using sub pixel rendering (AKA True Type) Note however it is background color dependent and thus for the progress bar will need two sprites, one for text over background, and one for text over the bar. You then render both text sprites, clipping them to as needed.



          Caveats



          One frame per frame. Dont use more than one call torequestAnimationFrame` per display frame. Multi frames calls have many problems. Render all content from one main loop function.



          Changing fonts during a frame render should be minimized, this includes the font size. The best is that you use only one font, set at a fixed resolution. For example set font once at start ctx.font = "32px Arial"; and then use the transform to size it. ctx.setTransform(fontSize / 32, 0, 0, fontSize / 32, textX, textY)



          Warning: Avoid the following as they are frame rate killers and can make a 60FPS visualization drop to only a few frames a second.




          • ctx.filter Only for static renders

          • Shadows. Maybe if the blur is small and its one or two items, but it adds an order of magnitude to rendering time for each change to any shadow settings that are rendered.


          • Dashed lines (on Chrome) ctx.strokeText the number of dashes have a one time performance cost. Calling ctx.stroke with dashed line ctx.setLineDash(, or after changing the transform while there is an active line dash, there is performance hit directly related to the number of dashes drawn (including clipped strokes not rendered).



            In previous version of this answer I said "even if not rendered" referring to the performance hit of dashed strokes, that was incorrect. There is no cost until you call ctx.stroke







          share|improve this answer























          • Thanks for these tips. I will probably render on the canvas then!
            – pydude
            Aug 3 at 14:03












          up vote
          2
          down vote



          accepted







          up vote
          2
          down vote



          accepted






          Best performance




          I am already using a canvas for some other things, would it be faster to just put the bar on that?




          If you are already rendering to a canvas that is under the boost bar the answer is...



          • Yes much faster to draw the boost to the canvas.

          • No if text render quality is more important than frame rate.

          Simple render function



          You can either render the whole boarder, background, bar and text each frame, and still have a performance increase over HTML reflows






          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          There are however some problems with this. The canvas text quality is very poor compared to the DOM, and if you are rendering text elsewhere using another font, switching fonts means that the code is not optimal.



          The following addresses rendering issues.



          Sprite sheet



          Sprites are fast. Even a medium level device can render 400+ scaled rotated, non uniformly scaled, alpha faded sprites per frame @ 60FPS if done correctly, a medium Laptop 1000+ and top end machines, well I have got 64000+ @ 60FPS



          Ideally for the best performance and quality you should have the (boost bar) background and text pre-rendered. Either as a loaded image (PNG) or (SVG) that you scale and render once to an offscreen canvas. Or render them with code to the offscreen canvas.



          You should use a single offscreen canvas to group related sprites. As an example using only the background and text create a canvas that is twice the height. At game start render the background in the top half and the text in the bottom.



          The code to render at run time then becomes






           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support






          Text quality & performance



          Interactive full screen canvas apps can have many (100+) widgets rendered using the 2D API. The more you need the more favorably it is to render direct to canvas.



          But...



          Canvas text is notoriously poor quality and a good reason to take the DOM performance hit in favor of quality.



          If you are already rendering text to the canvas and find it acceptable then for consistency, render all text the same way.



          Sprite sheet



          As outlined above pre rendering text to an offscreen canvas sprite sheet will have improvements in performance. Quality can be improved using some simple text rendering techniques.



          Production render



          You can render text in production using Photoshop or like software and load as a PNG



          Using the above two methods has some benifits



          • There is no performance penalty for non uniform scaling, rotation, and skews.

          • Fades are also easy using ctx.globalAlpha

          • Glows, negatives, jell like pixel FXs can be achieve using the ctx.globalCompositeOperation

          No text at all



          Text is language dependent, fonts rendering is inconstant from browser to browser, browser version, OS version, GPU setup. The best option is in fact to not use any text at all, or minimize text use in favor of icons.



          Sub pixel rendering.



          You can get very high quality pre-rendered text using sub pixel rendering (AKA True Type) Note however it is background color dependent and thus for the progress bar will need two sprites, one for text over background, and one for text over the bar. You then render both text sprites, clipping them to as needed.



          Caveats



          One frame per frame. Dont use more than one call torequestAnimationFrame` per display frame. Multi frames calls have many problems. Render all content from one main loop function.



          Changing fonts during a frame render should be minimized, this includes the font size. The best is that you use only one font, set at a fixed resolution. For example set font once at start ctx.font = "32px Arial"; and then use the transform to size it. ctx.setTransform(fontSize / 32, 0, 0, fontSize / 32, textX, textY)



          Warning: Avoid the following as they are frame rate killers and can make a 60FPS visualization drop to only a few frames a second.




          • ctx.filter Only for static renders

          • Shadows. Maybe if the blur is small and its one or two items, but it adds an order of magnitude to rendering time for each change to any shadow settings that are rendered.


          • Dashed lines (on Chrome) ctx.strokeText the number of dashes have a one time performance cost. Calling ctx.stroke with dashed line ctx.setLineDash(, or after changing the transform while there is an active line dash, there is performance hit directly related to the number of dashes drawn (including clipped strokes not rendered).



            In previous version of this answer I said "even if not rendered" referring to the performance hit of dashed strokes, that was incorrect. There is no cost until you call ctx.stroke







          share|improve this answer















          Best performance




          I am already using a canvas for some other things, would it be faster to just put the bar on that?




          If you are already rendering to a canvas that is under the boost bar the answer is...



          • Yes much faster to draw the boost to the canvas.

          • No if text render quality is more important than frame rate.

          Simple render function



          You can either render the whole boarder, background, bar and text each frame, and still have a performance increase over HTML reflows






          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          There are however some problems with this. The canvas text quality is very poor compared to the DOM, and if you are rendering text elsewhere using another font, switching fonts means that the code is not optimal.



          The following addresses rendering issues.



          Sprite sheet



          Sprites are fast. Even a medium level device can render 400+ scaled rotated, non uniformly scaled, alpha faded sprites per frame @ 60FPS if done correctly, a medium Laptop 1000+ and top end machines, well I have got 64000+ @ 60FPS



          Ideally for the best performance and quality you should have the (boost bar) background and text pre-rendered. Either as a loaded image (PNG) or (SVG) that you scale and render once to an offscreen canvas. Or render them with code to the offscreen canvas.



          You should use a single offscreen canvas to group related sprites. As an example using only the background and text create a canvas that is twice the height. At game start render the background in the top half and the text in the bottom.



          The code to render at run time then becomes






           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support






          Text quality & performance



          Interactive full screen canvas apps can have many (100+) widgets rendered using the 2D API. The more you need the more favorably it is to render direct to canvas.



          But...



          Canvas text is notoriously poor quality and a good reason to take the DOM performance hit in favor of quality.



          If you are already rendering text to the canvas and find it acceptable then for consistency, render all text the same way.



          Sprite sheet



          As outlined above pre rendering text to an offscreen canvas sprite sheet will have improvements in performance. Quality can be improved using some simple text rendering techniques.



          Production render



          You can render text in production using Photoshop or like software and load as a PNG



          Using the above two methods has some benifits



          • There is no performance penalty for non uniform scaling, rotation, and skews.

          • Fades are also easy using ctx.globalAlpha

          • Glows, negatives, jell like pixel FXs can be achieve using the ctx.globalCompositeOperation

          No text at all



          Text is language dependent, fonts rendering is inconstant from browser to browser, browser version, OS version, GPU setup. The best option is in fact to not use any text at all, or minimize text use in favor of icons.



          Sub pixel rendering.



          You can get very high quality pre-rendered text using sub pixel rendering (AKA True Type) Note however it is background color dependent and thus for the progress bar will need two sprites, one for text over background, and one for text over the bar. You then render both text sprites, clipping them to as needed.



          Caveats



          One frame per frame. Dont use more than one call torequestAnimationFrame` per display frame. Multi frames calls have many problems. Render all content from one main loop function.



          Changing fonts during a frame render should be minimized, this includes the font size. The best is that you use only one font, set at a fixed resolution. For example set font once at start ctx.font = "32px Arial"; and then use the transform to size it. ctx.setTransform(fontSize / 32, 0, 0, fontSize / 32, textX, textY)



          Warning: Avoid the following as they are frame rate killers and can make a 60FPS visualization drop to only a few frames a second.




          • ctx.filter Only for static renders

          • Shadows. Maybe if the blur is small and its one or two items, but it adds an order of magnitude to rendering time for each change to any shadow settings that are rendered.


          • Dashed lines (on Chrome) ctx.strokeText the number of dashes have a one time performance cost. Calling ctx.stroke with dashed line ctx.setLineDash(, or after changing the transform while there is an active line dash, there is performance hit directly related to the number of dashes drawn (including clipped strokes not rendered).



            In previous version of this answer I said "even if not rendered" referring to the performance hit of dashed strokes, that was incorrect. There is no cost until you call ctx.stroke







          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          const boostBar = // ? and values are unknown set them to what you need
          x : ?, // For visual quality ensure these values are floored
          y : ?, // ...
          width : ? // ...
          height : ? // ...
          borderWidth : ? //...
          text : "Boost",
          max : ? max boost,
          boost : ? current level,
          font : "size and font name",
          style :
          bg : "CSS style value",
          boarder : "CSS style value",
          bar : "CSS style value",
          font : "CSS style value",
          ,
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw outer boarder
          ctx.fillStyle = stl.boarder;
          ctx.fillRect(0, 0, bB.width, bB.height);

          // Draw inner background
          const bW = bB.borderWidth;
          const w = bB.width - bW *2; // will need this again
          const h = bB.height - bW *2; // will need this again
          ctx.fillStyle = stl.bg;
          ctx.fillRect(bW, bW, w, h);

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          ctx.font = bB.font;
          ctx.fillStyle = stl.font;
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText(bB.text, w / 2, bB.height / 2);


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support






           // following on from above snippet

          spriteSheet : // an offscreen canvas containing background and text
          draw(ctx)
          const bB = boostBar; // aliases to reduce code noise
          const stl = bB.style;

          // set to bar local coords
          ctx.setTransform(1, 0, 0, 1, bB.x, bB.y);

          // Draw sprite that has background and boarder
          ctx.drawImage(bB.spriteSheet,
          0, 0, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );

          // calculate bar size
          const barSize = (bB.boost / bB.max) * w;

          // draw bar
          ctx.fillStyle = stl.bg;
          ctx.fillRect((w - barSize) / 2, bW, barSize, h);

          // Draw sprite that has text overlay
          ctx.drawImage(bB.spriteSheet,
          0, bB.height, bB.width, bB.height,
          0, 0, bB.width, bB.height
          );


          // reset transform if you render other content at absolute coords
          ctx.setTransform(1, 0, 0, 1, 0, 0);
          // Note that you can use
          // ctx.resetTransform(); // but check for support







          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Aug 3 at 14:59


























          answered Aug 3 at 1:24









          Blindman67

          5,1981320




          5,1981320











          • Thanks for these tips. I will probably render on the canvas then!
            – pydude
            Aug 3 at 14:03
















          • Thanks for these tips. I will probably render on the canvas then!
            – pydude
            Aug 3 at 14:03















          Thanks for these tips. I will probably render on the canvas then!
          – pydude
          Aug 3 at 14:03




          Thanks for these tips. I will probably render on the canvas then!
          – pydude
          Aug 3 at 14:03












          up vote
          2
          down vote













          You may put your text "Boost" in a seperate element and position it.
          Check this:






          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          Note how position: absolute; behaves inside position: relative;.




          After all I suggest you to drop this part and code it with canvas. Even CSS animations will outperform requestAnimationFrame.



          Example with CSS animations:




          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>








          share|improve this answer























          • I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
            – pydude
            Aug 2 at 20:20











          • @pydude, sure, it will work just fine.
            – sineemore
            Aug 2 at 20:25










          • How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
            – pydude
            Aug 2 at 20:35










          • @pydude, I mean canvas will work just fine (:
            – sineemore
            Aug 3 at 10:54










          • oh, okay. Thanks for the tips.
            – pydude
            Aug 3 at 13:58














          up vote
          2
          down vote













          You may put your text "Boost" in a seperate element and position it.
          Check this:






          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          Note how position: absolute; behaves inside position: relative;.




          After all I suggest you to drop this part and code it with canvas. Even CSS animations will outperform requestAnimationFrame.



          Example with CSS animations:




          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>








          share|improve this answer























          • I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
            – pydude
            Aug 2 at 20:20











          • @pydude, sure, it will work just fine.
            – sineemore
            Aug 2 at 20:25










          • How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
            – pydude
            Aug 2 at 20:35










          • @pydude, I mean canvas will work just fine (:
            – sineemore
            Aug 3 at 10:54










          • oh, okay. Thanks for the tips.
            – pydude
            Aug 3 at 13:58












          up vote
          2
          down vote










          up vote
          2
          down vote









          You may put your text "Boost" in a seperate element and position it.
          Check this:






          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          Note how position: absolute; behaves inside position: relative;.




          After all I suggest you to drop this part and code it with canvas. Even CSS animations will outperform requestAnimationFrame.



          Example with CSS animations:




          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>








          share|improve this answer















          You may put your text "Boost" in a seperate element and position it.
          Check this:






          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          Note how position: absolute; behaves inside position: relative;.




          After all I suggest you to drop this part and code it with canvas. Even CSS animations will outperform requestAnimationFrame.



          Example with CSS animations:




          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>








          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          var inner = document.querySelector(".inner");
          var cb = function()
          var k = 1 - Date.now() % 1500 / 1500;
          inner.style.width = (k * 100).toFixed(2) + "%";
          window.requestAnimationFrame(cb);
          ;
          cb();

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>





          var inner = document.querySelector(".inner");
          inner.addEventListener("animationend", function ()
          // Animation just ended
          inner.parentNode.style = 'display:none;';
          )
          inner.style.animationDuration = '2s';
          inner.style.animationName = 'boost';

          .boost 
          border: 2px solid #999;
          width: 300px;
          position: relative;
          font: 16px mono;


          .boost .inner
          margin: 0 auto;
          background-color: #999;
          animation-timing-function: linear;


          .boost .text
          position: absolute;
          text-align: center;
          top: 0;
          left: 0;
          width: 100%;


          @keyframes boost
          0% width: 100%;
          100% width: 0;

          <div class="boost">
          <div class="inner">&nbsp;</div>
          <div class="text">BOOST</div>
          </div>






          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Aug 2 at 19:28


























          answered Aug 2 at 19:14









          sineemore

          1,175217




          1,175217











          • I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
            – pydude
            Aug 2 at 20:20











          • @pydude, sure, it will work just fine.
            – sineemore
            Aug 2 at 20:25










          • How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
            – pydude
            Aug 2 at 20:35










          • @pydude, I mean canvas will work just fine (:
            – sineemore
            Aug 3 at 10:54










          • oh, okay. Thanks for the tips.
            – pydude
            Aug 3 at 13:58
















          • I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
            – pydude
            Aug 2 at 20:20











          • @pydude, sure, it will work just fine.
            – sineemore
            Aug 2 at 20:25










          • How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
            – pydude
            Aug 2 at 20:35










          • @pydude, I mean canvas will work just fine (:
            – sineemore
            Aug 3 at 10:54










          • oh, okay. Thanks for the tips.
            – pydude
            Aug 3 at 13:58















          I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
          – pydude
          Aug 2 at 20:20





          I don't think CSS animations will work because the rate of change varies. But you think I should just use canvas instead of individual elements?
          – pydude
          Aug 2 at 20:20













          @pydude, sure, it will work just fine.
          – sineemore
          Aug 2 at 20:25




          @pydude, sure, it will work just fine.
          – sineemore
          Aug 2 at 20:25












          How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
          – pydude
          Aug 2 at 20:35




          How could it possibly work? If the CSS animation says it will take 2 seconds for it to go down, it won't go any other speed.
          – pydude
          Aug 2 at 20:35












          @pydude, I mean canvas will work just fine (:
          – sineemore
          Aug 3 at 10:54




          @pydude, I mean canvas will work just fine (:
          – sineemore
          Aug 3 at 10:54












          oh, okay. Thanks for the tips.
          – pydude
          Aug 3 at 13:58




          oh, okay. Thanks for the tips.
          – pydude
          Aug 3 at 13:58












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f200836%2fchanging-the-width-of-a-centered-progress-bar%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Python Lists

          Aion

          JavaScript Array Iteration Methods