1

I'm trying to fade the content of one canvas into another. Starting with the script here I have the following which works but I'm confused as to why the fade happens so quickly - why does globalAlpha seem to have almost completely faded the new image in after only 0.2 of op? Script is below and here on jsbin.

<canvas id="main" width="500" height="300"></canvas>
<canvas id="test" width="500" height="300"></canvas>

<script>

var width = 500, height = 300;

var main = document.getElementById('main');
var mainctx = main.getContext('2d');

//begin path, create a rect the size of the canvas
mainctx.beginPath();
mainctx.rect(0, 0, width, height);
mainctx.fillStyle = "#ff0000";
mainctx.fill();

var test = document.getElementById('test');
var testctx = test.getContext('2d');

//begin path, create a rect the size of the canvas
testctx.beginPath();
testctx.rect(0, 0, width, height);
testctx.fillStyle = "#00ff00";
testctx.fill();

var op = 1;

function fade()
{
    op -= 0.001;

    console.log(op);

    //mainctx.clearRect(0, 0, width, height);
    mainctx.globalAlpha = 1 - op;
    mainctx.drawImage(testctx.canvas, 0, 0);

    if (op <= 0.8) setTimeout(function(){ console.log("done"); }, 1);
    else requestAnimationFrame(fade);
}

fade();

</script>
garrettlynchirl
  • 790
  • 8
  • 23
  • Maybe in your `setTimeout` instead of `1` use a higher value like `2000` – Hackerman Jul 04 '18 at 18:16
  • It's not the speed of the fade as such that is the problem (although I do need to slow it down) it's the distribution of the fade from 1.0 to 0.0. By the time it gets to 0.8 the new image has almost completely faded in. – garrettlynchirl Jul 04 '18 at 18:29
  • 1
    You will have to keep your original canvas state in a third, offscreen canvas that you will draw along with the second one at each step of your cross fade – Kaiido Jul 05 '18 at 00:28
  • @Kaiido an example would help as to why a third canvas is needed. At the moment I have this working with two canvases just not precisely the way I want. – garrettlynchirl Jul 05 '18 at 13:20
  • 1
    @garrettlynch because 0.1 opacity + 0.1 opacity is not 0.2 opacity, so to achieve the transition you need to go from the original image at every steps. I don't have time right now to compose an answer, but I'll try tomorrow. – Kaiido Jul 05 '18 at 13:40
  • @Kaiido yes I guessed that and that is my question really, there's clearly not a steady rate of change but some sort of curve effect in the opacity change, as it changes the rate of change grows. Surely I just need a formula rather than the 1 - op that diminishes op on each pass to counter this? – garrettlynchirl Jul 05 '18 at 13:49

2 Answers2

1

It just appears as if it is fading this quickly because you are not clearing the canvas, you keep overwriting the same transparent image over each other.

All you need to do is remove the // in front of the clearRect function

<!DOCTYPE html>
<html>

  <head>
  </head>

  <body>

   <canvas id="main" width="500" height="300"></canvas>
   <canvas id="test" width="500" height="300"></canvas>

  <script>

  var width = 500, height = 300;
  var main = document.getElementById('main');
   var mainctx = main.getContext('2d');

  //begin path, create a rect the size of the canvas
  mainctx.beginPath();
   mainctx.rect(0, 0, width, height);
  mainctx.fillStyle = "#ff0000";
   mainctx.fill();

  var test = document.getElementById('test');
  var testctx = test.getContext('2d');

  //begin path, create a rect the size of the canvas
  testctx.beginPath();
  testctx.rect(0, 0, width, height);
  testctx.fillStyle = "#00ff00";
  testctx.fill();

  var op = 1;

  function fade()
  {
      op -= 0.001;

      console.log(op);

          //you already had it here, i only had to remove the "//"
      mainctx.clearRect(0, 0, width, height);
      mainctx.globalAlpha = 1 - op;
          mainctx.drawImage(testctx.canvas, 0, 0);

      if (op <= 0.1) setTimeout(function(){ console.log("done"); }, 1);
      else requestAnimationFrame(fade);
  }

  fade();

  </script>

  </body>

</html>

https://jsbin.com/yulekoxuza/edit?html

Hope it helps ;)

0

Thanks to @Kaiido's suggestion of a third canvas and the answer by @Soul_man in this post I had an idea that seems to work well. Instead of holding the original image in a third canvas (seemed cumbersome but perhaps I'm wrong - maybe this is worse memory wise?) I'm now storing the original image using getImageData and writing it into the canvas on each loop before I write in the new image at a higher and higher opacity level. The script is below and there is a jsbin here.

<canvas id="main" width="500" height="300" style="border: 1px solid #ff0000;"></canvas>
<canvas id="test" width="500" height="300" style="border: 1px solid #ff0000;"></canvas>

<script>

var width = 500, height = 300, text = "hello";


var main = document.getElementById('main');
var mainctx = main.getContext('2d');

//begin path, create a rect the size of the canvas
mainctx.beginPath();
mainctx.rect(0, 0, width, height);

//percentage to hex colour and fill canvas rect
mainctx.fillStyle = "#ff0000";
mainctx.fill();

//write text to canvas
mainctx.font = 'Bold 100px Arial';
mainctx.textAlign="center";
mainctx.textBaseline = "middle";
mainctx.fillStyle = '#ffffff';
mainctx.fillText(text, 0 + (width / 2), 0 + (height / 2));


var test = document.getElementById('test');
var testctx = test.getContext('2d');

//begin path, create a rect the size of the canvas
testctx.beginPath();
testctx.rect(0, 0, width, height);

//percentage to hex colour and fill canvas rect
testctx.fillStyle = "#00ff00";
testctx.fill();

//start opacity at 0
var op = 0;

//copy the existing main canvas image into memory
var originalimage = mainctx.getImageData(0, 0, width, height);


//fade canvas
function fade()
{
    //place the original image
    mainctx.putImageData(originalimage, 0, 0);

    //starting at 0 set the global alpha and then draw the new image
    mainctx.globalAlpha = op;
    mainctx.drawImage(testctx.canvas, 0, 0);

    //increment
    op += 0.01;

    if (op >= 1.0) setTimeout(function(){ console.log("done"); }, 2000);
    else requestAnimationFrame(fade);
}

fade();

</script>
garrettlynchirl
  • 790
  • 8
  • 23
  • 1
    Yes the third offscreen canvas should be more *performant* than putImageData, but obviously, this is a *should* as it depends a lot on the device and browsers implementation. So either way os fine. – Kaiido Jul 05 '18 at 23:44