javascript - HTML5 Canvas: How to fake globalCompositeOperation="darker" - Stack Overflow

admin2025-04-20  0

I've googled and googled about this, and all I can find, including on StackOverflow, is "support was and is broken in most major browsers." Not an actual solution to my problem.

This month's Playboy came with a pair of 3D glasses (red/cyan) to view the eye-popping centerfold. Naturally, I hit the Internets to find every single red/cyan anaglyph I could and look at how awesome they are. Eventually I found some animated GIFs, which led to the idea that maybe I should make some cool HTML5 Canvas thing that lets you put shapes on a scene in 3D.

This is how far I got. Only works well in Google Chrome. In Firefox, the "Elevated Text" should look correct, but not the rectangles.

The way I'm generating the scene is thus: There are layers that each contain a Z-index, and you can place a rectangle or some text on whichever layer you want. The concept is simple. When drawing the object, it draws one [Z-index] pixels to the left in pure red, then it draws one [Z-index] pixels to the right in pure cyan.

In theory, the overlapping parts should subtract to bee pure black. In Chrome, this happens for filling rectangles, stroking text, but not for filling text. In Firefox, this only happens for stroking text.

Although the intended effect of globalCompositeOperation="darker" should do exactly what I want, it's obvious that going down this road is going to bring nothing but pain.

Does anyone here have an idea as to how I can get the effect I want without using globalCompositeOperation? I tried messing with the alpha channel on the colors but didn't really like how that came together (they never add up to pure black). I could draw a third black rectangle between the red and cyan ones, but that doesn't solve the problem for text or arbitrary shapes.

I could do the pixel-for-pixel rendering myself in the Javascript, but that just seems like overkill. Any thoughts?

I've googled and googled about this, and all I can find, including on StackOverflow, is "support was and is broken in most major browsers." Not an actual solution to my problem.

This month's Playboy came with a pair of 3D glasses (red/cyan) to view the eye-popping centerfold. Naturally, I hit the Internets to find every single red/cyan anaglyph I could and look at how awesome they are. Eventually I found some animated GIFs, which led to the idea that maybe I should make some cool HTML5 Canvas thing that lets you put shapes on a scene in 3D.

This is how far I got. Only works well in Google Chrome. In Firefox, the "Elevated Text" should look correct, but not the rectangles.

The way I'm generating the scene is thus: There are layers that each contain a Z-index, and you can place a rectangle or some text on whichever layer you want. The concept is simple. When drawing the object, it draws one [Z-index] pixels to the left in pure red, then it draws one [Z-index] pixels to the right in pure cyan.

In theory, the overlapping parts should subtract to bee pure black. In Chrome, this happens for filling rectangles, stroking text, but not for filling text. In Firefox, this only happens for stroking text.

Although the intended effect of globalCompositeOperation="darker" should do exactly what I want, it's obvious that going down this road is going to bring nothing but pain.

Does anyone here have an idea as to how I can get the effect I want without using globalCompositeOperation? I tried messing with the alpha channel on the colors but didn't really like how that came together (they never add up to pure black). I could draw a third black rectangle between the red and cyan ones, but that doesn't solve the problem for text or arbitrary shapes.

I could do the pixel-for-pixel rendering myself in the Javascript, but that just seems like overkill. Any thoughts?

Share Improve this question edited Dec 16, 2014 at 20:00 Levi Lindsey 1,0682 gold badges10 silver badges17 bronze badges asked May 16, 2010 at 21:37 jticklejtickle 511 silver badge5 bronze badges 1
  • This question is quite old. I wonder if now the new blending modes of the canvas would solve this issue, maybe the multiply mode (?) dev.w3/fxtf/positing-1/#blendingmultiply – GameAlchemist Commented Oct 18, 2014 at 10:23
Add a ment  | 

5 Answers 5

Reset to default 3

If you still need this, I have written a free context-blender library that lets you perform Photoshop-style blend modes between two canvases. I have not yet added 'darker', but you could either:

  1. Fork the project on GitHub, add your own support for darker (it's pretty easy to see how to add a mode) and then send me a pull request, or
  2. Ply me with promises of upvotes to get it added for you. :) The only hard part (as with many of the blending modes) will be attempting to determine what is correct when blending one or two areas which are <100% opacity.

It seems that the correct mode in Firefox is globalCompositeOperation="difference". Haven't tested in Chrome or IE.

Because "difference" is a mathematical operation, there is no ambiguity in the implementation, unlike the subjective term "darker".

Maybe you would like to use darken instead of darker. darker has been removed from the specification in 2007

It's a bit of a hacky way but it worked for me. You can invert the entire canvas by doing

ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);

Then render whatever you want to render using globalCompositeOperation = "lighter". Then invert the entire canvas again and it should give the same results as a "darker" blend mode would.

I tested the suggestion of Jespertheend, and it works well. The code is bellow :

// Simulate the non existing mand : 
// ctx.globalCompositeOperation = "darker";
// Draw with the plementary color,
// Draw with the plementary color, and with "lignter"
// Draw the whole rectangle with "difference"
// to obtain the equivalent of "darker"
ctx.globalCompositeOperation = "source-over";  // Default
ctx.fillStyle = "rgb(255, 0, 0)"; // opposé du cyan
ctx.fillRect(100, 20, 75, 50);
ctx.globalCompositeOperation = "lighter";
ctx.fillStyle = "rgb(0,255,0)";  // opposé du magenta
ctx.fillRect(120, 50, 75, 50); 

// To invert the colors
ctx.globalCompositeOperation = "difference"; // Non standard
ctx.fillStyle = "rgb(255,255,255)"; // == @ffffcc
ctx.fillRect(100, 20, 95, 80); 
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745081838a283907.html

最新回复(0)