Reputation: 10975
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
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
Reputation: 105035
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