Joey
Joey

Reputation: 10975

HTML5 Canvas alpha transparency doesn't work in firefox for curves when window is big

I'm drawing a curve on an HTML5 canvas and am using alpha transparency to create a glow effect, by drawing a thicker version of the curve underneath with an alpha of less than 1, then drawing a thinner version of the curve on top (and I'm doing this with several levels of recursion).

Okay here's the problem. It works exactly the way I want it to in Chrome, giving a beautiful glow effect. But in Firefox, the alpha doesn't render properly if my browser dimensions are bigger than around 300px in height (yes that sounds crazy but it is actually what it is doing for some reason). If I resize my browser to be extremely tiny, then all the sudden the alpha works and I get my awesome glow. Once I make the window a reasonable size, the alpha no longer works so instead of a glowing line I just get a really thick line. :( Code is below.

HTML:

<body>
    <canvas id="viewport">
    <script type="text/javascript" src="scripts/render.js"></script>
</body>

CSS:

* {
    background-color:#000000;
    padding:0px;
    margin:0px;
    width:100%;
    height:100%;
    overflow:hidden;
}

#viewport {
    border:0px;
}

Javascript:

window.viewport = document.getElementById("viewport");
window.context = viewport.getContext("2d");
window.xFactor = 1;
window.yFactor = 1;

function initializeViewport() {
    maximizeViewport();
    setFactors();
}

function maximizeViewport() {
    viewport.width = window.innerWidth;
    viewport.height = window.innerHeight;
}

function setFactors() {
    xFactor = window.innerWidth / 100;
    yFactor = window.innerHeight / 100;
}

function absX(x) {
    return Math.floor(x * xFactor);
}

function absY(y) {
    return Math.floor(y * yFactor);
}


function drawQuadraticCurve(startX, startY, controlX, controlY, endX, endY, lineWidth, gradient, alpha, glowiness, glowLevel) {
    glowLevel = (typeof glowLevel === 'undefined') ? 0 : glowLevel;

    // Draw the glow first
    if (glowLevel < glowiness) {
        drawQuadraticCurve(startX, startY, controlX, controlY, endX, endY, lineWidth + Math.sqrt(glowLevel), gradient, alpha*0.65, glowiness, glowLevel + 1);
    }

    // Then draw the curve
    context.beginPath();
    context.moveTo(absX(startX), absY(startY));
    context.quadraticCurveTo(absX(controlX), absY(controlY), absX(endX), absY(endY));
    context.lineWidth = lineWidth;
    context.strokeStyle = gradient;
    context.globalAlpha = alpha;
    context.shadowColor = "#FFFFFF";
    context.shadowBlur = 0;
    context.shadowOffsetX = 0;
    context.shadowOffsetY = 0;
    context.stroke();
}

    function createRadialGradient(colors, innerX, innerY, innerR, outerX, outerY, outerR) {
    var gradient = context.createRadialGradient(absX(innerX),absY(innerY),Math.min(absX(innerR/2), absY(innerR/2)),absX(outerX),absY(outerY),Math.min(absX(outerR/2), absY(outerR/2)));
    var gradientLength = colors.length;
    for (i=0; i<gradientLength; i++) {
        gradient.addColorStop(colors[i][0], colors[i][1]);
    }

    return gradient;
}


initializeViewport();
drawQuadraticCurve(80,65,20,70,70,10, 1,createRadialGradient([[0,"#FFFFFF"],[0.7,"#33CCFF"],[1,"#9944FF"]],50,50,1,50,50,90),1,8,0);

Screenshot of it working in Chrome: https://i.sstatic.net/tUagK.png

Screenshot of it NOT working in Firefox: https://i.sstatic.net/m0HDU.png

Screenshot of it working in Firefox after I've resized the window to be ridiculously small: https://i.sstatic.net/o2uGM.png

First working solution gets an upvote and a green checkmark! Yay!

Upvotes: 0

Views: 1619

Answers (2)

markE
markE

Reputation: 105035

This is really a comment, but it wouldn't fit in the space allocated to a comment. :-)

I've consulted the All-Knowing-Oracle of Html5 Canvas--the w3.org.

If you assign a zero shadowBlur (as you do) the spec says there should be no shadow applied.

That means that FF with the larger canvas size is correctly applying the w3 standard (not drawing any shadow) and both Chrome & FF(smaller version) are incorrectly applying a shadow when it should not.

http://www.w3.org/TR/2dcontext/

Shadows are only drawn if the opacity component of the alpha component of the color of shadowColor is non-zero and either the shadowBlur is non-zero, or the shadowOffsetX is non-zero, or the shadowOffsetY is non-zero.

Therefore, to have cross-browser compatibility, you mustn't rely on quirks in the rendering when shadowBlur=0. You must create your glow in another way within the "rules".

Upvotes: 1

markE
markE

Reputation: 105035

enter image description here

Here is a glowing quadratic curve made up of small, individual line segments--each segment being a different color. A shadowColor equal to the segment color causes the glow. The rendering is compatible across browsers (including FF).

(You can control the linewidth and the glow strength)

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

// variables to define colors -- use hsl instead of rgb
var hue=10;
var hueShift=4;

// define the quadratic curve
var startPt={x:350,y:100};
var controlPt={x:0,y:250};
var endPt={x:350,y:400};

// variables defining the starting & ending point of 
// the current line segment.
var newXY=startPt;
var oldXY=startPt;

// the current interval along the quadratic curve
// (used to calc an x,y along the curve)
// (t is kind-of like a percentage along the curve--kind of but not)
var t=0;

// the unshadowed linewidth
ctx.lineWidth=1;

// the shadow to apply around the line
ctx.shadowBlur=7;

// round the endcaps to visually blend the line segments
ctx.lineCap='round';

// start with a black-filled canvas
ctx.fillStyle='black';
ctx.fillRect(0,0,cw,ch);

// start the animation
requestAnimationFrame(animate);


function animate(time){ 

  // calculate a new x,y along the curve
  var T=t/100;
  var newXY=getQuadraticBezierXYatT(startPt,controlPt,endPt,T);

  // change the color for this segment
  hue=(hue+hueShift)%360;

  // draw this line segment with a shadow-glow
  glowLine(oldXY,newXY,hue);

  // set old=new in preparation for the next loop
  oldXY=newXY;

  // request another animation loop intil reaching 100
  if(++t<100){
    requestAnimationFrame(animate);
  }
}

function glowLine(oldXY,newXY,hue){
  // calculate the hsl color given the new hue
  var hsl="hsl(" + (hue % 360) + ",99%,50%)";
  // draw a glowing line segment
  // (==a line segment with a shadow of the same color as the line segment)
  ctx.beginPath();
  ctx.moveTo(oldXY.x,oldXY.y);
  ctx.lineTo(newXY.x,newXY.y);
  ctx.fillStyle= hsl
  ctx.strokeStyle=hsl;
  ctx.shadowColor=hsl;
  // overdraw the line segment so it really stands out
  for(var i=0;i<6;i++){
    ctx.stroke();
  }
}

// calculate an [x,y] along a quadratic curve given an interval T
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
  var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x; 
  var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y; 
  return( {x:x,y:y} );
}
body{ background-color:ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=500 height=500></canvas>

Upvotes: 1

Related Questions