I have a project I'm working on that creates a graph that looks like this:
There is a functionality of the original app that I'm rewriting into web where you can click near the black paths and it will select that path, as indicated by the one path that is noticeably thicker.
I managed to get the onclick for the black paths working, but I'm finding the range to which you can select these lines is far too small (it's pixel perfect), and would make the feature worthless.
Is there a way I can expand the range at which you can click a path? Even 5 pixels on each side would help immensely.
My current line of thinking is to make significantly thicker white paths beneath the black paths and use their onclick, but while I'm testing that out I figured I'd ask if there's some other (hopefully easier) way that's not clear to me that someone else might know. I haven't seen anything within the Raphael documentation that does what I need, but of course if there is something I missed I'd love to see it. Any ideas are appreciated.
Thanks to Ian for confirming my suspicions, this is what I ended up doing:
this.line = canvas.path(this.lineString);
this.selectionLine = canvas.path(this.lineString);
this.selectionLine.attr('stroke-width', 15);
this.selectionLine.attr('stroke-linecap', 'round');
this.selectionLine.attr('opacity', 0);
this.selectionLine.toFront();
The attr('stroke-linecap', 'round') bit gives the path ends a curved portion so if you click just above a path it'll still count. Likely unnecessary, but I feel it important to account for possible user frustration.
It's important that you verify that the selection path is in fact drawing above the display path, otherwise if the user does manage to click the display path's area, the selection won't go through. Of course you could just stick the onclick onto the display line too, but I feel that drawing the selection path above the other is a cleaner solution.
It looks like this if you remove the opacity line, change the color, and change toFront() to `toBack()'
Related
I am having some trouble with scaling and rotation of SVG images with FabricJS.
I've been working on an icon builder for my website. A user can add layers, select a shape for that layer, then set a number of options such as coloring and stroke width. I populate the possible "shapes" and, for maximum flexibility in the shape's complexity, I am using SVG files that I have created with Inkscape. I use the loadSVGFromURL functionality in FabricJS to handle loading the file onto my canvas.
The problem is when I load some (seems to be most) of these SVGs to the canvas then try to use the handles to scale and rotate the image, the rendered result is not appearing as expected: Octagon SVG Image and Action Results.
On that image, don't worry about the "sliced" edges, that's just the editor's boundaries. After loading my octogon.svg to the canvas, it looks right. When I rotate, it gets messed up. I rotated to the right and downward and the image moved to the left and upward. Then, when I scale, it moves vertically the opposite direction.
I have tried programmatically changing these settings and it behaves the same way. I've read several posted questions here and on newsgroup discussions, but nothing got me a solid solution.
One very close solution was using an optimization tool to remove some transformation data that is apparently not liked much by fabric (that tool being found here http://petercollingridge.appspot.com/). It seemed to work, but not for every image, this one included. In fact, regardless of the optimization options I chose, the tool altered the octagon so much, it wasn't really usable as an octogon. That tool is also reference in this existing question: 10470508 (issue-with-svg-display-rendering-in-fabric-js)
I have saved the SVG out of Inkscape in various formats: Inkscape SVG, plain SVG, and Optimized SVG - none made any difference. I also tried Simplifying the Path prior to exporting in all of these formats - again, no difference (though for some shapes, this did seem to work).
I have read many bugs and the related discussions on GitHub with Kangax involved and tried things like the centeredRotation property and origin points on the objects.
I noticed that Inkscape's x,y of 0,0 is the bottom left corner which I thought was a little odd and thought maybe that had something to do with the scaling at least. To attempt fixing it from that point of view, I set flipY on the object, but that didn't work at all. It appeared very far above the editor panel. It almost feels like the transform point is somewhere way above the bounding box. But I cannot verify that. All the settings I check seem like they are set correctly.
I did try a hexagon made in Illustrator and it seemed to work, but I do not have Illustrator at my disposal and to say that is a "solution" is somewhat undesirable. Perhaps this is better logged as a bug, but with all the related chatter, I am somewhat hoping that it is resolved and I just have not found the right combination of settings or processes to follow. I really need to be able to use Inkscape for these. Kangax has pointed out that Inkscape SVGs are more troublesome for FabricJS, but he goes on to say they are getting better. So perhaps this can help with another step forward.
I have also reviewed the code itself on GitHub, but cannot seem to pin point what exactly I would need to change to fix this; though, I am not really sure where the point of failure is - fabric, my settings, or my SVG file.
UPDATE 2/21/2014 10:27PM EST
I began digging into the SVG file structure, comparing the ones giving me trouble with the ones that are working. It definitely does seem like the problem is due to a "transform" attribute being present on a path, container, or other object tag. The optimization processes I've tried should have removed them, but I still have not found a solid solution that just works in all cases. I'll keep you updated.
I don't know much about fabricjs and Inkscape, but using the svg DOM the following will apply for rotation and scale transformations applied individually to an element: Maybe you can apply this. If you want to rotate+scale an element your can append the transformation strings or use transformation matrix. Hope this helps
var bb=elem.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----rotate from center of elem---
elem.setAttribute("transform","rotate(angle "+cx+" "+cy+")")
//---scale: translate center to(0,0)+scale+translate back---
elem.setAttribute("transform","translate("+cx+" "+cy+")scale(myScale)translate("+(-cx)+" "+(-cy+")")
Would it be advantageous to load your svg files as xml using XMLHttpRequest? You could then treat them as xml, work on them, then insert them as SVG by cloning them.
pretty seamless, I think. You can extract elements from XMLdoc and insert them as svg by merely cloning them. Also you can include a DIV in your html and dump the entire xml response text into it as its innerHTML. Then everything is available for DOM workings.
var XMLdoc
function loadSVGasXML()
{
var SVGFile="my.svg"
var loadXML = new XMLHttpRequest;
function handler()
{
if(loadXML.readyState == 4 && loadXML.status == 200)
{
//---responseText---
var xmlString=loadXML.responseText
//---mySVGDiv.innerHTML=xmlString ---
//---DOMParser---
var parser = new DOMParser();
XMLdoc=parser.parseFromString(xmlString,"text/xml").documentElement ;
}
}
if (loadXML != null)
{
loadXML.open("GET", SVGFile, true);
loadXML.onreadystatechange = handler;
loadXML.send();
}
}
Like the other issues posted about this, the problem was still in the individual transformations on the paths. The online solutions I found never worked as well as I needed them to; at least not with SVGs from InkScape. Saving the SVG in a different type right out of InkScape also did not impact the end result. Some shapes, when "simplified", were fine; however, it was not consistent enough. So, I set out to develop a solution.
After reading SVG file structure documentation, I created this specifically to handle InkScape SVGs. Some SVG commands are not handled, but so far, I have not noticed an issue with the few missing commands (which were excluded simply due to time).
EvilEye Games - InkScape SVG Transform Remover
In the end though, I just decided to use some free SVGs from another website. So it was all for not for me. However, I was able to figure it out and fix the problem. So figured maybe this would come in handy for someone else. Its not perfect, but it does what it needs to.
I have raphael paths plotted and text on its top but when two path overlaps the longer text are broken or hidden by other element. I have jsfiddle . I tried :
r.toFront(); for path
AND
'fill-opacity' : 0 for object
but it is not working. I tried manually positioning the text but it's rather very hard when the path is around 50 :(. Is there any property like z-index in raphael. I tried that too but it doesn't work.
r.text()creates a new text element for you. You need to send that to the front. But if you add further paths afterwords, they are still stacked upon it.
To solve this, you may separate the text creation in a second loop or use Element.insertBefore() and Element.insertAfter() to send the country outlines to the back and the text-labels to front.
A quite simple fix is on jsfiddle.
I'm trying to build something in HTML5/Canvas to allow tracing over an image and alert if deviating from a predefined path.
I've figured out how to load an external image into the canvas, and allow mousedown/mousemovement events over it to draw over the image, but what I'm having trouble getting my head around is comparing the two.
Images are all simple black on white outlines, so from what I can tell a getPixel style event can tell if there is black underneath where has been drawn upon or underneath where the mouse is on.
I could do it with just the mouse position, but that would require defining the paths of every image outline (and there are a fair number, hence ideally wanting to do it by analyzing the underlying image)..
I've been told that its possible with Flash, but would like to avoid that if possible so that compatability with non-flash platforms (namely the ipad) can be maintained as they are the primary target for the page to run.
Any insight or assistance would be appreciated!
I think you already touched upon the most straight-forward approach to solving this.
Given a black and white image on a canvas, you can attach a mousemove event handler to the element to track where the cursor is. If the user is holding left-mouse down, you want to determine whether or not they are currently tracing the pre-defined path. To make things less annoying for the user, I would approach this part of the problem by sampling a small window of pixels. Something around 9x9 pixels would probably be a good size. Note that you want your window size to be odd in both dimensions so that you have a symmetric sampling in both directions.
Using the location of the cursor, call getImageData() on the canvas. Your function call would look something like this: getImageData(center_x - Math.floor(window_size / 2), center_y - Math.floor(window_size / 2), window_size, window_size) so that you get a sample window of pixels with the center right over the cursor. From there, you could do a simple check to see if any non-white pixels are within the window, or you could be more strict and require a certain number of non-white pixels to declare the user on the path.
The key to making this work well, I think, is making sure the user doesn't receive negative feedback when they deviate the tiniest bit from the path (unless that's what you want). At that point you run the risk of making the user annoyed and frustrated.
Ultimately it comes down to one of two approaches. Either you load the actual vector path for the application to compare the user's cursor to (ie. do point-in-path checks), or you sample pixel data from the image. If you don't require the perfect accuracy of point-in-path checking, I think pixel sampling should work fine.
Edit: I just re-read your question and realized that, based on your reference to getPixel(), you might be using WebGL for this. The approach for WebGL would be the same, except you would of course be using different functions. I don't think you need to require WebGL, however, as a 2D context should give you enough flexibility (unless the app is more involved than it seems).
I always had the idea to make a textarea based program, that gets the last line and uses it as a command. So it looks like some cool DOS-like program.. But in fact is simple textarea -> AJAX -> PHP -> textarea again. Textarea based version works great, but has alot of drawbacks. Mostly related to not getting the right command and possible vulnerabilities to the script.
So, my idea is to use a output container and command line totally separately. I found a good example of what I want, but I cannot figure out, how it is actually made. The main idea is that the hole thing feels like one block of text and you can highlight it:
But you actually cant delete the prompt (c:>) or the already outputted text above.. Since the command line is an actual input.
I don't understand, how this effect is being achieved. You can see the above examples source code, it is basically:
<div id="black_wrapper">
<div id="outputted_code"></div>
<span id="prompt">c:\></span><span id="commandline"></span><span id="blinker"></span>
<input type="text" name="actual_commandline" value="" />
</div>
And the JavaScript behind it is: http://pastebin.com/pjbd9Y7k
How can I merge the spans line with the input, so it works like one line, but you cannot manipulate the span#prompt or span#blinker contents?
It is very hard to find any help on www for such localized problems. I can see some CSS in the example-link above..however my knowledge of CSS tells me, that it has nothing to do with that layout.
Also, I drew out exactly what I'm thinking to do, as it is very hard to explain these types of technical questions:
Red = General wrapper, not much function
Blue = Prompt area, very static
Green = Writing area, expands its width when typing, pushing the blinker more to the right
Purple = Blinker container, simple gif animation that looks like this:
Note: I'm not actually ripping-off that example site, but well only that command-line layout. Any hint, link code or idea is welcome. It can be totally alternative, but should achieve the goal, thanks :)
Edit: The question got solved thanks to Joe. However, since the original idea might interest somebody in the future.. I will include a small picture, of what my script looks like. Maybe this inspires some of you someday :)
What he's doing is hiding a textbox off-screen. The actual command line is just an element styled to have 1 line's worth of height. When you click this command line, the textbox takes focus.
The C:\> and _ sandwich the textarea, so your text appears in between, thus increasing the width on the command line element as you type, and pushing the _ to the right, giving the illusion that it moved on its own.
http://jsfiddle.net/YeR3L/1/
I'm looking for a way trough javascript find the main color from a image. Probably trough some algorithm to cluster some areas.
Problem because if I use pixel, will count more pixels used. Ex. if i have brown and very colors next to brown, then a part with white. the main color could be white. And not want color average because could not represent the real color of the image.
Any suggestion of plugins, code to implement, websites . Thanks in advance
UPDATE
Something like this :
http://www.cssdrive.com/imagepalette/
There here something explaining, not sure if i can reuse the js code.
http://harthur.wordpress.com/2009/12/18/getting-the-color-scheme-of-a-website-using-canvas-and-hierarchical-clustering/
This is tough but doable.
The first step is to get the pixel data from the image - in order to do this, you'll need to draw the image onto a canvas element and get the pixel data. Note that the Same Origin Policy applies to the image, so the image must be on the same server as the script or you'll need to use a proxy.
Now you can apply an algorithm to the pixel data to find the "main" color. The easiest option is the average, but it sounds like you don't want that. There are a lot of clustering algorithms out there; probably what you want is to perform color quantization to reduce the number of colors in the palette to some small number, then take the color that represents the most pixels in an image.
The median cut algorithm is a good, relatively simple option here, though it's still a fair amount of coding. I worked on a small hobby project to implement this algorithm in Javascript - you can see my code here. It won't work for you out of the box, but I've probably done most of the hard work for you.
May be you can try PnnQuant.js
Demo site: https://mcychan.github.io/PnnQuant.js/demo/
CIEDE2000 color difference formula is adapted by choosing radio button with Quality: High
On top of that, Web Workers is used to create a background thread to invoke long running scripts and handle computationally intensive tasks like the image quantization.