i'm a beginner in Javascript so please bear with me.
Basically I'm making a sandbox drawing facility for a website using Javascript. And this is done using the canvas. What I need to do is to be able to resize the canvas dynamically but at the same time keep everything on the canvas to scale.
I don't think this question has been asked before. It seems trivial but I currently have all my objects on the canvas defined in absolute coordinates. I also have mouse events to use to draw things. And when I want to enlarge the canvas (by doubling the size say). All the objects inside won't be enlarged properly to scale and the mouse coordinate system would be messed up too.
Only solution i can think of is add a scale factor to ALL my drawing parts, but this is very tricky with a lot of code. Is there a better way?
If you don't mind jaggies on your double-sized canvas drawings then you can simply use CSS to double-size your canvas. Then divide every incoming mouseEvent coordinate by 2.
If you don't want jaggies on your double-sized canvas then:
Double-size the canvas element: canvas.width*=2 and canvas.height*=2 This automatically erases all canvas content.
Scale up the canvas: context.scale(2,2)
Redraw all your drawing parts using the unchanged original coordinates. A happy note: you do not have to scale any of your drawing coordinates -- context.scale automatically does that for you.
Divide every incoming mouseEvent coordinate by 2.
Related
I'm trying to create a small website using the FabricJS library, which adds additional features to the web canvas element.
My issue that I, however, have is that i want to resize the canvas (in red) so that it fills the whole webpage.
On this canvas, there is a background image (in green) where I'll create some drawings on (in orange, this could be lines, squares,...).
Now, I would like to export all drawings in a coordinate system relative to the image and not to the whole canvas, because it should be possible to freely move around and zoom in/out the image for an enhanced drawing experience.
My idea, on how to solve this, would be to calculate the image position relative to the canvas and subtract them from the drawings - but that includes a lot of calculation.. Maybe there is a more genius approach with FabricJS?
Moreover, how can i guarantee that my drawings move around and zoom in/out with the image, so that my drawings are always true to the image?
I've thought about this for days and came to the realization that i need input from the professionals.
I think toLocalPoint() might help. Given an object imageObj and absolute coordinates left and top of your drawing, you can find the relative coordinates like this:
const abs = new fabric.Point(left, top)
const rel = imageObj.toLocalPoint(point, 'left', 'top')
console.log(rel.x, rel.y)
As for your second question: there is no easy way to "tie" two objects together, other than grouping them - and I assume you don't want to group them. Therefore, you would need to listen to all the appropriate events emitted by one object and make the adjustments to the other object in their handlers. To find out what events make sense to listen to in your case, see the events demo.
so i have been trying to work on a canvas sketching app. and i had this problem where the line drawn are very unclean. As you can see from image below.
then i tried this and got the below result which is what i desire.
But this created a new problem, the eraser function wont work. Now I've been trying to make this two work for sometime, but just could'nt do it.
So what i want is a new smoothing technique or a way to make the above two work.
BTW here is the demo without smoothing: http://jsbin.com/axarun/1/edit
and here is the demo with smoothing: http://jsbin.com/aviluk/2/edit
Thnx in advance.
Method 1:
You can "force" the canvas to use anti-aliasing by doing this:
ctx.translate(0.5, 0.5);
Force in quotes as this is dependent on actual implementation in the browser you're using. Currently there is no way to enable/disable anti-alias by intention.
Method 2:
Set the canvas to "high-resolution" by setting the canvas itself to double size, but force it to be displayed in the original size by styling the canvas element with CSS rules:
<canvas id="myCanvas" width="1600" height="1200"
style="width:800px;height:600px" />
Just remember to double your mouse-coordinates as well as pen-width. This will eat 4x more memory and performance, but will give you high-resolution lines (in appearance).
In this case the browser will treat the canvas as an image and apply anti-aliasing as it does for any scaled image, and not the canvas' method for anti-alias.
Please note that this is not the same as applying scale transform to the canvas.
See demo of "hi-res canvas" here:
http://jsfiddle.net/q2t5A/
Method 3-ish:
For actual smooth lines you can check out my function to do so here:
http://www.codeproject.com/Tips/562175/Draw-Smooth-Lines-on-HTML5-Canvas
This takes an array of x/y couples and smooths the line (cardinal spline). The lines will go through the original points and no control points are needed.
This will of course imply that you record the actual points, and when mouse up you redraw the canvas using the curve() function in this code. That would be a correct approach in any case, to record the strokes in an array and then redraw (this will also allow you to use layers). There are workarounds to avoid render everything by using off-screen
canvases to store f.ex. each layer. By drawing a canvas onto another with a small non-integer offset will force anti-alias (but see point 1).
I am trying to transform a non-uniformed portion of image in a canvas element to a proper rectangle (ie. Like taking a non-rectangle portion of an image in photoshop and using Distort to make it rectangle), but am having difficulty understanding Canvas Matrix Transforms.
I am not after code, just a point in the right direction, in terms of understanding how I could achieve this.
Cheers
Update 1: Incase I didn't explain well enough http://i.imgur.com/QTB6q.png
Update 2: The those boxes are an area inside a photo which was added to the canvas, essentially I am cropping and straightening a portion of the image.
In html5 canvas you can apply only affine transformations using transform or setTransform() methods, so you need to know what is the matrix applied already and using the inverse matrix to make it look like a square area, for other transformations (non-affine), it is needed some math knowledge (that I don't have), create or search for an implementation for Canvas API (which works only with affine transformations).
I'm trying to draw a tiled background using Javascript on an HTML5 canvas, but it's not working because shapes that intersect the edges of the canvas don't wrap around to the other side. (Just to be clear: these are static shapes--no motion in time is involved.) How can I get objects interrupted by one side of the canvas to wrap around to the other side?
Basically I'm looking for the "wraparound" effect that many video games use--most famously Asteroids; I just want that effect for a static purpose here. This page seems to be an example that shows it is possible. Note how an asteroid, say, on the right edge of the screen (whether moving or not) continues over to the left edge. Or for that matter, an object in the corner is split between all four corners. Again, no motion is necessarily involved.
Anyone have any clues how I might be able to draw, say, a square or a line that wraps around the edges? Is there perhaps some sort of option for canvas or Javascript? My google searches using obvious keywords have come up empty.
Edit
To give a little more context, I'm basing my work off the example here: Canvas as Background Image. (Also linked from here: Use <canvas> as a CSS background.) Repeating the image is no problem. The problem is getting the truncated parts of shapes to wrap around to the other side.
I'm not sure how you have the tiles set-up, however, if they are all part of a single 'wrapper' slide which has it's own x,x at say 0,0, then you could actually just draw it twice, or generate a new slide as needed. Hopefully this code will better illustrate the concept.
// Here, the 'tilegroup' is the same size of the canvas
function renderbg() {
tiles.draw(tiles.posx, tiles.posy);
if(tiles.posx < 0)
tiles.draw(canvas.width + tiles.posx, tiles.posy);
if(tiles.posx > 0)
tiles.draw(-canvas.width + tiles.posx, tiles.posy);
}
So basically, the idea here is to draw the groupings of tiles twice. Once in it's actual position, and again to fill in the gap. You still need to calculate when the entire group leaves the canvas completely, and then reset it, but hopefully this leads you in the correct direction!
You could always create your tillable image in canvas, generate a toDataUrl(), and then assign that data url as a background to something and let CSS do the tiling.. just a thought.
Edit: If you're having trouble drawing a tillable image, you could create a 3*widthx3*width canvas, draw on it as regular (assuming you grab data from the center square of data as the final result), and then see if you can't draw from subsets of the canvas to itself. Looks like you'd have to use:
var myImageData = context.getImageData(left, top, width, height);
context.putImageData(myImageData, dx, dy);
(with appropriate measurements)
https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas/
Edit II: The idea was that you'd have a canvas big enough that has a center area of interest, and buffer areas around it big enough to account for any of the shapes you may draw, like so:
XXX
XCX
XXX
You could draw the shapes once to this big canvas and then just blindly draw each of the areas X around that center area to the center area (and then clear those areas out for the next drawing). So, if K is the number of shapes instead of 4*K draws, you have K + 8 draws (and then 8 clears). Obviously the practical applicability of this depends on the number of shapes and overlapping concerns, although I bet it could be tweaked. Depending upon the complexity of your shapes it may make sense to draw a shape 4 times as you originally thought, or to draw to some buffer or buffer area and then draw it's pixel data 4 times or something. I'll admit, this is some idea that just popped into my head so I might be missing something.
Edit III: And really, you could be smart about it. If you know how a set of objects are going to overlap, you should only have to draw from the buffer once. Say you got a bunch of shapes in a row that only draw to the north overlapping region. All you should need to do is draw those shapes, and then draw the north overlapping region to the south side. The hairy regions would be the corners, but I don't think they really get hairy unless the shapes are large.... sigh.. at this point I probably need to quiet down and see if there's any existing implementations of what I speak out there because I'm not sure my writing off-the-cuff is helping anybody.
I'm currently implementing a HTML canvas based webapp that features panning. Is there a way to use an auxiliary buffer to hold the presently visible area so when I pan I don't have to redraw the whole canvas and only have to draw the newly visible areas?
See my previous response to a related question: What is the fastest way to move a rectangular (pixel) region inside a HTML5 canvas element
Just draw the entire canvas in a div that has overflow:hidden and implement panning as re-positioning the top and left of the canvas within that div. It is much faster. And don't worry about drawing canvases tens of thousands of pixels wide/tall, I've successfully used this on very-very large and complex HTML and SVG elements.
Take a look at the pixel manipulation API.
http://dev.w3.org/html5/2dcontext/#pixel-manipulation