HTML5 how to draw transparent overlapping spheres? - javascript

I have a complex HTML5 task..
I need to draw transparent overlapping spheres. Please have a look at:
http://imgur.com/7cCkt
I need to draw a sphere which is easy, But, I need to fill it as it look in the picture above (link) and make sure its semi-transparent..
The spheres has to move, so, one time the smaller sphere in the back can be animated and be bigger..
I know its complicated (it looks easy but its hard to program).
Any help will be appreciated!
Thanks!

You can set the globalAlpha value to get the transparency effect:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.globalAlpha = 0.5;
Something like this jsfiddle

If you want to draw transparent spheres use KineticJS:
http://jsfiddle.net/palani/mN2dC/

Related

Javascript draw border around shape on Image canvas

I am searching for a solution to draw a border around all shapes on an Image. I have already tried this solution Draw border around nontransparent part of image on canvas but this doesnt work for me.
Imagine this png
the solution I am looking for should look like this
is there any libary/solution?
Thank you
If you're drawing the all the shapes using ctx.fill(), you can just call ctx.stroke() before each call to ctx.fill(). This will result in a line of width ctx.lineWidth/2, since half of the line will be covered by the shape itself. However, his won't work for other methods such as ctx.drawImage() or ctx.putImageData(). Please specify how exactly you're drawing these shapes to the canvas to receive some more detailed help.
Edit: I think you can use the solution you already mentioned, you just need to make the non-black part of your image transparent. You can do this by editing the the imageData of the canvas:
var ctx = canvas.getContext("2d");
var imageData = ctx.getImageData(0,0,canvas.width,canvas.height);
for (let i=0;i<imageData.data.length;i+=4){
if (shouldBeTransparent(imageData.data[i],imageData.data[i+1],imageData.data[i+2]){
imageData.data[i+3] = 0;
}
}
ctx.putImageData(imageData,0,0);
function shouldBeTransparent(r,g,b){
return r!=0||g!=0||b!=0;
}
This will make all pixels that are not entirely black transparent, so you can continue with the method you already mentioned.

How to get a certain region of canvas

I have a complete canvas and I want to crop some area of canvas using pure JavaScript/jQuery.
This is something I want (crop some part of canvas):
ctx.clip() would help you. (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip)
var ctx=canvas.getContext("2d")
ctx.rect(x1,y1,x2-x1,y2-y1)
ctx.clip()
// draw something you want.
If you want to rectangle crop after drawing, ctx.drawImage would help you. (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)

Canvas - How to draw a growing trail

I want to draw a growing trail behind an moving object. I know this look quite easy :) But there are some constraints :
the trail must have some homogeneous transparency
and i can not use caching methods because of performance issues
I have tested 2 ways :
One with lineTo() and incrementing stroke width, but the alpha transparency is not homogenic...
https://jsfiddle.net/zOgs/9ntajsa1/
One with lineTo() and circles to fill the blank, transparency is OK but there is a strange behavior when drawing from left to right, negative space appears...
https://jsfiddle.net/zOgs/psa3x9y2/
I also try to use compositeOperation with something like this, but it's messing with my background...
trail.alpha = 0.5;
trail.compositeOperation = 'xor';
for(var i=nb; i>=0; i--) {
trail.graphics.drawCircle(points[i].x,points[i].y,size/2).closePath();
}
I can't find a valid solution to this problem and i am beginning to despair :(
There is probably a better way to do this, but here is an easy way: Use an off-screen canvas to draw the trails, then display that canvas as a bitmap child of the main stage.
Here is a fiddle based on your first one:
https://jsfiddle.net/lannymcnie/9ntajsa1/1/
// Canvas to draw to:
var offCanvas = document.getElementById("canvas2");
var offStage = new createjs.Stage(offCanvas);
// Add the offStage to the main stage.
var bmp = new createjs.Bitmap(offCanvas);
stage.addChild(bmp);
bmp.alpha = 0.1;
// Still get events from main stage
stage.addEventListener('stagemousemove',onMouseMove);

canvas clearrect, with alpha

So I know that context.clearRect makes pixels transparent, but I'm wondering, is there a function to make pixels translucent?
For example, say I have a canvas with these colors (fourth one in each color is alpha):
#ffff #feef #abff
#5f6f #000f #ffff
Running clearRect would resolve into this (or something, just make them all transparent):
#fff0 #fee0 #abf0
#5f60 #0000 #fff0
I want to remove opacity, but not make it transparent (kind of like globalAlpha for clearRect), so that it can end up like this (lets say I set the globalAlpha equivalent to 0.5):
#fff8 #fee8 #abf8
#5f68 #0008 #fff8
Is this possible? Or would it be simpler just to draw everything on an off-screen canvas, then draw that canvas (with globalAlpha set) on an on-screen one?
Let me know if this isn't clear in any way.
The answer above gets the job done, however getImageData is super slow and if you have a lot of other stuff going on it will slow down your animation immensely. If you create a second off screen canvas element you can set its global alpha to .9 and shuffle them back and forth and get the same effect with much greater speed.
context2.clearRect(0,0,width,height);
context2.globalAlpha = .9;
context2.drawImage(canvas1,0,0);
context1.clearRect(0,0,width,height);
context1.drawImage(canvas2,0,0);
context1.the rest of the drawing that you are doing goes here.
I just tried to figure this out too, and I've decided to count through the pixels, setting the alpha channel of each one manually. This is a bit more work, because I can't just cover the entire canvas with a rect, but it's working so far.
I'm doing this so that I can set a background image for the webpage and put my canvas animation over it, without having to draw the background in the canvas element.
var oldArray = context.getImageData(0,0,canvas.width,canvas.height);
//count through only the alpha pixels
for(var d=3;d<oldArray.data.length;d+=4){
//dim it with some feedback, I'm using .9
oldArray.data[d] = Math.floor(oldArray.data[d]*.9);
}
sw.context.putImageData(oldArray,0,0);

Canvas - Fill a rectangle in all areas that are fully transparent

I'm writing a simple 2D game engine using the HTML5 canvas. I've come to adding a lighting engine. Each light source has a radius value and an intensity value (0-1, eg 1 would be very bright). There's also an ambient light value that is used to light everything else in the world that isn't near a light source (0-1, eg 0.1 would be moonlight). The process of lighting is done on a separate canvas above the main canvas:
For each light source, a radial gradient is drawn at that position with the same radius as the light source. The gradient is given two stops: the center is black with an alpha of 1-intensity of the light and the end/edge is black with alpha of 1-ambient light value. That all works fine.
This is where it goes wrong :/ I need to fill the whole canvas with black with and alpha of 1-ambient light value and at the moment I do this by setting the context.globalCompositeOperation to source-out then fillRecting the whole canvas.
My code for this stuff is:
var amb = 'rgba(0,0,0,' + (1-f.ambientLight) + ')';
for(i in f.entities) {
var e = f.entities[i], p = f.toScreenPoint(e.position.x, e.position.y), radius = e.light.radius;
if(radius > 0) {
var g = cxLighting.createRadialGradient(p.x, p.y, 0, p.x, p.y, radius);
g.addColorStop(0, 'rgba(0,0,0,' + (1-e.light.intensity) + ')');
g.addColorStop(1, amb);
cxLighting.fillStyle = g;
cxLighting.beginPath();
cxLighting.arc(p.x, p.y, radius, 0, Math.PI*2, true);
cxLighting.closePath();
cxLighting.fill();
}
}
//Ambient light
cxLighting.globalCompositeOperation = 'source-out';
cxLighting.fillStyle = amb;
cxLighting.fillRect(0, 0, f.width, f.height);
cxLighting.globalCompositeOperation = 'source-over';
However instead of getting what I wan't out of the engine (left) I get a kind of reversed gradient (right). I think this is because when I draw the rectangle with the source-out composite operation it affects the colours of the gradient itself because they are semi-transparent.
Is there a way to do this differently or better? Using clipping maybe, or drawing the rect over everything first?
Also, I modified the Mozila Dev Centre's example on composting to replicate what I need to do and none of the composite modes seemed to work, check that out if it would help.
Thanks very much, any answer would be great :)
One trivial way would be to use imageData but that would be painfully slow. It's an option, but not a good one for a game engine.
Another way would be to think of the ambient light and the light-source as if they were one path. That would make it very easy to do:
http://jsfiddle.net/HADky/
Or see it with an image behind: http://jsfiddle.net/HADky/10/
The thing you're taking advantage of here is the fact that any intersection of a path on canvas is always only unioned and never compounded. So you're using a single gradient brush to draw the whole thing.
But it gets a bit trickier than that if there's more than one light-source. I'm not too sure how to cover that in an efficient way, especially if you plan for two light-sources to intersect.
What you should probably do instead is devise an alpha channel instead of this overlay thing, but I can't currently think of a good way to get it to work. I'll revisit this if I think of anything else.
EDIT: Hey! So I've done a bit of thinking and came up with a good solution.
What you need to do is draw a sort of alpha channel, where the dark spots mark the places where you want light to be. So if you had three light sources it would look like this:
Then you want to set the fill style to your ambient color and set the globalCompositeOperation to xor and xor the whole thing.
ctx.fillStyle = amb;
ctx.globalCompositeOperation = 'xor';
ctx.fillRect(0,0,500,500);
That will leave you with the "opposite" image except the transparent parts will be correctly ambient:
Here's a working example of the code:
http://jsfiddle.net/a2Age/
Extra optimization: You can actually achieve the effect without using any paths at all, by simply drawing the exact same radial gradients onto rects instead of circular paths:
http://jsfiddle.net/a2Age/2/
Hope that helps!
Just an idea, but since you're getting the opposite effect you're going for from your gradient, have you tried reversing the gradient?

Categories

Resources