Adding a grid on canvas bring some lag, optimisations? - javascript

That's the code:
http://jsfiddle.net/Mgq79/3/
for (var xgrid = 0; xgrid < (canvasWidth / grid_step); xgrid++)
{
ctx.moveTo(xgrid * grid_step, 0);
ctx.lineTo(xgrid * grid_step, canvasHeight);
}
for (var ygrid = 0; ygrid < (canvasHeight / grid_step); ygrid++)
{
ctx.moveTo(0, ygrid * grid_step);
ctx.lineTo(canvasWidth, ygrid * grid_step);
}
Long story short - after adding the grid, noticed some lag while rendering at small velocity values (<=150m/s) while it was almost smooth near upper 1000m/s limit. So how to optimise this part?
Thanks
ps link updated
Also note that lag is very noticeable when trajectories are 3 or more

For me works about the same. It's only 1 ms difference which I'm sure you can't really notice.
I for one would really want to skip drawing that grid over and over and I would make the canvas transparent and have the grid as a repeating background image of the canvas container.
Or draw it once on a temp canvas and then use drawimage and just move it over to your canvas if redrawing it is necessary for some reason.
Also, you should use a path to draw the graph and not 1x1 rectangles. The path can be finished after all the points are there. Drawing and filling a rectangle at every iteration is bound to be slow.

Related

Drawing multiple rotated images next to each other

I'm trying to draw multiple rotated images next to each other on an HTML5 canvas, but unfortunately there's always a gap between them:
The idea is that I want to draw multiple blocks of different sizes using tiles. Each separate block can be rotates. When drawing such a block, I call the canvas's rotate function once to then draw all the tiles next to each other to form such a block.
At this point in time I don't really know what to do to solve the issue. Ofcourse I could use an offscreen canvas for each seperate block, but to my knowledge this will create some serious performance issues in my game since there can be a lot of these blocks, each with their own sizes, tiles and rotation.
Another option would be to use patterns, for this specific scenario that would be a viable option. But unfortunately I also need to draw blocks where the tiles on the edges get a different image.
What would be a good way to get rid of these gaps? Shall I just draw extra tiles in between to fill the gaps in a bit of an hacky way. Or is there an option I haven't thought of yet?
Edit:
JSFiddle: https://jsfiddle.net/Oli414/oxap9fgr/2/ (It applies to drawing in general, not just images).
ctx.rotate(rotation * Math.PI / 180);
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
ctx.fillRect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
Steps:
1.: Create a fillStyle with the image, using ctx.createPattern(Image, wrap), and set wrap as "repeat" (Don't forget to wait before the image loads.
2.: Instead of drawing each image in a separate drawImage call, just use ctx.fillRect(beginX, beginY, width, height). This also has a better performance for bigger tiles.
There shouldn't be any gaps between the images.
If you don't like this idea, then you could simply oversize the image by 1 pixel in each direction.

enlarging polygon without scaling in fabricjs

I have a page where initially is loaded an SVG and then for each path of the SVG is applyed a background image in a way similar to the one showed in the dynamic pattern demo.
At the same time, I want to enlarge the loaded SVG to screen size. I have used the setScaleX() method on all the SVG's elements but the background image appears distorted, probably because of the scaling ratio.
So I've tried to use the setWidth() method, calling it on the original value of the element multiplied by my ratio. This solution works with an SVG composed of only "Rect", but when there are some polygons inside they're not enlarged, only the containing box is updated.
I can understand this, setting the width of a polygon necessarily means updating the coordinates of its points, but this is the effect I want to obtain.
So, there's a way to enlarge a polygon in fabricjs without using setScaleX() method?
After long time I've found a solution for this problem. You can find and explanation of the FabricJS behaviour in this github issue I made.
The way I've found to enlarge a polygon without scaling it in FabricJS is to move every single point of the polygon, multiplying it by the scale ratio needed.
There's the code I'm using.
var paths = canvas.getObjects();
var ratio = 2;
for (var c=0; c<paths.length; c++) {
var p = paths[c];
p.setWidth(p.getWidth() * ratio);
p.setHeight(p.getHeight() * ratio);
if (p.points) {
for(var j=0;j< p.points.length; j++) {
p.points[j].x *= ratio;
p.points[j].y *= ratio;
}
}
p.setLeft(p.getLeft() * ratio);
p.setTop(p.getTop() * ratio);
p.setCoords();
}
canvas.renderAll();

Using KineticJs to update and rescale a drawing

I am having an issue trying to update a drawing that uses a number of different drawing objects. The drawing is similar to an AutoCad drawing and is measured in mm so the scale is already being calculated in order to get the drawing to fit on the stage. When this is calculated the scale is set to one.
I have the drawing objects (lines, circles, arcs, etc.) stored in an array. I am trying to update the drawing andd rescale it without clearing the stage and doing a full redraw to improve performance.
What I am trying to do is to increment the length of the drawing. The drawing has a cut point so the objects to the right of the cut point will move by the increment value and any lines that span the cut point will increase in length. This change will require an update to the scale initially calculated to get the drawing to appear on the stage.
There are 2 Fiddles that I have set up to demonstrate the problem. The first (http://jsfiddle.net/tctruckscience/3HxuP/4/) shows what I am currently doing. The problem is that drawing will scale but it will start to move away from the right hand side of the screen.
originalRectWidth = 2600;
rectWidth = rectWidth + 20;
scaleValue = originalRectWidth / rectWidth;
oldRectWidth = rect.getWidth();
newPixelsPerScaleUnit = 260 / rectWidth;
newRectScaled = rectWidth * newPixelsPerScaleUnit;
drawingGroup.setWidth(newRectScaled);
rect.setWidth(newRectScaled);
drawingGroup.scaleBy(scaleValue);
Also, if I make a lot of increments and then make a large decrement using a text box there is an issue in the redraw. It that lines are not decremented correctly. I think it is an issue with the scaling. When I resize the page, which calls a refresh of the drawing objects from the array in which they are held, the drawing proportions are correct
The second Fiddle (http://jsfiddle.net/tctruckscience/rsEyA/15) shows how I would like the drawing to behave.
oldWidth = drawingGroup.getWidth();
newWidth = drawingGroup.getWidth() + 20;
scaleValue = originalWidth / newWidth;
Is there a way to do this?

HTML5 Canvas smooth scroll tilemap

I've tried to build a scrollable tilemap with the canvas object which automatically calculates it's size relative to the screen resolution and renders the tiles relative to a position on a (bigger) map.
I planned to move the generated tiles and only draw the tiles that need to be drawn new.
So that means I scroll the map, draw the new tiles at the top (for example on moving upwards) and deleting the last tiles on the botton which are now out of the visible canvas.
My problem is:
I don't think that it's very good for the performance to change the position of every tile in the canvas, I think this could be solved using getImageData() and putImageData() but there is still one problem left:
If i just move these tiles and draw new tiles it will always "hop" for 30px (1 tile = 30x30), so is there a simple / performance technically good way to make this with a smooth, linear scroll effect?
Just fill the board wither using drawImage or pattern (the latter require you to use translate to get the tiles in the right position).
drawImage takes new destination size as argument so you can zoom the tiles too.
Here is a pattern implementation I made for some other question, but I assume you should be able to see what goes on in the code:
function fillPattern(img, w, h) {
//draw the tile once
ctx.drawImage(img, 0, 0, w, h);
/// draw horizontal line by cloning
/// already drawn tiles before it
while (w < canvas.width) {
ctx.drawImage(canvas, w, 0);
w *= 2;
}
/// clone vertically, double steps each time
while (h < canvas.height) {
ctx.drawImage(canvas, 0, h);
h *= 2;
}
}
The performance is good as you can see in the implementation this was used for (video wall with live scaling and tiling).
To project this more to what you have - instead of drawing each tile as above you can simply draw the canvas to a new position and fill in the new "gap":
ctx.drawImage(myCanvas, offsetX, offseY);
fillGap();
You could have used clipping with drawImage but the canvas will clip this for new internally so there is no gain in clipping the image in JavaScript as you move part of it outside the canvas.

Fastest algorithm to draw a crossword grid in <canvas>?

I'm rendering a grid of cells, very much like the grid you find in a crossword puzzle, but using four different colors to fill each cell (not only black or white).
The grid size is about 160x120, and I need to render it as fast as possible, as it will be used to display a Cellular automaton animation.
I have tried two different approaches to render the grid:
Render each cell using something like:
var w = x + step;
var h = y + step;
canvasContext.fillStyle=cell.color;
canvasContext.fillRect(x+1,y+1,w-1,h-1);
canvasContext.strokeRect(x,y,w,h);
Render the all of cells without the border, and then render the grid lines using:
var XSteps = Math.floor(width/step);
canvasContext.fillStyle = gridColor;
for (var i = 0, len=XSteps; i<len; i++) {
canvasContext.fillRect(i*step, 0, 1, height);
}
//Similar thing for Y coord
Both algorithms perform poorly: it is slower to draw the grid than the cells in both cases. Am I missing something? How can I optimize those algorithms? Is there another way I should try?
Note: the grid moves, as the user can displace it or zoom the view.
The general question will be: what is the fastest algorithm to draw a grid of cells on a element?
The fastest way to do something is to not do it at all.
Draw your unchanging grid once on one canvas, and draw (and clear and redraw) your cellular automata on another canvas layered above (or below) that. Let the browser (in all it's native compiled optimized glory) handle dirtying and redrawing and compositing for you.
Or (better) if you are not going to change your grid size, just create a tiny image and let CSS fill it as the background.
Demo of CSS Background image to Canvas: http://jsfiddle.net/LdmFw/3/
Based on this excellent demo, here's a background image grid created entirely through CSS; with this you could change the size as desired (in whole-pixels increments).
Demo of CSS3 Grid to Canvas: http://jsfiddle.net/LdmFw/5/
If you must draw a grid, the fastest will be to just draw lines:
function drawGrid(ctx,size){
var w = ctx.canvas.width,
h = ctx.canvas.height;
ctx.beginPath();
for (var x=0;x<=w;x+=size){
ctx.moveTo(x-0.5,0); // 0.5 offset so that 1px lines are crisp
ctx.lineTo(x-0.5,h);
}
for (var y=0;y<=h;y+=size){
ctx.moveTo(0,y-0.5);
ctx.lineTo(w,y-0.5);
}
ctx.stroke(); // Only do this once, not inside the loops
}
Demo of grid drawing: http://jsfiddle.net/QScAk/4/
For m rows and n columns this requires m+n line draws in a single pass. Contrast this with drawing m×n individual rects and you can see that the performance difference can be quite significant.
For example, a 512×512 grid of 8×8 cells would take 4,096 fillRect() calls in the naive case, but only 128 lines need to be stroked in a single stroke() call using the code above.
It's really hard to help without seeing all the code to know where the performance is going, but just off the bat:
Instead of drawing a background grid using stroke, can you draw it using one call to drawImage? That will be much faster. If its truly static then you can just set a css background-image on the canvas to an image of the grid you want.
You're using fillRect and strokeRect a lot and these can probably be replaced with several calls to rect() (the path command) and only a single call to fill at the very end. So all the filled cells are rendered at once with a single filling (or stroking or both) command.
Set the fillStyle/strokeStyle as little as possible (not inside loops if you can avoid it)
You are using fill to draw the lines; it would be faster, I think, to define a path and stroke it:
canvasContext.beginPath();
var XSteps = Math.floor(width / step);
canvasContext.fillStyle = gridColor;
var x = 0;
for (var i = 0, len = XSteps; i < len; i++) {
canvasContext.moveTo(x, 0);
canvasContext.lineTo(x, height);
x += step;
}
// similar for y
canvasContext.stroke();

Categories