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();
Related
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.
I am working on a big project where exercises in Canvas are created through JSON-data and CreateJS. The purpose of having it in HTML 5 is to not have to use a separate app for your phone, you can always use the website.
Everything works fine, however in mobile the Canvas is rescaled to full screen. This is done through checking the screen size, and if it's small enough to be mobile the canvas is scaled through this code:
// browser viewport size
var w = window.innerWidth;
var h = window.innerHeight;
// stage dimensions
var ow = canvasWidth;
var oh = canvasHeight;
// keep aspect ratio
var scale = Math.min(w / ow, h / oh);
stage.scaleX = scale;
stage.scaleY = scale;
// adjust canvas size
stage.canvas.width = ow * scale;
stage.canvas.height = oh * scale;
This works great for most of the exercises, like quizzes and such, where all you have to do is click on a button. However we also have some drag and drop-exercises, and an exercise where you can color a drawing. These of course rely on the mouse coordinates to work properly. The problem is, when the canvas is scaled the mouse coordinates are not. So when you drag an item or try to draw, there is an offset happening. So your drawing appears way left of your click, and when picking up a draggable object it doesn't quite follow your click correctly.
Had I made the code from the beginning I'm fairly sure how I would have recalculated the coordinates, but since they are calculated by CreateJS I don't really know how I should go about this.
This was reported as a problem by someone about a year ago here, where this solution was suggested:
I was able to work around this by adding a top-level container and attaching my Bitmaps to that and scaling it.
The whole exercise is inside a container which I have tried to scale but to no avail. I have also tried sending the scale as a parameter to the parts of the exercise created (for example the menu, background images and such) and not scale it all together, and it seems to work okay since then I can exclude the drawing layer. But since it is a large project and many different exercises and parts to be scaled it would take quite some time to implement, and I'm not sure it's a viable solution.
Is there a good and easy way to rescale the mouse coordinates along with the canvas size in CreateJS? I have found pure Javascript examples here on SO, but nothing for CreateJS in particular.
Continued searching and finally stumbled upon this, which I hadn't seen before:
EaselJS - dragging children of scaled parent. It was exactly what I was looking for. I needed to change the coordinates I drew with this:
var coords = e.target.globalToLocal(e.stageX, e.stageY);
Then I could use the coords.x and coords.y instead of directly using e.stageX and e.stageY like before.
I have checked this question which provides the perfect answer. But my problem is slightly different. I have a canvas of 300 x 300 and i am re-sizing the canvas using css to 200 x 60. If i re-size the canvas using css i am not able to get the color value onmouseover.
In the re-sized fiddle if you mouse over right below the red or blue rectangles you will notice it still says #FF0000 & #0000FF respectively while it should be #000000. So how to make it work even with re-sized canvas?
Fiddle: Re-sized with css.
Fiddle: Non re-sized.
You need to apply a scale factor inside the mouse handler method. The scale factor is the relationship between your canvas's bitmap (actual size) and the element size (CSS size).
For example:
// find scale:
var sx = example.width / parseInt(example.style.width, 10);
var sy = example.height / parseInt(example.style.height, 10);
// apply to x/y
x = (x * sx)|0; // scale and cut any fraction to get integer value
y = (y * sy)|0;
Updated fiddle
In addition the code need to have some boundary check of the coordinates so getImageData() won't fail (not shown here).
I am using a jquery plugin that utilities canvas to draw up spot motion animations.(http://arena.palamago.com.ar/spotMotion/)
I know that in this instance i can use animated GIF images, but the image types i use in future will be requiring higher quality and transparency.
If you look at the jsfiddle below you will see the images are not sharp, i am on a retina display and they look even worse, the original image is 800px. Canvas is not scaling the images high enough fo some unknown reason. I am fairly new to canvas and have seen a few methods for up scaling but have had no luck in getting a better result.
I looked at canvas width and canvas style width
canvas.width = "200";
canvas.height = "200"; // allow 40 pixels for status bar on iOS
canvas.style.width = "100px";
canvas.style.height = "100px";
I also looked at css image rendering techniques
canvas { image-rendering:optimizeQuality;}
http://jsfiddle.net/TsAzP/1/
Another attempt but i just cant seem to intergrate it with this plugin.
function enhanceContext(canvas, context) {
var ratio = window.devicePixelRatio || 1,
width = canvas.width,
height = canvas.height;
if (ratio > 1) {
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
context.scale(ratio, ratio);
}
}
I have seen some very complicated methods with people writing up-scaling algorithms, i just dont understand how to put it together. If anyone knows how to improve image quality please spare me some time.
Thank you
Problem
The cause is that the source image is too large to reduce in size in a single down-scale.
The browser typically uses bi-linear interpolation over bi-cubic interpolation when it comes to the canvas element.
Bi-linear interpolation analyses 2x2 pixels while bi-cubic uses 4x4 (in down-sampling functioning as a low-pass filter to create an average pixel). If the image size reduction is too steep there is simply not enough pixels to consider for averaging and the result will be in part "choppy" or pixelated.
Solution
To solve you can do one of the following steps:
Prepare the image(s) at a smaller size in an image editor (for example Photoshop) and scale the image to the destination size you want to use (ref. retina display).
Process the image on client before drawing it by creating an off-screen canvas and scale down the image in 2-3 steps.
The first step could be a better solution for a variety of reasons such as:
Processing of the image (for size) does not happen on client
Quality in resulting image (easier to post-process)
Bandwidth reduction (less data to transfer)
Faster processing of the image (in use) on client
Saves on battery (less processing involved)
As for the second step: There's too much code in the fiddle (TL; TR), but the principle is as follows (and it's not so complicated):
/// create two temporary canvas elements
var ocanvas = document.createElement('canvas'), /// off-screen canvas
tcanvas = document.createElement('canvas'), /// temp canvas
octx = ocanvas.getContext('2d'),
tctx = ocanvas.getContext('2d');
Then we do a first step-down scaling of the image - for this example we will do it twice which is the minimum needed. You might need a third step if the size difference is huge (you can normally calculate this by using a function of log etc., but I'll leave that out of the um, "equation" here):
/// use temp canvas (tcanvas) to scale for the first step
tcanvas.width = img.width * 0.5; /// 50% allow good result with bi-linear
tcanvas.height = img.height * 0.5;
/// draw image into canvas
tctx.drawImage(img, 0, 0, tcanvas.width, tcanvas.height);
The next step is just as simple as the above but with an absolute size:
/// set destination size
ocanvas.width = 200;
ocanvas.height = 200;
/// draw temp canvas into canvas
octx.drawImage(tcanvas, 0, 0, ocanvas.width, ocanvas.height);
You can now use ocanvas in your solution instead of img.
We use two canvases as we want to use the final ocanvas to replace img directly later at the proper size. If we used one canvas we would have to resize it in the final step which mean the canvas would be cleared.
If you do need a third step then you can reuse one of the canvases.
The added advantage here is that the browser won't need to scale anything when animating which reduces the load on the CPU/GPU.
I suggest also doing this down-scaling inside a function so that the temporary canvas references (except the one you need to use pf course, which you need to return) can be easily discarded by the browser after use (GC/memory wise).
Hope this helps!
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();