I'm fairly new to javascript and I want to make a small line spectrum using html canvas and javascript so that I can later animate it when a button is clicked to show absorbtion lines and such just like in chemistry. I'm just not sure if pure javascript is able to handle this. Can anyone provide some information on how this could be done or if this is even possible? I can gladly provide more information if needed.
EDIT: Here is what I have so far but I don't know why the color gradient is slanting where the colors change and the widths of the colors need adjusted so they take up equal amounts along the rectangle.
HTML
<canvas id="spectralCanvas" width="600" height="100"></canvas>
JavaScript
function loadSpectralCanvas() {
var spectralCanvas = document.getElementById('spectralCanvas');
var spectralCtx = spectralCanvas.getContext('2d');
var backgroundCtx = spectralCanvas.getContext('2d');
spectralCtx.rect(0, 0, spectralCanvas.width, spectralCanvas.height / 2);
//Creates black underlay to represent absorption lines on the spectrum.
backgroundCtx.fillStyle = 'black';
backgroundCtx.fillRect(0, 0, spectralCanvas.width, spectralCanvas.height / 2);
//Creates the ROY G BIV line spectrum.
var gradient = spectralCtx.createLinearGradient(0, 0, spectralCanvas.width, spectralCanvas.height / 2);
gradient.addColorStop(0.14, '#882022'); //Red
gradient.addColorStop(0.28, '#E83B23'); //Orange
gradient.addColorStop(0.42, '#FFF101'); //Yellow
gradient.addColorStop(0.56, '#89C540'); //Green
gradient.addColorStop(0.70, '#1BBDC9'); //Blue
gradient.addColorStop(0.84, '#0279B9'); //Indigo
gradient.addColorStop(1.0, '#58235E'); //Violet
spectralCtx.fillStyle = gradient;
spectralCtx.fill();
}
loadSpectralCanvas();
JSFiddle
The last argument to the create gradient function should be 0, as this will produce a horizontal gradient that stretches the width of the canvas.
For your colour stops, you may be better off writing 1/7, 2/7 and so on to let JavaScript handle it for you.
It's definitely possible with pure JavaScript. The Mozilla Developer Network is a great resource to start with. They have a canvas tutorial that should be able to get you started in the right direction. Depending on how much experience you have you might consider going straight to the drawing shapes part of the tutorial. Good luck.
Related
So I have a semi-complex canvas drawing someone gave me. It draws an image vertically (i.e., top-down). Let's assume its a stick figure with facial features.
This is done in Javascript and Canvas. i.e.: ctx.beginPath(), ctx.moveTo(x,y), ctx.lineTo(1,1), etc.
I want the stick figure to move towards some point (x,y) and to face that direction while moving toward it. For example, if the x,y is near the bottom right, I want the stick figure to be oriented in a way such that its feet are facing towards the bottom right while it is moving.
The main question is, how would I go about doing this (i.e changing the stickman), knowing that I have a "hardcoded" drawing (in this example, stickman) that has been given to me?
You can render the received image on a separate canvas (doesn't need to be displayed) and use ctx.canvas.toDataURL() to convert it to an image. You could then embed the resulting image in your canvas and apply transforms to it more easily.
I mentioned this in a comment on the question but it sounded like fun, so I implemented a proof of concept.
var canvasObject = function(ctx) {
ctx.beginPath();
ctx.moveTo(0,0);
ctx.arc(30,30,15,0,2*Math.PI);
ctx.fillStyle='red';
ctx.fill();
return ctx;
}
var myCtx = document.querySelector('canvas').getContext('2d');
var objCtx = document.createElement('canvas').getContext('2d');
var renderedObjUrl = canvasObject(objCtx).canvas.toDataURL();
var renderedObj = document.createElement('img');
renderedObj.setAttribute('src', renderedObjUrl);
myCtx.drawImage(renderedObj, 30, 10);
<canvas id="myCanvas" width="600" height="400"></canvas>
I've been playing with canvas element and discovered that when I attempt to draw NxN uniform solid-colored cells next to each other, in some width/height configurations, there are blurry white-ish lines between them.
For instance, this canvas is supposed to look black but contains some sort of grid which I conjecture to be a result of faulty antialiasing in the browser.
Suffice to say, this bug appears only in some configurations but I would like to get rid of it for good. Is there any way to circumvent this? Have you ever had problems with antialiasing in canvas?
I have made this fiddle which demonstrates the issue and allows you to play with the dimensions of the canvas and number of cells. It also contains the code I use to draw the cells, so that you can inspect it and tell me if I'm doing anything wrong.
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
for (var i = 0; i < numberOfCells; ++i) {
for (var j = 0; j < numberOfCells; ++j) {
ctx.fillStyle = '#000';
ctx.fillRect(j * cellWidth, i * cellHeight, cellWidth, cellHeight);
}
}
Thanks in advance,
Petr.
jsFiddle : https://jsfiddle.net/ngxjnywz/2/
snippet of javascript
var cellWidth = Math.ceil(canvasWidth / numberOfCells);
var cellHeight = Math.ceil(canvasHeight / numberOfCells);
Depending on the width, height and your numberOfCells you are sometimes getting a... lets say 4.2 which is 4, however this would be displayed wrong and will allow a 1 pixel blank line to appear. So all you need to do is use the Math.ceil function and this will cause your cellWidth and cellHeight to always be the higher number and you won't get blank lines anymore
The best solution is to add a 0.5 pixel wide stroke around all the fills, using the same style as the fill and offsetting all drawing so that you render at the center of pixels rather than the top left.
If you add scaling or translation you will have to adjust the coordinates so that you still give the centers for your drawing coordinates.
In the end you can only reduce the artifacts but for many situations you will not be able to completely remove them.
This answer shows you how to remove the artifacts for an untransformed canvas.
How to fill the gaps
After reading through and trying several approaches, I've decided to come up with my own. I've created another (virtual) canvas which had integer dimensions corresponding to the number of cells in the grid.
After drawing all the cells in there, I call context.drawImage() on the main canvas and pass the virtual canvas as an argument along with offset and scale parameters to make it fit rest of my drawing. Assuming that the browser would scale the virtual canvas's image as a whole (and not as individual cells), I was hoping to get rid of the unwanted separator lines.
In spite of my efforts, the lines are still there. Any suggestions?
Here's the fiddle demonstrating my technique: https://jsfiddle.net/ngxjnywz/5/
I'm trying to create my first game to HTML5. And I search for hours like leaving a text persperctiva (for canvas).
See attached what I need. Are two "points" in text that needs to be modified to the effect that I need.
Image: https://pbs.twimg.com/media/BVbuU1PCUAA7d8a.png
PS: I managed to leave with only the text "rotation" (basic) and that is not right for my purpose.
All topics that I found say in response is not possible.
Canvas's 2d context can't do the non-parallel transforming that is shown in your link.
To do perspective-like warping, you will need to use the canvas 3d context (webGL).
Alternatively, here is a post on how to interpolate pixels from an original triangle into a distorted triangle:
http://codeslashslashcomment.com/2012/12/12/dynamic-image-distortion-html5-canvas/
This will allow you to "manually" do perspective distortions in 2d context.
It doesn't look like there's much perspective involved, so you might get away with a simple skew:
var angle = -0.2;
context.setTransform(1, 0, angle, 1, 0, 0);
context.drawImage(img, 100, 0, 350, 100);
http://jsfiddle.net/fTQcn/
I'm trying to understand the combination of HTML5/CSS3 and Javascript more and more.
That's why I thought, make a little project so you learn all about that more.
In short, I like the new iOS7 wallpaper and use it on my website (http://www.betadevelops.com). Then I thought, let's make this more lightweight and draw it with pure Javascript.
I started and managed to get quite far (http://www.betadevelops.com/jOS7.html). But now I face a stupid problem I can't seem to get fixed.
I draw circles on the canvas, and dynamically assign colors to it. But each time a new circle (and so a new color gets chosen) it automatically recolors the old circles...
So let's say, 10 circles:
1: blue circle, draw's it and done
2: yellow circle, draw's it and done, but it also colors the first blue one to yellow
I also wanted to add opacity and blurring. The opacity kinda works in the sense it has opacity on only 2-3 circles from the 20 I draw. I think this is not possible because I use Math.Random the calculate a random opacity.
Considered the blurring, I can add blurring to the whole canvas with follow code
canvas.style.webkitFilter = "blur(3px)";
but that's not what I want. I want the blur on the circle itself and to be more precisely, the outline. I read about it and it's not possible, but you can mimmick the looks with using CSS box-shadow.
So I tried
canvas.style.webkitFilter = "box-shadow(10px 10px 5px #888)";
but this also doesn't work it seems...
So, you website guru's. What am I doing wrong and can you help me out?
You can find the code by clicking on the second link. Uploaded it there.
EDIT:
Nevermind the blur, managed to solve it partially with this code
if (blurred) {
ctx.shadowColor = color;
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
#Stig Runar Vangen has the correct answer.
I would just add that if you don't intend the circles to "run", you could use ctx.closePath after drawing each ctx.arc.
ctx.beginPath();
ctx.arc(centerX, centerY, diameter, 0, 2 * Math.PI, false);
ctx.closePath();
color = color.replace('opacity', Math.random().toString());
ctx.fillStyle = color;
ctx.fill();
The reason why you see that all your circles gets the same color, might be because you join all circles into one draw operation. To separate each circle draw operation, start each circle placement with:
ctx.beginPath();
Each arc should then also be drawn with a call to either ctx.stoke() or ctx.fill() after the definition of each single circle.
This is purely guesswork as I haven't seen your code.
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?