i'm about to create small challenge for my team which is similar with challenge honeycomb candy in series Squid Game of Netflix.
The idea is user will use mouse click to make a dot around a shape, then i will use api of canvas context to draw another shape from those dots.
Example:
var canvas = document.body.appendChild(document.createElement("CANVAS")), context = canvas.getContext("2d");
context.beginPath();
// all points are given as x (from left to right), y (from top to bottom)
context.moveTo(10, 20); // x = 10 (10 px from the left edge of the canvas), y = 20 (20 px from the top edge of the canvas)
context.lineTo(100, 97); // x = 100, y = 97
context.lineTo(50, 105); // x = 50, y = 100
context.stroke();
finally i will compare this shape with the original shape, if they're same then user win.
anybody know how to compare two shape are same in js? or i need to compare each of dot of two shapes?
There is no straightforward way to compare shapes that are drawn on the canvas.
One possibility would be to store information about shape in const or array(with all shapes) and read the data of the shape that has been created by the user.
Suggested steps :
get your standard shape coordinates
get the coordinates of the points done by the user
compute the figure bounding box (min/max on x/y basically).
draw the shape and fill it with color (on hidden canvas)
set globalCompositeOperation of context to xor, and draw what would be the standard figure on this canvas.
<canvas id="visibleCanvas" width="630" height="500"></canvas>
<canvas id="hiddenCanvas" width="630" height="500" style="display:none;"></canvas>
//later in code
hiddenContext.globalCompositeOperation = 'xor';
that should create similar output:
ctx.getImageData(sx, sy, sw, sh);
of the canvas and count pixels.
compute the number out of non-null pixels. Generally speaking the more empty pixels the better the score is
Here is perfect art about canvas globalComposite operations https://techglimpse.com/canvas-types-source-destination-operations/
Related
I have a simple task - I need to draw one rectangle, rotate and copy it and clone its rotated version. I try to do it like so:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
//1. rotate canvas
ctx.rotate(-30 * Math.PI / 180);
ctx.rect(10, 60, 80, 40);
ctx.stroke();
//2. copy rotated rectangle
var img = ctx.getImageData(0, 0, 140, 140);
//3. rotate back
ctx.rotate(30 * Math.PI / 180);
//4. draw rotated version of the rectangle
ctx.putImageData(img, 80, 100);
The idea is very simple. At first step I draw a rectangle, at the second step I rotate my canvas and do what I think is a snapshot (or create a copy of my rotated rectangle), at the third step I rotate my canvas back (so my rotated rectangle must not be rotated any longer) and at the final step I add a new object to the canvas - the copy of the rotated rectangle. But this is what I get:
Whereas I expected to get this picture:
What am I doing wrong and how can I get the desired result?
"putImageData is not affected by the transformation matrix"
See here: putImageData() on rotated canvas work incorrect
Instead, if you want a complex image to be copied into memory, and then repeatedly drawn on the screen at different angles, you might consider using a temporary canvas. This post gives a good example:
How to rotate the existing content of HTML5 canvas?
If you need to create multiple image sprites, and creating multiple canvases won't do, consider drawing your sprite to an appropriately sized temporary canvas, and then convert the contents of the temporary canvas to an image using canvas.toDataUrl
The post gives a nice example:
https://davidwalsh.name/convert-canvas-image
I have a bit of code (involving "canvas"), which generates a graph on a four-quadrant cartesian plane. (Please see the JsFiddle link in the comment below.)
I want to create a bit of code that adds a point to a specific position on the plane. However, I want the point to get plotted based on the intervals on the x- & y-axes rather than pixels. In other words, I don't want to have to guess and check where each coordinate is on the graph and then adjust accordingly. If I move the graph 200 pixels down on the page, I want the point to likewise move 200 pixels down.
Coding novice, here (if you couldn't tell already). It took me forever to get to this point, so I would greatly appreciate any help anyone is willing to offer.
Thanks!
The 2D canvas context provides a transformation to all rendering.
You can set the matrix with ctx.setTransform and you can multiply the existing transformation with ctx.transform, ctx.scale, ctx.rotate, ctx.translate
Personally I am a big fan of ctx.setTransform(a,b,c,d,e,f); where
a,b is the unit length and direction of the X axis in pixels
c,d is the unit length and direction of the Y axis in pixels
e,f is the location of the origin relative to the top left and is in
pixels.
Basicly 2 vectors defining the size (scale) and direction of a pixel x and y axis, and a coordinate defining where on the canvas the origin is. The coordinate is not effected by the scale or rotation.
So if you want the X axis to point down and the scale to be two then
a = 0, b = 2 the Y axis is then b = -2, c = 0 to be 90deg clockwise from the X axis.
If you want the axis to remain the same but the scale scale = 2 changed then
a = scale,b = 0, c= 0, d = scale. And to have the origin at the center of the canvas e = canvas.width/2, f = canvas.height/2
Now if you draw an arc ctx.arc(0,0,100,0,Math.PI*2) you will see a circle in the center of the canvas with a radius = 100*scale
Hope that makes sense....
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.)
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
Lets say I draw a rectangle on the html canvas:
draw.rect(x, y, w, h, color); // color red
After I draw the rectangle I will draw a circle on the same canvas:
draw.circle(x, y, d, color); // color green
I have to randomly generate the coordinates for the circle.
Both draw functions are inside a loop - set interval - and a clear canvas function.
I am wondering if there is a way to make sure I won't draw the circle over the rectangle.
In a normal situation that would be easy, just remember the last coordinates of the rectangle and choose different ones for the circle - but for other reasons I cannot do it.
Would it be possible to check the canvas for the color of the rectangle which was drew on it, and make sure the circle will not be drawn over that color?
I know how to analyze the color of a background image, but I don't know if the above is possible.
For background images I use:
ctx.getImageData()
You will always be able to store the last drawn coordinates.
At least by using a glval var. which you can make not-so ugly
by using a namespace :
window.myApp = {};
myApp.lastDrawnRect = { x:-1, y:0, w:0, h:0 };
myApp.storeRect= function(x,y,w,h) {
var rect = myApp.lastDrawnRect;
rect.x = x; rect.y = y; rect.w = w;
}
and when you draw your rect you can store the coordinates :
raw.rect(x, y, w, h, color); // color red
myApp.storeRect(x,y,w,h);
You might want to store that the rectangle is not drawn by
taking the convention that x==-1 ===> rectangle cleared.
Then you can use that data when you draw your circle, with
classic boundary checking.
Whatever is drawn on the canvas is accessible using getImageData.
A canvas is a pixel matrix with drawing helpers attached.
Before drawing the circle you can ctx.getImageData(x, y, 1, 1) and check if it's red.
You most probably have to check the pixels on the edge of the circle, not the center.
Start from the circle equation.
It's been discussed before.