HTML5 canvas - controlling what happens when drawing on same place - javascript

How can i control what happens when i draw a line in the same coordinates as previous line?
Currently the color becomes brighter, but i want it to stay the same (or more accurately - i want the second line to on top of the first line).
I tried to do:
_context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
But it seems to work only when drawing canvas on canvas, not when drawing on the same canvas.

Supposing your color has a full alpha, your problem is probably related to the fact you draw lines of width 1 at integer coordinates.
Look at the following fiddle : http://jsfiddle.net/RAgak/
Drawing at integer coordinates makes the line wider and fuzzy. And drawing a second time makes it brighter. But this doesn't happen when I draw the line at half-integer coordinates.
var y = 10;
c.beginPath();
c.moveTo(0, y);
c.lineTo(30, y);
c.stroke(); // fuzzy
c.beginPath();
c.moveTo(50, y+0.5);
c.lineTo(80, y+0.5);
c.stroke(); // ok
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
See illustration : The first horizontal line was drawn with a y position of 1. This line is fuzzy and wide. The second horizontal line was drawn with a y position of 4.5. It is thin and precise.
The solution, at least when drawing horizontal or vertical lines (or rects), is to take the width of the line into account and draw at integer or half-integer coordinates.

Related

p5.js canvas drawing path with ellipses

I am working on a real time canvas drawing webapp using socket.io, node.js, and p5.js. I am having trouble creating a smooth line when the mouse is dragged. If the mouse is dragged too fast there is a trail of empty space in between each ellipse. The end goal here is to create a smooth path. Here are the things I have tried so far:
Attempt 1:
noStroke();
fill(lineColor[0],lineColor[1],lineColor[2]);
ellipse(mouseX, mouseY, lineThickness, lineThickness);
Attempt 2:
strokeWeight(lineThickness);
line(mouseX,mouseY);
stroke(lineColor[0],lineColor[1],lineColor[2]);
Here is a picture of what the issue looks like:
canvas drawing incomplete path image
any feedback is welcome; thanks!
Kevin's answer is great because it will be more efficient to draw lines instead of many ellipses. You should also look into:
beginShape()/endShape()
bezierVertex()
curveVertex()
curvePoint()
The above should help you draw a smooth path and setting a thicker stroke will looks as it many filled ellipses are connected forming the path.
If for some reason you do want to draw many ellipses, you can interpolate position when the mouse move faster and create gaps to fill those gaps.
For more information and p5.js example, check out this answer:
Processing: Draw vector instead of pixels
You could use the pmouseX and pmouseY variables, which hold the previous position of the cursor. Use that to draw a line from the previous position to the current position to fill in the blank space between mouse events.
From the reference:
// Move the mouse across the canvas to leave a trail
function setup() {
//slow down the frameRate to make it more visible
frameRate(10);
}
function draw() {
background(244, 248, 252);
line(mouseX, mouseY, pmouseX, pmouseY);
print(pmouseX + " -> " + mouseX);
}
<script src="https://github.com/processing/p5.js/releases/download/0.5.11/p5.js"></script>

Canvas 2D maze torch effect

I am working on a school project that includes these conditions:
Make maze with using only JS, HTML5 and CSS.
Make a torch effect around the character. You cannot light through walls.
I started making this game with the use of canvas.
I have succeeded to make a torch effect around the character as shown here:
http://people.inf.elte.hu/tunyooo/web2/HTML5-Maze.html
However, I cannot make it NOT to light through walls.
I am fairly sure I should do something like this:
Start a loop in all directions from the current position of the character up until it reaches the view distance OR if the context.getImageData() returns [0,0,0,255]. This way, I could get the character's distance from northern, eastern, western and southern walls.
Then, I could light the maze around the character with a (viewdistance-DistanceFrom*Wall) rectangle.
Unfortunately though, after 15 hours of thinking about this I am running out of ideas how to make this work.
Any tips are appreciated.
A simpler way of doing this is (ps: I get a "forbidden" error on the link provided so i cannot see what you did):
Have a matte version of the maze, a transparent and white image where white represent allowed drawing areas. This matte image should match the maze image in size and placement.
Create an off-screen canvas the size of the torch image
When you need to draw the torch first draw the matte image into the off-screen canvas. Draw it offset so the correct part of the matte is drawn. For example: if the torch will be drawn at position 100, 100 on the maze then draw the matte into the off-screen canvas at -100,-100 - or simply create the canvas the same size as the maze and draw in the matte at 0,0 and the torch at the relative position. More memory is used but simpler to maintain.
Change composite mode to source-in and then draw the torch. Change composite mode back to copy for the next draw.
Now your torch is clipped to fit within the wall. Now simply draw the off-screen canvas to your main canvas instead of the torch.
Note: it's important that the torch is made such as it cannot reach the other side of the wall (diameter size) or it will instead shine "under" the maze walls - this can be solved other ways though by using matte for different zones which is chosen depending on player position (not shown here).
To move in the demo below just move the mouse over the canvas area.
Live demo
function mousemoved(e) {
var rect = canvas.getBoundingClientRect(), // adjust mouse pos.:
x = e.clientX - rect.left - iTorch.width * 0.5, // center of torch
y = e.clientY - rect.top - iTorch.height * 0.5;
octx.drawImage(iMatte, 0, 0); // draw matte to off-screen
octx.globalCompositeOperation = 'source-in'; // change comp mode
octx.drawImage(iTorch, x, y); // clip torch
octx.globalCompositeOperation = 'copy'; // change comp mode for next
ctx.drawImage(iMaze, 0, 0); // redraw maze
ctx.drawImage(ocanvas, 0, 0); // draw clipped torch on top
}
In the demo the torch is of more or less random size, a bit too big in fact - something I made quick and dirty. But try to move within the maze path to see it being clipped. The off-screen canvas is added on the size of the main canvas to show what goes on.
An added bonus is that you could use the same matte for hit-testing.
Make your maze hallways into clipping paths.
Your torch effects will be contained within the clipping paths.
[ Addition to answer based on questioner's comments ]
To create a clipping path from your existing maze image:
Open up your maze image in a Paint program. The mouse cursors X/Y position are usually displayed as you move over the maze image.
Record the top-left and bottom-right of each maze hallway in an array.
var hallways=[];
hallways.push({left:100, y:50, right: 150, bottom: 65}); // for each hallway
Listen for mouse events and determine which hallway the mouse is in.
// hallwayIndex is the index of the hallway the mouse is inside
var hallwayIndex=-1;
// x=mouse's x coordinate, y=mouse's y coordinate
for(var i=0;i<hallways;i++){
var hall=hallways[i];
if(x>=hall.left &&
x<=hall.right &&
y>=hall.top &&
y<=hall.bottom)
{ hallwayIndex=i; }
}
Redraw the maze on the canvas
Create a clipping path for the current hallway:
var width=hall.right-hall.left;
var height=hall.bottom-hall.top;
ctx.beginPath();
ctx.Rect(hall.left,hall.top,width,height);
ctx.clip();
Draw the player+torch into the hallway (the torch will not glow thru the walls).
There is a brilliant article on this topic: http://www.redblobgames.com/articles/visibility/
Doing it accurately like that, however, is a lot of work. If you want to go with a quick and dirty solution, I would suggest the following. Build the world from large blocks (think retro pixels). This makes collision detection simpler too. Now you can consider all points within the torch radius. Walk in a straight line from the character to the point. If you reach the point without hitting a wall, make it bright.
(You could do the same with 1-pixel blocks too, but you might hit performance issues.)

Shape transformation - circular section

Is there any simple transformation (transformation matrix...) that will transform section A into section B? The only requirement is that both sections should have the same size X.
I plan to fill section A with an image and then to transform it into section B including the image. Right now I use javascript KineticJS but I am willing to change the framework if necessary.
Is that doable?
Your solution is not trivial.
Consider this illustration:
Lines radiate 360 degrees outward from a centerpoint and pass through both arcs A and B.
This illustration shows many more lines around the centerpoint.
Think of this as moving every pixel from A to a corresponding pixel in B. This uses linear interpolation (lerping) to move each pixel from A to B. The total number of colored pixels in A & B are the same--no pixels have been "magically" added to B.
Here’s code for this illustration:
var cx=150;
var cy=150;
ctx.lineWidth=1;
for(var a=0;a<Math.PI*2;a+=Math.PI/240){
for(var r=25;r<50;r++){
var x1=cx+r*Math.cos(a);
var y1=cy+r*Math.sin(a);
var x2=cx+(r+25)*Math.cos(a);
var y2=cy+(r+25)*Math.sin(a);
ctx.fillStyle="blue";
ctx.fillRect(x1,y1,1,1);
ctx.fillStyle="blue";
ctx.fillRect(x2,y2,1,1);
}
}
ctx.lineWidth=2;
ctx.beginPath();
ctx.arc(cx,cy,25,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx,cy,50,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(cx,cy,75,0,Math.PI*2);
ctx.closePath();
ctx.stroke();
The problem
Notice that most of arc A is filled with blue pixels while some parts of B are “moired” (not completely filled). That’s because A’s quantity of pixels will not completely fill B’s larger space.
Your image would appear the same. It would appear "correctly" in arc A but would have missing pixels in arc B.
What you need to do is “fill in the gaps in B” with appropriately determined pixels.
The solution
You can do this with an algorithm like bilinear interpolation which fills in the gaps in B by selecting a “best” pixel color. It does this by comparing 4 adjacent pixels in arc A. This algorithm enlarges a smaller image into a larger image by filling in "missing" pixels inside the larger space.
You will have to slightly adjust this algorithm to do non-axis-aligned interpolation. Here’s an link to a nice example of bilinear interpolation:
http://strauss.pas.nu/js-bilinear-interpolation.html

JavaScript canvas, getting flat lines

I'm working on project which have to draw graphs. Everything is quite good, but noticed one problem, the lines are showing strange.. it seems like someone draw my graph with brush holding horizontally.. when line goes down everything is OK, but when line is going horizontal the it becomes much smaller ... I can't find what's the problem could be..
Please help, because I started to get wrong graphs when it needs to draw horizontal line...
Here is the link to my project:
http://www.unolita.lt/images/signalai/Documents/Koreliacine%20funkcija.html
You can clearly see my problem on 1st picture..
Here is it's code:
function drawSignal()
{
var canvas = document.getElementById("canvSignal");
if (canvas.getContext)
{
var ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
function Signalas()
{
<...>
ctx.beginPath();
ctx.moveTo(x, y);
ctx.strokeStyle = "black";
<...>
y=250- Sn[n] ;
ctx.lineTo(x, y);
ctx.stroke(x, y);
<...>
To put all code here was too much problematic..
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
Possible Solution : If you have to draw a line with an odd numbered width, then you will have to offset the center of your line by 0.5 up or down. That way rendering will happen at boundary of pixel and not in middle, and you will always have a sharp line with no residue at the end edges.
So add 0.5 for odd numbered line width so that your points should be half numbered
ctx.lineTo(x+0.5, y+0.5);
ctx.stroke(x+0.5, y+0.5);
I have modified your code like this in line number 134 and 135 and got a output like this . Hope, this helps
Refer Here :
incorrect display lineWidth=1 at html5 canvas
HTML5 Canvas and Line Width
Line Width in Canvas

Drawing lines in canvas, but the last ones are faded

I'm trying to draw a grid of white lines on a black background.
The bottom 3 horizontal lines seem faded until I redraw them, and I can't figure out why this is happening. Has anyone seen this before and/or know what I'm doing wrong?
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
See illustration : The first horizontal line was drawn with a y position of 1. This line is fuzzy and wide. The second horizontal line was drawn with a y position of 4.5. It is thin and precise.
For example in your code, I had good results by changing your horizontal lines loop to this :
// Horizontal lines
for (var i = 1; i < objCanvas.height / intGridWidth; i++)
{
objContext.strokeStyle = "white";
var y = Math.floor(i*intGridWidth)+0.5
objContext.moveTo(0, y);
objContext.lineTo(objCanvas.width, y);
objContext.stroke();
}
Here's a fiddle demonstrating it with very thin and clean lines :
http://jsfiddle.net/dystroy/7NJ6w/
The fact that a line is drawn over all pixels it is over means the only way to draw an horizontal line with a width of exactly one pixel is to target the middle. I usually have this kind of util functions in my canvas based applications :
function drawThinHorizontalLine(c, x1, x2, y) {
c.lineWidth = 1;
var adaptedY = Math.floor(y)+0.5;
c.beginPath();
c.moveTo(x1, adaptedY);
c.lineTo(x2, adaptedY);
c.stroke();
}
Of course you'd better do it for vertical lines too to have a good looking page.
It doesn't look faded for me. Maybe it's something to do with your OS or PC, which is not able to render the drawing properly. I'm using Chrome 20 on Win 7. Test it out.
You have to define objContext.lineWidth like this:
objContext.lineWidth = 2;
I'm not sure why last line gets faded though.
See http://jsfiddle.net/jSCCY/

Categories

Resources