views:

914

answers:

2

I have the following code which tries to combine a vertical mirrored image with a transparent to background color gradient. When combining these two effects it fails, do I need to overlay a PNG gradient over the canvas instead of trying to get the canvas to perform both operations?

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
    <meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
    <style type="text/css">
     body {
      background-color: #eee;
      text-align: center;
      padding-top: 150px;
     }
    </style>
    <script type="text/javascript">
     var img = new Image();
     img.onload = function() {     
      var ctx = document.getElementById("output").getContext("2d");
      // reflect image
      ctx.translate(0, 75);
      ctx.scale(1, -1);
      ctx.drawImage(img, 0, 0, 75, 75);

      // add gradient
      var grad = ctx.createLinearGradient(0, 0, 0, 20);
      grad.addColorStop(0, 'transparent');
      grad.addColorStop(1, '#eeeeee');

      ctx.fillStyle = grad;
      ctx.fillRect(0, 0, 75, 20);
     };

     img.src = "test.jpg";
    </script>
</head>
<body>
    <div><img src="test.jpg" height="75" width="75" /></div>
    <canvas id="output" width="75" height="20"></canvas>
</body>
</html>
A: 

Using a second canvas doesn't provide a very convincing gradient overlay. May have to go with a gradient PNG for decent results.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
    <meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
    <style type="text/css">
     body {
      background-color: #eee;
      padding-top: 150px;
     }

     canvas {
      float: left;
     }

     canvas#grad {
      margin-left: -75px;
     }
    </style>
    <script type="text/javascript">
     var img = new Image();
     img.src = "test.jpg";

     img.onload = function() {
      var reflect = document.getElementById("reflect").getContext("2d");
      // reflect image
      reflect.translate(0, 75);
      reflect.scale(1, -1);
      reflect.drawImage(img, 0, 0, 75, 75);

      // add gradient
      var overlay = document.getElementById("grad").getContext("2d");
      var grad = overlay.createLinearGradient(0, 0, 0, 15);
      grad.addColorStop(0, 'transparent');
      grad.addColorStop(1, '#eeeeee');

      overlay.fillStyle = grad;
      overlay.fillRect(0, 0, 75, 15);
     };
    </script>
</head>
<body>
    <div>
     <div><img src="test.jpg" height="75" width="75" /></div>
     <canvas id="reflect" width="75" height="15"></canvas>
     <canvas id="grad" width="75" height="15"></canvas>
    </div>
</body>
</html>
Greg K
A: 

There is no need for 2 canvas elements. The code below works.Tested it on FireFox 3.0 on Linux.

I have changed the canvas sizes so I could see it better while testing, I made the canvas 200 x 100 pixels. You will need to resize back to your needs (75x20). For testing purposes, I've made the overlay 100x100 pixels so I could see half the image with a gradient.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
<style type="text/css">
    body {
            background-color: #eee;
            text-align: center;
            padding-top: 150px;
    }
</style>
<script type="text/javascript">
    var img = new Image();
    img.onload = function() {
            var ctx = document.getElementById("output").getContext("2d");
            // reflect image
            ctx.translate(0, 100);
            ctx.scale(1, -1);
            ctx.drawImage(img, 0, 0, 200, 100);

            // add gradient
            var grad = ctx.createLinearGradient(0, 0, 0, 100);
            grad.addColorStop(0.3, 'rgb(255,255,255)');
            grad.addColorStop(0.7, 'rgba(255,255,255,0)');

            ctx.fillStyle = grad;
            ctx.translate(0,0);
            ctx.rect(0, 0, 100, 100);
            ctx.fill();
    };

    img.src = "img/avatarW3.png";
</script>

<canvas id="output" width="200" height="100" style="border:1px solid black;"></canvas>

Edit to explain some of this:

I think you were basically missing the alpha attribute on the gradient.

grad.addColorStop(0.7, 'rgba(255,255,255,0)');

The fourth parameter is alpha. Also, since you flip the canvas upside down, the gradient is currently drawn upside down (hence the second color stop has the transparency on it, and yet the block is transparent on the top side).

If you wanted to do things correctly, you would need to:

ctx.save();
ctx.translate(0, 100);
ctx.scale(1, -1);
ctx.drawImage(img, 0, 0, 200, 100);
ctx.restore();

And now you can draw your transparent gradient block as you need it.

Also, for me the ctx.fillRect() command did not work. I had to use ctx.rect() and ctx.fill().

Hope this helps.

Mike Mytkowski
Great thanks. Also tested this in Safari on Mac, so I guess it'll work in Chrome too.I ended up changing some of the gradient parameters to make the reflection look a bit more mac-esque. grad.addColorStop(0, 'rgba(238,238,238,0.8)'); grad.addColorStop(1, 'rgb(238,238,238)');
Greg K