I'm trying to draw an interactive map in Javascript, using Raphael to do the heavy lifting.
The map background is a fairly complicated thing containing a grid, the map elements, labels, etc. On top of this I'm then going to draw the stuff the user is actually working with. Because the background is complex, I don't want to have to rerender it every frame. So, after drawing it I would like to reuse those drawing elements, merely changing the translation, rotation, scaling of the background as the user pans, zooms, etc.
Unfortunately I'm rather confused by Raphael's transformation primitives: they're not behaving as I would expect. If I call scale(), the scaling appears to apply to the original size of the drawing element; but translate() is cumulative, so it applies to the previous translation. rotate() can be either, as it has an option I can set...
Is it possible to do absolute translation? That is, to be able to specify the absolute coordinates of the new center of my objects (which are usually paths)? Failing that, is keeping track of the old location so I can apply a delta when I want to move it to the new location a reasonable way of doing this?
Or would I be better off simply rerendering the whole thing every frame? (I see suggestions that Raphael isn't good at transformations of complex drawings, as most of it is done in Javascript; looking at the SVG that's being produced, I see that the translation appears to be getting backed into the path data, which would bear this out...)
(BTW, FWIW I'm using the GWT Raphael interface for all this.)
You can use Element.attr to set absolute positions. Just change x and y properties:
myElement.attr("x", myX);
myElement.attr("y", myY);
I've used the raphael-zpd plugin with success. I'm not sure if that will plug into GWT - you could check out their source code and adapt it to your use case.
Project: https://github.com/somnidea/raphael-zpd
Source: https://github.com/somnidea/raphael-zpd/blob/master/raphael-zpd.js
Related
I certainly don't expect anyone to actually provide a working solution for this. My question at this point is a simple one: can this be done with an HTML5 canvas, or would I be spinning my wheels in the attempt?
I'm a programmer, but my forte is in PHP, JavaScript, traditional HTML, etc. ...I haven't had a chance to play with HTML5 yet.
The elements you see in the example, I can save out as individually as necessary. So to make the blocks rotate around the center, I was thinking I save a square image with the block in the appropriate corner, respectively. Then rotating the image would pivot around center appropriately, unless you can set a point of origin on an image a la PhotoShop.
The KineticJS library looks promising for this type of animation as well, but I'll leave the recommendations to you fine folks.
Anyway, here is the example I want to replicate:
I won't give any library recommendations for the same reasons that #Diodeus points out, but maybe I can help your selection process. What you're trying to do can be done multiple ways in the browser right now: Canvas, SVG, and/or CSS3 animations.
Your example above is basically a few vector graphics composed together with a gradient on your center "pie timer". Because of this I would lean towards using S V G, especially if you want to allow interactions with your component (each SVG element can have event handlers).
The canvas element is better for "pixel by pixel" control of your visual content on the page. Adding content in the canvas doesn't grow the DOM (like with SVG) so it will normally perform better, but you lose things like native event handlers and animations that you will end up having to re-implement on your own.
More about the choice between SVG and Canvas, and an SVG animation example
Once you have the components in the page, they'll need to be wired up and animated. The animation can be broken down into:
Scaling
Background color fading
Rotation
"Weird gradient pie timer" example with CSS3
These can be done with CSS animations, SVG animations, or with plain old javascript. The choice depends on what you'll be animating. If I was selecting a library I would want to find one that tried to use the newer methods (SVG/CSS3) when it can, and gracefully degrade when it cannot.
I would be weary of libraries that try to re-implement things that are already available natively in the browser. Relying more on the browser instead of your own code to do things like animations means that the browser can optimize its operations and use things like hardware acceleration to improve your performance.
Hopefully this can aid your library selection. Remember, libraries come and go all the time so don't get too attached to one. An ideal implementation should allow for you to easily swap out your animation or display code without having to touch other unrelated pieces.
Sure, it's not all that difficult when you break it down into pieces.
Here are some technologies and techniques to get you started.
You use Canvas by (1) displaying some drawings, (2) erasing, (3) displaying some new drawings
When you do this redrawing rapidly, you get animated effects like your image shows.
Html Canvas uses a context to draw with (think of it as the pen for the canvas)
drawing a path: context.beginPath + context.moveTo + context.lineTo will define a path that creates your "fan blade" polygons. You can use context.fillStyle to fill the polygons with you colors.
fading: context.globalAlpha will change the opacity of new drawings
rotating: context.translate(centerX,centerY) + context.rotate(radianAngle) will rotate new drawings (like your rotating polygons, your tick-marks, )
scaling: context.translate(centerX,centerY) + context.scale(scaleX,scaleY) will scale your polygons.
arcs: context.arc(centerX,centerY,radius,beginningAngle,endingAngle) will draw an arc with a specified centerpoint and sweeping from a beginning angle to an ending angle.
math: circleCircumferenceX = centerX+radius*Math.cos(radianAngle) circleCircumferenceY = centerY+radius*Math.sin(radianAngle) uses trigonometry to calculate an xy coordinate on the circumference of a circle. You can combine this trig + Math.random to place your "speckles" in an arc around your centerpoint.
i'm learning Html5 Canvas for some weeks, but the problem above baffle me a long time.
An irregular shape, possible is a circle, Rect, ellipse, polygon , or a path which constructed by some lines and bezier curve...
I found some algorithm for some shape, like circle, rect, and polygon,but , if i used them in canvas, it will be so complex for those many many shape.
I also view some canvas libary,such as Kinetic.js, paper.js, fabric.js,etc, they all do this work well, but the code of they is so much and blend here to there, so I can't get main point...
but i found,all of them don't use the 'isPointInPath' method to do this work. why? if use this, I can do this work too!
kinetic.js , i knew him use the getImageData to determine, but strange, the imagedata its got is no alpha(alpha is always 255), but the shape its drew is semitransparent, oh no my brain is out of thought.
so i'm here want to know how determine a point is in a irregular shape(possibly it's semitransparent) in canvas, even a think way can help me.
and , is some preblem with the 'isPointInPath' method? thus no one use it?
I see no reason why you can't use the built-in functions:
var isInPath = context.isPointInPath(x, y)
For strokes you can use - strokes are separate as a path can be an open line or you can have stroke widths that expands outside the actual polygon:
var isInStroke = context.isPointInStroke(x, y)
Note that this will only work for the last path (after using beginPath()). If you need to iterate several paths (ie. shapes) you need to re-construct the paths (no need to stroke or fill though). This is perhaps a reason why some don't use it.
Transparency is not an issue as checking paths involves the vectors, not the rendered output of those (color information is not part of the check).
In the future you will be able to use the Path object directly. Currently this is not implemented in any browser which otherwise would make iterating a breeze; so re-constructing last path is the only way for now. As you don't actually have to draw anything when checking, the performance is acceptable unless there are a zillion objects to iterate.
I'm writing an JavaScript application that is drawing arbitrarily shaped objects on a HTML canvas. The user should be able to select any of the objects by clicking on them.
To make this an O(1) operation I'm using a shadow canvas, i.e. a not displayed canvas that has exactly the same size, where each object drawn on the normal canvas is also drawn there - but with a color that represents it's ID.
So a simple ghostContex.getImageData() together with the mouse click coordinates gives me the color at that pixel and thus the ID of the clicked object.
All of that is working fine - except when I click on the exact border of an object.
As it's drawn with anti-aliasing on the ghost canvas I get a wrong color (as that color is a mixture between the correct ID and the ID of the object under it that was drawn before...). This wrong color is representing a wrong ID and thus I'm selecting a totally different object :(
How can I solve that problem?
Note #1: I'm already using the translate(0.5, 0.5) trick to prevent most anti-aliasing
Note #2: I was trying to write this application with SVG before, but especially this object selection was extremely slow as I guess it's been too many objects for the collision detection. That's the main reason why I want a O(1) approach now... Oh, and this way I can easily draw a much bigger line on the ghost canvas than the line is drawn on the normal canvas to make picking much easier.
Note #3: Relevant browsers are Firefox, Chrome, Android 2.3+ native and iOS native
The reason why I couldn't accept any answer here is quite easy and quite sad: it doesn't exist... :(
The antialiasing can not be switched of, the standard has no method for that. But the standard does have a hit test function (http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/#hit-regions) that would do exactly what is needed here. Even in a nice way that would hide the nasty details for the developer - but it's not implemented in any of the browsers right now.
And implementation was looking to be far away till impossible (see e.g. comment #6 at https://code.google.com/p/chromium/issues/detail?id=328961). But apparently it gained momentum during the last month...
So what can be done in the mean time? What did I do?
In my code I could implement for each shape a isPointInShape() method. So I use the ghost canvas trick to get a shape and verify with the isPointInShape() that I really selected the correct shape. This helps in the anti aliased pixels not to pick a wrong shape (just think of clicking on the border of shape #2 where we had a 50% antialias transparency - this would tell you wrongly a selection of shape #1...).
If implementing a generic isPointInShape() is quite hard for your shape you could try a trick that I was reading of somewhere else (I didn't try it tough, so I haven't tested it...):
Create an additional ghost canvas of size 1x1 pixel that is positioned exactly on the mouse position. Then draw the shape of interest - when the A of the RGBA is changed, this shape does belong to that pixel.
I know this is an old post, but recently I had a similar issue. The way I solved the "seam of two colors" problem was doing a 10x10 pixel sampling of the secondary canvas instead of a single pixel. I then stringified the RGB values and used these as keys in a map that mapped to the object that color represents. So initially with the 1 pixel sampling I used the map immediately to determine the associated object but antialiasing created halfway colors that didn't exist in the map. The 10x10 method solves this problem by looping through the 100 RGB values returned and creating a "counting map." This map uses the stringified colors and maps them to a count, but only includes valid colors from the first map in the count. So you end up with a map saying you counted 65 red pixels and 23 blue pixels (where the remaining 12 pixels were some weird anti-alias hybrid). In the same loop where I was counting the colors I also maintained a variable for current max count and current color associated with that max count (to avoid looping through this new map again). Now at the end you have the color that was counted the most in that 10x10 sampling and can use that to map back to the object associated with it. You will only get an undefined result if no valid colors were found in the 10x10 sample which you can reasonably assume means the "background" was clicked.
I made up the name ghost context! Are you using my old tutorial? :)
In that old tutorial I do not clear the ghost context after each object is drawn to it. In your case, to fix your issue, you may need to clear after testing each object on the ghost context.
Make sure of course that you are translating the ghost context and normal context by precisely the same amounts. (and translating them back, or resetting the transformation, afterwards).
Is there any change to remove or resize selected object from Canvas without changing other design.
For example:- I drawn circles (just for help Circle1, circle2, circle3) .
circle1 will be bottom of other two circles. Now I want to remove circle2 or re-size. But it should not effect other circles.
And it there any change do this without using clear Canavas method.
It should work something like powerpoint design just draw and resize and delete.
I do not think that is possible, canvas is a bitmap object as far as I know and anything you draw on it updates the image.
If you like to use circle as an object you probably should look to SVG
Citation:
"once the rectangle is drawn, the fact that it was drawn is forgotten by the system. If its position were to be changed, the entire scene would need to be redrawn, including any objects that might have been covered by the rectangle."
You could try drawing the circles on separate canvases. In this case all you would have to do is to get rid of the Canvas element containing the circle itself.
Of course this means you'll have to do use some CSS trickery (namely z-index and absolute positioning)... It also incurs some overhead. This might be acceptable if you are dealing with an adequate amount of objects.
I agree with David about SVG. That might be a good option.
With Canvas you have to start setting up your own framework. I started a few simple tutorials on the subject, including resizing shapes.
In short, you're gonna have to start keeping track of each object you have drawn so you can re-draw them every time something moves.
One possibility is to use a canvas library like fabric.js, which allows you to draw and access canvas objects programmatically. Having canvas contents as a number of objects makes it easy to modify those objects dynamically, without affecting anything else; move, resize, delete, clone, change properties (color, opacity, etc.)
I'm writing an app for shape manipulation, such that after creating simple shapes the user can create more complex ones by clipping the shapes against each other (i.e. combining two circles together into a figure 8 stored using a single path rather than a group, or performing intersection of two circles to create a "bite" mark), and am trying to decide on a graphics library to use.
SVG seems to handle 80% of the functionality I need out of the box (shape storage, movement, rotation, scaling). The problem is that the other 20% (using clipping to create a new set of complex polygons) seems impossible to achieve without recreating SVG functionality in my own modules (I'd have to store the shape once for drawing inside SVG, and once for processing clipping myself). I could be wrong about SVG, but by reading about Raphael library (based on SVG), it seems like it only handles clipping using a rectangle, and even that clipping is temporary (it only renders part of the shape, but still stores entire shape to be rerendered once the clipping rectangle is moved). Perhaps I'm just confused about SVG standard, but even retrieving/parsing the paths to compute a new path using subsets of previous paths seems non-obvious in SVG (there is a Subpath() function, but I don't see anything to find the points of intersection of two polygon perimeters, or combine several subpaths into a single path).
As a result, Canvas seems like a better alternative since it doesn't introduce the extra overhead by keeping track of shapes I'd already have to keep track of to make my own clipping implementation work. Not only that, I've already implemented the polygon class that can be moved, rotated, and scaled. Canvas has some other issues, however (I'd have to implement my own redraw method, which I'm sure will not be as efficient as SVG one that takes advantage of browser-specific frameworks in Chrome and Firefox; and I'd have to accept IE incompatibility which is handled for free with libraries like Raphael).
Thanks
This may address what you're mentioning.
Clipping can be done using non-rectangular objects using the 'clipPath' element.
For example, I have element with id of 'clipper' that defines what to clip out, and a path that is subject to the clipping. Not sure if they intersect in this snippet.
<g clip-rule="nonzero">
<clipPath id="clipper">
<ellipse rx="70" ry="95" clip-rule="evenodd"/>
</clipPath>
<!-- stuff to be clipped -->
<path clip-path="url(#clipper)" d="M-100 0 a100 50"/>
</g>
This is just a snippet from something I have. Hope it helps.
Seems to me that you are trying to do 2D constructive geometry. Since SVG runs in retained mode, the objects you draw are stored and then the various operations performed. With Canvas you are running against a bit map so the changes are effected immediately. Since your users will in turn perform more operations on your simpler shapes to create ever more complex ones Canvas should in the long term be a better fit.
The only outstanding question is what will be done with those objects once your users are finished with them. If you zoom the image it will get the jaggies. SVG will avoid that problem but you trade-off with greater complexity and performance impact.
Both svg and canvas are a vector graphical technology.Each one having some different functionality.
Canvas
Canvas is a bitmap with an immediate modegraphics application programming interface (API) for drawing on it. Canvas is a “fire and forget” model that renders its graphics directly to its bitmap and then subsequently has no sense of the shapes that were drawn; only the resulting bitmap stays around.
More Information about canvas - http://www.queryhome.com/51054/about-html5-canvas
SVG
SVG is used to describe Scalable Vector Graphics
SVG is known as a retained mode graphics model persisting in an in-memory model. Analogous to HTML, SVG builds an object model of elements, attributes, and styles. When the element appears in an HTML5 document, it behaves like an inline block and is part of the HTML document tree.
More Information about SVG - http://www.queryhome.com/50869/about-svg-part-1
See here for more information about canvas vs svg in detail - Comparing svg vs canvas
You're right - you'll have to mathematically perform the clipping and creation of new shapes regardless of whether you use SVG or Canvas. I'm biased, it seems like it would be more useful to use SVG since you also get things like DOM events on the shapes (mouse, dragging) and serialization into a graphical format for free.