Javascript "Jigsaw puzzle" with irregular .png shapes - javascript

Soooo, what I'm trying to do is essentially a jigsaw puzzle. On the left side is an area with a stack of a dozen or so overlapping .png files of "ripped" paper pieces (all different irregular shapes with alpha) that when put together, in order on the right side, form a sheet of paper with notes on it. A visual (disregard rotated pieces):
Nothing new I know, but my two main caveats in developing this are that it has to work in ie8+ and on touch devices. So this means no flash (where this would of been very easy) and no canvas (stupid ie); which leaves me with js and maybe svg?
The drag and drop part in js is easy enough but the issue I'm having is that, as I'm sure you know, the alpha channel is ignored and the irregular ripped piece is actually a rectangle. Which makes this unusable given that pieces are a stack with pieces overlapping each other.
I can do granular hit detection on the drop by using an image map and setting a variable on rollover.
I've looked around a lot (here + google) and tried some ideas but I'm unable to solve the irregular shape select/drag issue. Any ideas?
Thanks for your time.

Low-tech solution
Nested elements with absolute position
If you're willing to spend a little extra time, there's a way to achieve this fairly closely, without having to use Flash, Canvas, SVG, or even image maps. And unlike an image map, it allows you to nest related content inside each hotspot if needed (e.g., pop-ups).
In the simplest case, you could use a single rectangular hyperlink hotspot for each piece of the puzzle. Obviously that greatly limits the range of shapes you can support (without interfering with overlapping elements).
But, if you take that hyperlink tag and give it a number of span tag children, and give each one absolute position (relative to the hyperlink), and apply the appropriate portion of the image to the background, then you can "construct" irregular image shapes that occupy a single irregular hotspot, with relatively-little interference with overlapping elements.
In effect, the image (with areas of transparency) is treated as a sprite file, with the hyperlink tag and the child span tags each occupying one portion of the "sprite file". Most of the transparent parts of the image will not be occupied by the hyperlink tag or the span tags.
Most shapes can probably be built using a hyperlink tag and 4 - 10 spans. Granted, the more irregular the shapes are, the more spans it will tend to require.
I've done this before, to create hotspots for each of the states on a US map, without using an image map (or Flash, Canvas, SVG), and it wasn't nearly as problematic as you'd think. It just requires a bit of time to figure out the details of how to break up each shape into the right number of rectangles.
The catch
Rounding error on mobile devices
Here's the catch, and it's a doozy. When a web page is scaled on mobile devices (and a regular page is almost always scaled on smaller devices), that introduces a rounding error that causes the px placement of the hyperlink tag and the span tags to possibly vary by at least 1 pixel horizontally and/or vertically. This also happens if desktop browsers are scaled; it's just that desktop browsers are not often scaled.
What would happen is that you would tend to wind up with 1 pixel or so of separation (or overlap) between the different parts of each shape. In many cases, that will tend to be very obvious and not acceptable visually. And depending on the implementation, the locations can vary by as much as 2px or 3px. When it occurs it's difficult to solve, and there are limits to how much of it can be solved.
Last I checked, Firefox is the only browser that's smart enough about rounding px values on scaled pages to avoid this problem. Hopefully other browsers will eventually support it better, as even simple pages often suffer from rounding errors.
The solution
Separate the images from the hotspots
The rounding error isn't much of a problem with hotspots (where precision isn't important). Where it really causes problems is with images (when you see an image not lining up where it should).
It may be possible to avoid the worst of the image rounding errors by doing the following:
Have one set of HTML code for the hotspots, same as described above, except don't display any part of the images in the hotspots. Give them all transparent backgrounds.
Have another set of HTML code for the images. Each one would be a single rectangular element that displays all of the image.
Place each image at the same position as the related hotspot.
Make sure the set of hotspots and the set of images both have the same z-index order. All of the hotspots will be on top of all of the images, but within the hotspots and within the images the order needs to be consistent.
When the hotspot for a piece is dragged, update the position of the related image to keep them at the same location. In effect, the image shadows the hotspot while it's being dragged.

Isn't ExplorerCanvas an option for you? I would think that that would allow you to create a canvas-based solution. That will be much better than anything done with plain html element manipulation through javascript

Related

Fabric.js Efficiency of Clipped Sprite vs. Individual Images

I intend to have a FabricJS canvas that will display potentially dozens of different graphics, which could either be served up as individual image files or a sprite. For each graphic, it's possible that particular graphic would be displayed on the canvas 0 to dozens of times. I know that FabricJS has the clipping ability, which would make the use of sprites possible, and I know that generally speaking, sprites on the web are highly preferred over individual images, such as for CSS.
However, I've also heard of browser behaviors/quirks surrounding canvas where some optimizations that you might expect to take place, don't -- I can't find the link at the moment, but one that I recall was how large canvas-content elements out of view in Chrome would still take a considerable amount of time to be calculated/"rendered", despite not being relevant to on-screen appearance.
So, are there any known expectations on whether n individual non-clipped images would be preferable/unpreferable to a sprite that's clipped n times for a FabricJS canvas? In case it matters, this would not be a heavily animated FabricJS canvas; things may move when dragged, for example, but constant animation is not what's involved in this case.
Just do a test yourself to find out what slows down your PC. It depends on the PC and browser and graphics card.
I don't know anything about Fabric.js, but computer CPUs and graphics cards can handle clipping/graphic manipulation easily. You're not displaying millions of polygons like a 3D game, so you should be fine.
Here's a website that explains the CSS sprites vs individual images network performance increases:
https://medium.com/parlay-engineering/emoji-at-scale-render-performance-of-css-sprites-vs-individual-images-f0a0a2dd8039

Can I do a 1000% zoom with JavaScript vector art?

I'm planning to commission a developer to help me create a simple mathematical art piece. I'm wondering if the following can be accomplished with JavaScript vector art, and if not, what approach you would recommend.
The image will start off with some intersecting lines forming a shape. This is essentially an image zoomed in 1000% or more, and the user can scroll to zoom out until the full image fits the width of the screen.
Naturally, an actual image of this size would be huge, so I'm thinking it would be better to draw it programmatically, which might also enable the line thickness to scale up a bit as you zoom out, so that they're not nearly invisible when zoomed all the way out. The image can not look pixellated when zoomed all the way in, but achieving this with some “trickery” like swapping out images is also ok.
Example:
Basically the reverse of zoom.it but at a significantly larger scale.
http://zoom.it/
Some libraries I've looked at are:
paper.js
fabric.js
leaflet.js
raphael.js
How can an extreme zoomout like this be accomplished?
You are definitely on the right track. I think what you want is very possible on the web using HTML5. You are correct in that the easiest/best performing implementation of this would be using vector graphics. You can use image tiles, however the preprocessing and bandwidth requirements for tiling get large very quickly.
Here are some of my thoughts from working with some of the libraries you listed:
Leaflet.js - Leaflet supports both drawing SVG elements as well as image tiling (if you wanted to go that approach). Leaflet is also "mobile first" in that it supports things such as pinch zooming and double tap to zoom out of the box. Scroll zooming is also supported out of the box. Getting something up and going with Leaflet is simple. As far as I know Leaflet is writing SVG to the DOM; which is something to keep in mind.
Raphael - Raphael is capable of what you want, however you may need to implement zooming aspects yourself. This is definitely possible to do and shouldn't be too difficult, but something to keep in mind. Raphael will write SVG elements to the DOM; which can get a bit unruly if you have many many SVG elements. However, you may be able to optimize this and create/destroy elements as you are zooming.
Paper and Fabric - These both appear to render SVG to Canvas (different than writing SVG to the DOM). These both look really powerful, and seem to have good APIs for zooming. You would likely still need to hook up scroll/touch gestures to get zooming to work the way you want. These both should perform very well as they are using lower level APIs which should bypass many issues you might have with doing this in the DOM.

Javascript/JQuery Calculation of Dimensions

I am looking to achieve something like this. A HTML view has a finite number of images (shown as red boxes in the image below). Are there any browser/jQuery APIs available today (cross-browser) which will let me calculate the dimensions of the remaining space (shown in green boxes) quickly? In the example shown below, it is easy to calculate the green area dimensions using simple geometry given the dimensions of the red boxes. But I am talking about very complex scenarios and complicated combination of images.
Appreciate any help. Thanks.
If you every images have absolute property, you can calculate dimension through top and left properties like $('#elementID').offset().top and $('#elementID').offset().left
From my experience working with DOM element dimensions, you cannot rely on them for exact values, and certainly can't really on them for the same values cross-browser. You can get OK results, but if you have complex scenarios then you will probably come undone at some point.
One way I have achieved similar things in the past is by drawing images to HTML5 Canvas. Using canvas you can have very fine-grained control. I have even iterated canvases pixel-by-pixel to get pixel perfect measurements of items on the canvas.
Check out this tutorial for a brief overview of drawing an image.
UPDATE
There is no easy way to do it. Using this method is low-level and will require you to use mathematics, and possibly byte-level image data from the canvas. However, if your problem is as complex as you suggest then you will have to get stuck in. When I did something similar I was also looking for an easy way to achieve what I wanted in the browser, then spent a month getting to grips with the canvas API, learning about byte-level colour data etc, but in then end I got what I needed, and ended up with something quite unique as it was difficult to achieve in a browser.
To get started, first I would say look at implementing a layered canvas by absolutely positioning multiple canvases on top of each other, then drawing a single image on each one. You already know the sizes of the images, and you can decide the coordinates of where to draw the image, so that's a start. In fact that may be all you need, you can track each image as you draw them by storing coords and dimensions, and you should be able to build up an accurate picture in numbers of where all your images are in 2D space.
Using those numbers you should then be able to calculate any empty spaces on there. However, that is a beyond me and probably a question for Mathematics Stack Exchange (which is actually down at the moment :D).

SVG text hit-test

I'm trying to implement collision detection for SVG text elements using client side JavaScript. The hit-test should check if any glyph of a text overlaps any glyph of another text element. Since getBBox and getExtentOfChar are anything than accurate I need a custom solution.
My first approach was to get the colour of each coordinate/pixel of an element and do the hit-testing manually, but this does not work because it isn't possible to get the colour of a coordinate. It would require an additional canvas to get pixel colours -> awful workaround.
Now I'm thinking about converting the text or the glyphs to polygons for hit testing. Is it possible? Or has anyone another approach for glyph based hit testing?
Best Regards
You are really entering a world of pain and cross browser problems. I ended up doing custom path-rendering of fonts only to get the total text length reliable and consistent. I don't even want to think about glyph-hitting.
One problem for example is that firefox (at least 3.6) and iirc also some version of opera has some rounding error when scaling so when you scale the parent-element holding the text and scale the text by the inverse of that scale, then the letter-spacing will be slightly different compared to without any scale. (Because each letter must begin on an even number or something like that, problem can be solved by multiplying both the upscale and downscale with like 10000 but that's another story)
The performance impact by using path compared to text is unfortunately quite noticeable. If your canvas does any form of animated panning or zooming you should switch to pure text-elements during the animation and once static, turn on path rendering for accuracy.
Fortunally converting svg-fonts to paths is very easy, it is plaintext and using the exact same format as the path-element. (beware of font-embedding-licenses though! Also keep file size in mind as you cannot use the fonts from the users system, )
As for the pixel-based hit-testing – if you switch to HTML5 Canvas, then this will become possible. Several projects provide easy transition from SVG to Canvas, e.g. fabric.js. See a comparison table here.
As for the polygon-based approach – possible, but difficult. You can convert text or glyphs to polygons (paths) using some tool (Inkscape's text-to-path for instance). And then there'll be calculations. Making a general solution for any text will require a lot of work. However, if the text doesn't change, then drawing your text manually using paths can be a quick and dirty solution.

Redraw lots of objects on Canvas HTML

Is there a quick and efficient way to move lots of objects in canvas? Basically if there are around 1000 objects and I want to move all of them at once to emulate scrolling, it is very slow to redraw every single object by calling drawImage() 1000+ times.
Is there anyway to optimize this? I have an example link of the problem (and that's only with 100 objects): http://craftyjs.com/isometric/
Since canvas doesn't provide fast low level bitmap copying it's hard to do stuff in multiple layers and scroll for example the whole background at once and then only render the edges.
So what can you do? In short, nothing. Especially not when scrolling, sure you can do tricks with multiple canvases when you have a more or less static background but for moving objects there are hardly any performance improving tricks.
So, you've go to wait for Hardware Acceleration shipping in all majors browsers, I know this sounds ridiculous but I'm too waiting for that :/
The problem is that the canvas was never designed for game stuff. It was designed as, well, basically some kind of on the fly drawing thing, guess the designers had Photoshop clones in mind, but definitely not games, let alone the fact that there's no fast clear operation proves that, there's not even optimization in place when clearing the whole canvas with the same color.
If the images are already composited, not moving relative to one another, and defined by a rectangular region, then using canvas.drawImage() with a canvas as the first parameter and drawing to a sub-region should be significantly faster than re-drawing all the objects.
You could also just layer multiple canvases and slide the top canvas with the objects in HTML to scroll them.
Edit: Having really looked at your example, it seems to me that it should be implemented similar to Google Maps: create tiles of canvases and slide them left/right on the HTML page; once a canvas has been slid off the screen entirely (for example, off the left edge), move it to the other side (to the right edge) and re-use it for drawing. With this you will only need to re-draw whatever objects overlap the canvases that are moving on the edges.
You can draw all objects on a second, off-screen canvas and then only blit the whole canvas (drawImage() accepts canvas element).
However, if you're targeting desktop browsers, then this shouldn't be necessary. I've implemented tile engine (source) that simply redraws whole scene and naive implementation turned out to be pretty fast.
What I did to solve this problem was I had 10 squares on my screen and I wanted to animate them on a white background. So I drew a white rectangle over the canvas to clear the canvas so the animation would work. Does that make sense?
#Ivo By the way I read on http://www.w3.org/TR/html5/the-canvas-element.html that canvas was made for applications like games because it was a solution to get rid of the dependency on a external engine. Canvas is built in so it's kind of like a flash player built into your browser powered by JavaScript. I think it's fascinating.
You can use tiled rendering.
http://www.gamesfrommars.fr/demojsv2/ (better viewed with Chrome)

Categories

Resources