An app I'm working on has a hierarchy of nested SVG boxes that the user can manipulate with the mouse, and I'm trying to find the most efficient and/or maintainable way to keep track of the relative coordinate systems involved, convert between them when necessary, etc.
The intended interface is intuitive. I'll explicitly list the requirements:
Boxes can be moved, rotated and resized with the mouse.
When resizing, moving or rotating a box, its children fluidly move and rotate with it.
Boxes can be dropped into new parents.
Some line segments will cross between boundaries.
The mouse-event coordinates will have to synchronize fluidly with whatever local coordinate space it's working with.
[Main question] It seems to me this kind of interaction is desirable for many types of apps. Surely, I'm not the first person trying this. But I can't find a good library or set of best practices. The closest I've come across is interact.js, but it has turned out to be too incomplete and inflexible. Is there anything else out there?
[Sub-question] In case there's not a good library out there, I'm considering at least two approaches: (i) Have only one coordinate space, and calculate the coordinates of specific boxes recursively by having them consult their parents and then apply their local transformations. (ii) Use transform on the containing <g> of each box, so we get a hierarchy of nested coordinate spaces. The latter seems to be more maintainable, but could the former be more performant? Thoughts welcome.
Related
I'm developing a D3 application that utilizes a lot of text. Since there is alot of text elements, panning around using D3 zoom causes some lag. Is there a way in which I can improve the performance of my application? I'm happy to hear any suggestions, you guys might have. I've been thinking about paginating my data and detecting pan events but my UI is very complex and the user has the freedom of placing things the way he wants so I am not sure how to go about implementing a pan/pagination solution.
Such an open question is bound to get very opinion-based answers, and without more context you're also bound to get some useless ones, but here you go: it depends on what you want and what you mean by "a significant amount".
Supposing that significant amount is > 1000 elements, consider using canvas instead of SVG. Sure, if you can click on individual text nodes, that's more of a hassle, but it should be really good at panning/zooming.
If that is not possible, look at your code. Are you repositioning all text nodes individually? If so, place them all inside one g node and give that node a transform and zoom. In other words, make that node responsible for all global movement, and place the text nodes only relative to each other.
If that is also not possible, consider removing text nodes if they're outside the bounds of the SVG. Repositioning invisible nodes takes a lot of computation power, be smart about it. This is probably the most complex solution, so try it last.
I'm presently implementing a few features into a vector editor based off of fabric.js and have hit a bump in the road with how best to handle this scenario:
In order to resize/scale the canvas smaller and larger via scaling grippies/controls, I've begun the task of implementing a "fake canvas" that stays in the middle of the real canvas (the real canvas is resized to fill it's DOM parent at all times. This presents several issues, the biggest being, dealing with objects on the canvas, ensuring their coordinate are always relative to my "fake canvas" (which is just a Rect with some restrictions applied, e.g. locking movement, rotation, etc).
So I'm really just looking for strategies/suggestions at how to go about doing this. For instance, I know I'd be dealing with newly added objects, moving objects and preventing them to be moved outside of the boundaries of my fake canvas. Among other considerations.
Thanks for the input and suggestions. I'm not looking for code, I'm more just looking for suggestions of how best to handle all the canvas related things, transferred to a fake canvas. I see that the Shutterstock Editor does some of this too, so I guess it doesn't seem like it's out of the realm of possibilities.
My page is running a touchmove event which captures the position of the user's finger on the screen via:
xPos = e.originalEvent.touches[0].pageX;
yPos = e.originalEvent.touches[0].pageY;
The page has many layers (created with position:absolute divs) and at this point, I want to calculte how many such layers exist below the user's current position on the screen.
The only method I can think of is to have an array of all the layers' positions and loop through that. However that seems rather processor intensive when there may be hundreds of layers on screen at once.
Is there a simple way in js or JQuery to count the items that exist in a position, or a better practise way to do it than my array suggestion.
As far as I know, there is no such way. My approach would be to give all layers a certain class, select them all and iterate through them. Depending on what you are trying to achieve with this and how often you'll have to perform this calculation, it may be possible to use caching and approximation (e.g. not checking a certain pixel but an area of x^2 pixels and caching the result, etc) to make things run faster.
That being said, I encourage you to first try the solution that you've thought of and see how fast it actually runs. Browsers are pretty fast for such standard operations (think layer drag & drop with boundary checks), so I'm pretty confident that it won't be as slow as you think it will be :)
If I have a canvas with a circle that changes color upon clicking on it, I can use a click event on the canvas element and handle the math for that (distance formula calculation <= radius). But what if I have two circles that overlap (like a van diagram), and I click in the middle of the two circles assuming that only the top circle should change color? If the math of the first circle is applied in this case, both circles would change color.
How would I deal with events in the canvas in terms of overlapping objects like the example above? With hopefully a fast/efficient algorithm?
You might want a framework like EaselJS that has a better api for what you're trying to do. Barebones canvas 2d-context doesn't provide much in terms of display-object / sprite behavior.
Responses above also mention some sort of list to represent layers. I don't think the implementation would be very difficult, just another condition to check for along with the radius.
Canvas isn't really like Flash or like a DOM tree whereby things have sort orders or z-indexes. Its a bit more like a flat rastered image and you have to rely upon other logic in your javascript to remember the sequence & stacking order of things you have drawn.
If you need this kind of interactivity I've always found it best to use a 3rd party library (unless it really is just a case of one or two circles which dont do much).
For interactive 'shape' based javascript graphics I would sugest Raphael.js or D3 which are actually more of SVG tools than a canvas one so maybe it's not for you but they are simple and cross-browser.
There's also processing.js (js port of Processing the Java lib) which feels a bit like flash and again can track all of the levels and objects. Theres a tonne of others but thats another topic.
If it's super simple the options might be:
Hold the co-ordinates of all shapes/elements composited on the canvas inside an object or array which also tracks their z-index/sort sequence, thereby letting you know whats on top.
Using the imagedata at the mouse coordinate of the click to establish what has been clicked
using multiple canvases composited on each other and letting the DOM do the work for the click events
If you need to work on different layers on HTML5 canvas what is the best way of doing it? I see some people decide to stack a number of canvases ontop of one another using position: absolute. Is this the best way?
Personally, I wouldn't stack canvas' on top of each other. A canvas is really just a bitmap displaying all the pixels you need so you should only need one for most cases.
I would suggest using a library to help you manage different objects. I have found that Grant Skinner's EaselJS is a breeze to work with.
This library lets you easily group objects and add them to the canvas, it also makes it trivial to add mouse listeners to capture clicks on objects etc. which is something that you would have to write lots of code to do when using canvas without a library.
There is documentation and examples on EaselJS also.
EDIT :
Here is an extract from the docs regarding the container used to group objects.
A Container is a nestable display lists that allows you to work with compound display elements. For example you could group arm, leg, torso and head Bitmaps together into a Person Container, and transform them as a group, while still being able to move the individual parts relative to each other. Children of containers have their transform and alpha properties concatenated with their parent Container. For example, a Shape with x=100 and alpha=0.5, placed in a Container with x=50 and alpha=0.7 will be rendered to the canvas at x=150 and alpha=0.35. Containers have some overhead, so you generally shouldn't create a Container to hold a single child.