Rectangle 27 10

There are two ways to group multiple SVG shapes and position the group:

The first to use <g> with transform attribute as Aaron wrote. But you can't just use a x attribute on the <g> element.

<svg>
<svg id="parent">
   <svg id="group1" x="10">
      <!-- some shapes -->
   </svg>
</svg>

In this way, the #group1 svg is nested in #parent, and the x=10 is relative to the parent svg. However, you can't use transform attribute on <svg> element, which is quite the contrary of <g> element.

why <g> doesn't have a x and y attribute while all other svg tags have it ? it means that position is always absolute in a <svg>... no relative position,

I agree... it seems like a bad design choice. Simply incrementing a group's X-transform requires writing a potentially complicated string and storing all the variables somewhere else

@user1952009: theres a chance just a chance that you dont actually understand the design choices that went into SVG.

SVG Positioning - Stack Overflow

svg
Rectangle 27 39

Mozilla introduced a CSS property for this purpose called pointer-events. It was originally limited to SVG shapes, but is now supported on most DOM elements in modern browsers:

span.label { pointer-events: none; }

COOL, didn't know it worked with SVG, only "regular" elements

@Cystack the 'pointer-events' property has been part of SVG since the very beginning (and no, it wasn't introduced by Mozilla - though Mozilla did pioneer the use of this property outside of svg). As the first link there says "Warning: The use of pointer-events in CSS for non-SVG elements is experimental".

Also, pointer-events does work in SVG content in IE9+ AFAIK.

I couldn't get stackLabels to not steal mouse events in Highcharts, this was the only workaround I could get to work!

javascript - How to make an SVG text element "click-through-able"? - S...

javascript svg
Rectangle 27 3

With the API options, you can't really do it. You can use advanced CSS selectors to hide the SVG shapes.

Add this CSS and it will hide the US shapes:

#visualization path:nth-child(237), #visualization path:nth-child(236) {
    display:none;    
}

I have built a page with some information on how to use this and other CSS techniques with the Google Geochart API. In case it helps, here's the link: http://cmoreira.net/interactive-world-maps-demo/advanced-customization/

Hide US on google geochart when region is set to Canada - Stack Overfl...

region regions google-visualization
Rectangle 27 251

Sorry but they are only in Unicode. :(

U+25B2
U+25BC
U+25C0
U+25B6
U+25B3
U+25BD
U+25C1
U+25B7
U+25B4
U+25C2
U+25BE
U+25B8
U+25C3
U+25BF
U+25B9
U+25B5

There are also some "pointy" triangles. You can read more here in Wikipedia:http://en.wikipedia.org/wiki/Geometric_Shapes

But unfortunately, they are all Unicode instead of ASCII. If you still want to use ASCII, then you can use an image file for it of just use ^ and v. (Just like the Google Maps in the mobile version this was referring to the ancient mobile Google Maps)

As others also suggested, you can also create triangles with HTML, either with CSS borders or SVG shapes or even JavaScript canvases.

div{
    width: 0px;
    height: 0px;
    border-top: 10px solid black;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-bottom: none;
}
<svg width="16" height="10">
    <polygon points="0,0 16,0 8,10"/>
</svg>
var ctx = document.querySelector("canvas").getContext("2d");

// do not use with() outside of this demo!
with(ctx){
    beginPath();
    moveTo(0,0);
    lineTo(16,0);
    lineTo(8,10);
    fill();
    endPath();
}

Weird how they are not symmetrical. (At least in Chrome on OSX)

How to have these symbols in Windows CE?

The ALT Codes for the first set are (Alt + 30) , (Alt + 31) , (Alt + 16) , (Alt + 17) , for those tying to type these in manually.

@Nate - Are those for Mac OS? Since they are not working for me on Windows.

Those are what I used to get them in Visual Studio and its how I typed them into my comment here. Windows 7. Maybe it isn't universal (though I thought it was). It could be the font you are using doesn't support these characters.

unicode - What characters can be used for up/down triangle (arrow with...

html unicode icons ascii symbols
Rectangle 27 18

fabric.Canvas#toJSON actually returns an object, not a JSON string.

The reason this works like that is due to JSON.stringify. The thing about JSON.stringify is that it supports custom serialization; all you need to do is pass an object that has toJSON method. And this is exactly what I did in fabric fabric.Canvas has toJSON method, which is essentially an alias to toObject method.

This allows us to serialize canvas by doing something as simple as:

JSON.stringify(canvas);
canvas
fabric.Canvas

The difference between toObject and toDatalessObject (as well as toJSON and toDatalessJSON) is that toDatalessObject returns "url" instead of actual path data. This is done to save on size of canvas representation with shapes of large size. If you load large SVG shape, its path data could literally result in multi-million character string. If you later need to serialize this data (say, for saving purposes), it makes much more sense to replace path data with URL of svg shape. Just imagine having to upload/download such huge strings to the server back and forth.

{ "angle" : 3,
  "fill" : "#00274D",
  "flipX" : false,
  "flipY" : false,
  "height" : 115,
  "left" : 353,
  "opacity" : 1,
  "overlayFill" : null,
  "path" : [ [ "M",
        67.390000000000001,
        22.390000000000001
      ],
      [ "c",
        2.5899999999999999,
        -0.42999999999999999,
        5.1100000000000003,
        1.4399999999999999,
        5.54,
        4.1799999999999997
      ],
      [ "c",
        0.42999999999999999,
        2.6600000000000001,
        -1.3,
        5.2599999999999998,
        -3.8900000000000001,
        5.6900000000000004
      ],
      [ "c",
        -1.8,
        0.28999999999999998,
        -3.6000000000000001,
        -0.57999999999999996,
        -4.6100000000000003,
        -2.02
      ],
      [ "L",
        44.5,
        34.560000000000002
      ],
      [ "l",
        10.869999999999999,
        59.619999999999997
      ],
      [ "c",
        17.420000000000002,
        -4.46,
        26.059999999999999,
        -14.18,
        27.5,
        -29.02
      ],
      [ "l",
        -10.01,
        -0.71999999999999997
      ],
      [ "L",
        88.700000000000003,
        51.909999999999997
      ],
      [ "l",
        9.4299999999999997,
        21.239999999999998
      ],
      [ "c",
        -3.3799999999999999,
        -1.95,
        -5.9000000000000004,
        -5.1100000000000003,
        -9.2899999999999991,
        -7.0599999999999996
      ],
      [ "c",
        -0.28999999999999998,
        25.059999999999999,
        -27.140000000000001,
        32.759999999999998,
        -33.770000000000003,
        47.950000000000003
      ],
      [ "C",
        44.420000000000002,
        100.08,
        26.5,
        114.77,
        6.9100000000000001,
        82.799999999999997
      ],
      [ "L",
        0,
        92.450000000000003
      ],
      [ "l",
        1.51,
        -21.600000000000001
      ],
      [ "l",
        19.66,
        4.6799999999999997
      ],
      [ "l",
        -9.4299999999999997,
        3.6699999999999999
      ],
      [ "c",
        7.4900000000000002,
        11.59,
        17.57,
        19.870000000000001,
        36.43,
        16.420000000000002
      ],
      [ "L",
        36.219999999999999,
        36.57
      ],
      [ "l",
        -18.649999999999999,
        2.3799999999999999
      ],
      [ "c",
        -0.14000000000000001,
        2.1600000000000001,
        -1.73,
        4.0300000000000002,
        -3.8900000000000001,
        4.3899999999999997
      ],
      [ "c",
        -2.5899999999999999,
        0.42999999999999999,
        -5.04,
        -1.4399999999999999,
        -5.54,
        -4.0999999999999996
      ],
      [ "c",
        -0.42999999999999999,
        -2.7400000000000002,
        1.3,
        -5.2599999999999998,
        3.8900000000000001,
        -5.6900000000000004
      ],
      [ "c",
        1.9399999999999999,
        -0.35999999999999999,
        3.8900000000000001,
        0.65000000000000002,
        4.9000000000000004,
        2.2999999999999998
      ],
      [ "l",
        17.93,
        -4.8200000000000003
      ],
      [ "l",
        -1.3700000000000001,
        -6.8399999999999999
      ],
      [ "c",
        -4.8200000000000003,
        -0.79000000000000004,
        -8.9299999999999997,
        -4.75,
        -9.7899999999999991,
        -10.08
      ],
      [ "c",
        -1.1499999999999999,
        -6.6200000000000001,
        3.1000000000000001,
        -12.890000000000001,
        9.4299999999999997,
        -13.970000000000001
      ],
      [ "c",
        6.4100000000000001,
        -1.01,
        12.460000000000001,
        3.46,
        13.539999999999999,
        10.08
      ],
      [ "c",
        0.85999999999999999,
        5.1799999999999997,
        -1.5800000000000001,
        10.15,
        -5.6900000000000004,
        12.6
      ],
      [ "l",
        1.8700000000000001,
        6.1200000000000001
      ],
      [ "l",
        20.739999999999998,
        -2.8799999999999999
      ],
      [ "C",
        64.010000000000005,
        24.260000000000002,
        65.519999999999996,
        22.75,
        67.390000000000001,
        22.390000000000001
      ],
      [ "L",
        67.390000000000001,
        22.390000000000001
      ],
      [ "z" ],
      [ "M",
        33.909999999999997,
        5.1799999999999997
      ],
      [ "c",
        -3.46,
        0.57999999999999996,
        -5.7599999999999998,
        3.96,
        -5.1100000000000003,
        7.5599999999999996
      ],
      [ "c",
        0.57999999999999996,
        3.6000000000000001,
        3.8900000000000001,
        6.0499999999999998,
        7.2699999999999996,
        5.4699999999999998
      ],
      [ "c",
        3.46,
        -0.57999999999999996,
        5.7599999999999998,
        -3.96,
        5.1799999999999997,
        -7.5599999999999996
      ],
      [ "C",
        40.609999999999999,
        7.0499999999999998,
        37.369999999999997,
        4.6100000000000003,
        33.909999999999997,
        5.1799999999999997
      ],
      [ "z" ]
    ],
  "scaleX" : 3.0299999999999998,
  "scaleY" : 3.0299999999999998,
  "selectable" : true,
  "stroke" : null,
  "strokeWidth" : 1,
  "top" : 220,
  "type" : "path",
  "width" : 99
}

you would have this:

{ "angle" : 3,
  "fill" : "#00274D",
  "flipX" : false,
  "flipY" : false,
  "height" : 115,
  "left" : 353,
  "opacity" : 1,
  "overlayFill" : null,
  "path" : "http://example.com",
  "scaleX" : 3.0299999999999998,
  "scaleY" : 3.0299999999999998,
  "selectable" : true,
  "stroke" : null,
  "strokeWidth" : 1,
  "top" : 220,
  "type" : "path",
  "width" : 99
}

Notice how large that data is, and how relatively small it becomes by replacing path chunk with url.

And that's a representation of a very simple shape.

The only requirement here is to set object's "sourcePath" using setSourcePath method before calling toDatalessObject/toDatalessJSON on it ("sourcePath" is internally copied to "path").

Thanks. I understand the 'dataless' aspect now. That's great. But, I have not been able to load the result of JSON.stringify(canvas) back into the canvas. I have made a work around, but I would love to understand the right way to do it. Below is basically what I have tried and failed with: var js = JSON.stringify(canvas); canvas.clear(); canvas.loadFromJSON(js); I get reference error For my workaround, I save the JSON, then walk through it, look at each object's type, make a new fabric object of corresponding type, initialize it with the params from json, and then add it...

Hmm. loadFromJSON should work. Do you have a testcase I can look at?

savedDataDLJSON = c1.toDatalessJSON()
savedDataDLJSON = JSON.stringify(c1.toDatalessJSON())

Thanks @kangax, I just did. Still not working. When I try to load the stringified datalessJSON or the sringified JSON, i get camelize is not defined in fabric/all-min.js:7. hmmm... I wonder what I am doing wrong? and sorry for all the trouble...

javascript - Loading JSON in canvas with fabric.js - Stack Overflow

javascript json canvas fabricjs
Rectangle 27 42

SVG is like a "draw" program. The drawing is specified as drawing instructions for each shape and any part of any shape can be changed. Drawings are shape-oriented.

Canvas is like a "paint" program. Once the pixels hit the screen, that is your drawing. You cannot change shapes except by overwriting them with other pixels. Paintings are pixel-oriented.

Getting great animation performance for user-manipulation via mouse drags is easier with Canvas than SVG.

A single pixel on the computer screen will often consume 4 bytes of information and a computer screen these days takes several megabytes. So Canvas might be inconvenient if you want to let the user edit an image and then upload it again.

By contrast, drawing a handful of shapes that cover the entire screen using SVG takes up few bytes, downloads quickly, and can be uploaded again easily with the same advantages going in that direction as when it comes down on the other direction. So SVG can be faster than Canvas.

Google implemented Google Maps with SVG. That gives the web app its zippy performance and smooth scrolling.

Not going to vote you down - the new version of google maps actually uses canvas now, not svg. The svg version is now deprecated.

What is the difference between SVG and HTML5 Canvas? - Stack Overflow

html5 canvas svg
Rectangle 27 33

SVG is an earlier standard for drawing shapes in browsers. However, SVG is at a fundamentally higher level because each drawn shape is remembered as an object in a scene graph or DOM, which is subsequently rendered to a bit map. This means that if attributes of an SVG object are changed, the browser can automatically re-render the scene.

In the example above, 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. But in the equivalent SVG case, one could simply change the position attributes of the rectangle and the browser would determine how to repaint it. It is also possible to paint a canvas in layers and then recreate specific layers.

SVG images are represented in XML, and complex scenes can be created and maintained with XML editing tools.

The SVG scene graph enables event handlers to be associated with objects, so a rectangle may respond to an onClick event. To get the same functionality with canvas, one must manually match the coordinates of the mouse click with the coordinates of the drawn rectangle to determine whether it was clicked.

Conceptually, canvas is a lower level protocol upon which SVG might be built.[citation needed] However, this is not (normally) the casethey are independent standards. The situation is complicated because there are scene graph libraries for Canvas, and SVG has some bit map manipulation functionality.

UPDATE: I use SVG because of its markup language abilities - it can be processed by XSLT and can hold other markup in its nodes. Similarly I can hold SVG in my markup (chemistry). This allows me to manipulate SVG attributes (e.g. rendering) by combinations of markup. This may be possible in Canvas but I suspect that it's a lot harder.

The last sentence in the last paragraph needs a citation or two as well. SVG has no "bit map manipulation functionality", unless the author is trying to misrepresent svg filter effects as that, but it's far from clear what is meant.

@Erik I would agree with you. Looks like this WP entry needs editing

It sounds like for most applications, SVG is superior to Canvas. Is that true? Is there anything that Canvas can do that SVG can't?

I know it is years later but today there are many canvas libraries, like paper.js and fabric.js

What is the difference between SVG and HTML5 Canvas? - Stack Overflow

html5 canvas svg
Rectangle 27 319

You can achieve the double curve easily with an inline SVG and the <path/> element instead of the <polygon/> element which doesn't allow curved shapes.

The following example uses the <path/> element with:

<svg width="30%" viewbox="0 0 30 42">
  <path fill="transparent" stroke="#000" stroke-width="1.5"
        d="M15 3
           Q16.5 6.8 25 18
           A12.8 12.8 0 1 1 5 18
           Q13.5 6.8 15 3z" />
</svg>

SVG is a great tool to make this kind of shapes with double curves. You can check this post about double curves with an SVG/CSS comparison. Some of the advantages of using SVG in this case are:

  • No HTTP request (if used inline like in the example)
<svg width="100%" height="100%" viewBox="0 0 8 8" />   <path d="M4 1L3 4A1.2 2 0 1 0 5 4"/> </svg>

@technosaurus the problem with reducing the number of commands in the d="..." attribute is that you don't have the double curve at the top of the drop anymore.

+1 because you should use SVG for this, not CSS. The CSS features you'd need to achieve it have about the same browser support as SVG so there's no advantage for CSS on that score. CSS can do shapes, but that's not what it's designed for; don't try to hammer in a nail with a screwdriver just for the sake of being clever when you can do it just as well with a tool designed for the job.

Even better: create a SVG file and use <img /> in the hypertext document.

+1 because I'm not good with SVG but this is simple enough for me to understand. I like the look of it too!

css - How do I create a teardrop in HTML? - Stack Overflow

html css css3 svg css-shapes
Rectangle 27 1

To draw on the SVG :

var svg_ns = "http://www.w3.org/2000/svg";
var my_svg = document.getElementById('your_svg');

var new_shape = document.createElementNS(svg_ns, 'rect');
new_shape.setAttributeNS(null, 'x', x);
new_shape.setAttributeNS(null, 'y', y);
new_shape.setAttributeNS(null, 'height', '50');
new_shape.setAttributeNS(null, 'width', '50');
new_shape.setAttributeNS(null, 'fill','#00FF00');
my_svg.appendChild(new_shape);
var svg_paths = my_svg.getElementsByTagName("path");
/* Then to hightlight a path : svg_paths[i].stroke='#0000FF' */

Can I draw a new shape over the selected path, ie i need the highlighted area as background and on the top of that svg background i want to draw new shapes.Is it possible??

javascript - Can I draw shapes over an svg file - Stack Overflow

javascript canvas svg html5-canvas
Rectangle 27 425

SVG would be easier for you, since selection and moving it around is already built in. SVG objects are DOM objects, so they have "click" handlers, etc.

Canvas has the best performance hands-down, but you have to implement all concepts of managed state (object selection, etc) yourself, or use a library.

HTML5 Canvas is simply a drawing surface for a bit-map. You set up to draw (Say with a color and line thickness), draw that thing, and then the Canvas has no knowledge of that thing: It doesn't know where it is or what it is that you've just drawn, it's just pixels. If you want to draw rectangles and have them move around or be selectable then you have to code all of that from scratch, including the code to remember that you drew them.

SVG on the other hand must maintain references to each object that it renders. Every SVG/VML element you create is a real element in the DOM. By default this allows you to keep much better track of the elements you create and makes dealing with things like mouse events easier by default, but it slows down significantly when there are a large number of objects

Those SVG DOM references mean that some of the footwork of dealing with the things you draw is done for you. And SVG is faster when rendering really large objects, but slower when rendering many objects.

A game would probably be faster in Canvas. A huge map program would probably be faster in SVG. If you do want to use Canvas, I have some tutorials on getting movable objects up and running here.

Canvas would be better for faster things and heavy bitmap manipulation (like animation), but will take more code if you want lots of interactivity.

I've run a bunch of numbers on HTML DIV-made drawing versus Canvas-made drawing. I could make a huge post about the benefits of each, but I will give some of the relevant results of my tests to consider for your specific application:

I made Canvas and HTML DIV test pages, both had movable "nodes." Canvas nodes were objects I created and kept track of in Javascript. HTML nodes were movable Divs.

The HTML test tab took forever to load (timed at slightly under 5 minutes, chrome asked to kill the page the first time). Chrome's task manager says that tab is taking up 168MB. It takes up 12-13% CPU time when I am looking at it, 0% when I am not looking.

The Canvas tab loaded in one second and takes up 30MB. It also takes up 13% of CPU time all of the time, regardless of whether or not one is looking at it. (2013 edit: They've mostly fixed that)

Dragging on the HTML page is smoother, which is expected by the design, since the current setup is to redraw EVERYTHING every 30 milliseconds in the Canvas test. There are plenty of optimizations to be had for Canvas for this. (canvas invalidation being the easiest, also clipping regions, selective redrawing, etc.. just depends on how much you feel like implementing)

There is no doubt you could get Canvas to be faster at object manipulation as the divs in that simple test, and of course far faster in the load time. Drawing/loading is faster in Canvas and has far more room for optimizations, too (ie, excluding things that are off-screen is very easy).

  • SVG is probably better for applications and apps with few items (less than 1000? Depends really)
  • Canvas is better for thousands of objects and careful manipulation, but a lot more code (or a library) is needed to get it off the ground.
  • HTML Divs are clunky and do not scale, making a circle is only possible with rounded corners, making complex shapes is possible but involves hundreds of tiny tiny pixel-wide divs. Madness ensues.

The Cake library is another example of doing moveable objects and animations with objects on a canvas

Wrong :P div's can scale if the browser is using hw accelerated CSS engine, css art is different and besides Canvas and SVG are the proper choice here , CSS art / div art is just when u dont need to overkill just a small overlay :P

Concerning DIVs, if you want to make circles/special shapes and is not going to change its image/sprite due course, you can just create a PNG and use it as background-image... Though you can do similar things in SVG/Canvas

What if you are creating an interactive map game? :p

javascript - HTML5 Canvas vs. SVG vs. div - Stack Overflow

javascript html5 svg html5-canvas
Rectangle 27 1253

You should be aware of a few key factors...

First, there are two types of compression: Lossless and Lossy.

  • Lossless means that the image is made smaller, but at no detriment to the quality.
  • Lossy means the image is made (even) smaller, but at a detriment to the quality. If you saved an image in a Lossy format over and over, the image quality would get progressively worse and worse.

There are also different colour depths (palettes): Indexed color and Direct color.

  • Indexed means that the image can only store a limited number of colours (usually 256), controlled by the author, in something called a Color Map
  • Direct means that you can store many thousands of colours that have not been directly chosen by the author

This is an old format. It is Lossless (no image data is lost on save) but there's also little to no compression at all, meaning saving as BMP results in VERY large file sizes. It can have palettes of both Indexed and Direct, but that's a small consolation. The file sizes are so unnecessarily large that nobody ever really uses this format.

GIF uses lossless compression, meaning that you can save the image over and over and never lose any data. The file sizes are much smaller than BMP, because good compression is actually used, but it can only store an Indexed palette. This means that for most use cases, there can only be a maximum of 256 different colours in the file. That sounds like quite a small amount, and it is.

GIF images can also be animated and have transparency.

Good for: Logos, line drawings, and other simple images that need to be small. Only really used for websites.

JPEGs images were designed to make detailed photographic images as small as possible by removing information that the human eye won't notice. As a result it's a Lossy format, and saving the same file over and over will result in more data being lost over time. It has a palette of thousands of colours and so is great for photographs, but the lossy compression means it's bad for logos and line drawings: Not only will they look fuzzy, but such images will also have a larger file-size compared to GIFs!

Good for: Photographs. Also, gradients.

PNG is a newer format, and PNG-8 (the indexed version of PNG) is really a good replacement for GIFs. Sadly, however, it has a few drawbacks: Firstly it cannot support animation like GIF can (well it can, but only Firefox seems to support it, unlike GIF animation which is supported by every browser). Secondly it has some support issues with older browsers like IE6. Thirdly, important software like Photoshop have very poor implementation of the format. (Damn you, Adobe!) PNG-8 can only store 256 colours, like GIFs.

Good for: The main thing that PNG-8 does better than GIFs is having support for Alpha Transparency.

PNG-24 is a great format that combines Lossless encoding with Direct color (thousands of colours, just like JPEG). It's very much like BMP in that regard, except that PNG actually compresses images, so it results in much smaller files. Unfortunately PNG-24 files will still be bigger than JPEGs (for photos), and GIFs/PNG-8s (for logos and graphics), so you still need to consider if you really want to use one.

Even though PNG-24s allow thousands of colours while having compression, they are not intended to replace JPEG images. A photograph saved as a PNG-24 will likely be at least 5 times larger than a equivalent JPEG image, with very little improvement in visible quality. (Of course, this may be a desirable outcome if you're not concerned about filesize, and want to get the best quality image you can.)

A filetype that is currently growing in popularity is SVG, which is different than all the above in that it's a vector file format (the above are all raster). This means that it's actually comprised of lines and curves instead of pixels. When you zoom in on a vector image, you still see a curve or a line. When you zoom in on a raster image, you will see pixels.

This means SVG is perfect for logos and icons you wish to retain sharpness on Retina screens or at different sizes. It also means a small SVG logo can be used at a much larger (bigger) size without degradation in image quality -- something that would require a separate larger (in terms of filesize) file with raster formats.

SVG file sizes are often tiny, even if they're visually very large, which is great. It's worth bearing in mind, however, that it does depend on the complexity of the shapes used. SVGs require more computing power than raster images because mathematical calculations are involved in drawing the curves and lines. If your logo is especially complicated it could slow down a user's computer, and even have a very large file size. It's important that you simplify your vector shapes as much as possible.

Additionally, SVG files are written in XML, and so can be opened and edited in a text editor(!). This means its values can be manipulated on the fly. For example, you could use JavaScript to change the colour of an SVG icon on a website, much like you would some text (ie. no need for a second image), or even animate them.

In all, they are best for simple flat shapes like logos or graphs.

Excellent answer. You may want to add thet JPEG can be lossless, too (although the lossy variants are mostly used).

@DjangoReinhardt the filter hack would introduce even more loss when you re-save the image. However, I don't think that's a good definition of lossy format or encoder, AFAIK JPEG's DCT is reversible, so a good encoder could re-save JPEG without introducing further loss.

@sudo No, BMP sure is easy to decode from a processing perspective, but unless it's stored locally on an SSD, I'd assume getting the file to the CPU to process will be slower than just processing a JPG, especially on a properly-written JPG decoder that uses hardware instructions that have been available for a decade or two.

image - PNG vs. GIF vs. JPEG vs. SVG - When best to use? - Stack Overf...

image svg png jpeg gif
Rectangle 27 1237

You should be aware of a few key factors...

First, there are two types of compression: Lossless and Lossy.

  • Lossless means that the image is made smaller, but at no detriment to the quality.
  • Lossy means the image is made (even) smaller, but at a detriment to the quality. If you saved an image in a Lossy format over and over, the image quality would get progressively worse and worse.

There are also different colour depths (palettes): Indexed color and Direct color.

  • Indexed means that the image can only store a limited number of colours (usually 256), controlled by the author, in something called a Color Map
  • Direct means that you can store many thousands of colours that have not been directly chosen by the author

This is an old format. It is Lossless (no image data is lost on save) but there's also little to no compression at all, meaning saving as BMP results in VERY large file sizes. It can have palettes of both Indexed and Direct, but that's a small consolation. The file sizes are so unnecessarily large that nobody ever really uses this format.

GIF uses lossless compression, meaning that you can save the image over and over and never lose any data. The file sizes are much smaller than BMP, because good compression is actually used, but it can only store an Indexed palette. This means that for most use cases, there can only be a maximum of 256 different colours in the file. That sounds like quite a small amount, and it is.

GIF images can also be animated and have transparency.

Good for: Logos, line drawings, and other simple images that need to be small. Only really used for websites.

JPEGs images were designed to make detailed photographic images as small as possible by removing information that the human eye won't notice. As a result it's a Lossy format, and saving the same file over and over will result in more data being lost over time. It has a palette of thousands of colours and so is great for photographs, but the lossy compression means it's bad for logos and line drawings: Not only will they look fuzzy, but such images will also have a larger file-size compared to GIFs!

Good for: Photographs. Also, gradients.

PNG is a newer format, and PNG-8 (the indexed version of PNG) is really a good replacement for GIFs. Sadly, however, it has a few drawbacks: Firstly it cannot support animation like GIF can (well it can, but only Firefox seems to support it, unlike GIF animation which is supported by every browser). Secondly it has some support issues with older browsers like IE6. Thirdly, important software like Photoshop have very poor implementation of the format. (Damn you, Adobe!) PNG-8 can only store 256 colours, like GIFs.

Good for: The main thing that PNG-8 does better than GIFs is having support for Alpha Transparency.

PNG-24 is a great format that combines Lossless encoding with Direct color (thousands of colours, just like JPEG). It's very much like BMP in that regard, except that PNG actually compresses images, so it results in much smaller files. Unfortunately PNG-24 files will still be bigger than JPEGs (for photos), and GIFs/PNG-8s (for logos and graphics), so you still need to consider if you really want to use one.

Even though PNG-24s allow thousands of colours while having compression, they are not intended to replace JPEG images. A photograph saved as a PNG-24 will likely be at least 5 times larger than a equivalent JPEG image, with very little improvement in visible quality. (Of course, this may be a desirable outcome if you're not concerned about filesize, and want to get the best quality image you can.)

A filetype that is currently growing in popularity is SVG, which is different than all the above in that it's a vector file format (the above are all raster). This means that it's actually comprised of lines and curves instead of pixels. When you zoom in on a vector image, you still see a curve or a line. When you zoom in on a raster image, you will see pixels.

This means SVG is perfect for logos and icons you wish to retain sharpness on Retina screens or at different sizes. It also means a small SVG logo can be used at a much larger size -- something that would otherwise require a separate larger file with raster formats.

SVG file sizes are often tiny, even if they're visually very large, which is great. It's worth bearing in mind, however, that it does depend on the complexity of the shapes used. SVGs require more computing power than raster images because mathematical calculations are involved in drawing the curves and lines. If your logo is especially complicated it could slow down a user's computer, and even have a very large file size. It's important that you simplify your vector shapes as much as possible.

Additionally, SVG files are written in XML, and so can be opened and edited in a text editor(!). This means its values can be manipulated on the fly. For example, you could use JavaScript to change the colour of an SVG icon on a website, much like you would some text (ie. no need for a second image), or even animate them.

In all, they are best for simple flat shapes like logos or graphs.

Excellent answer. You may want to add thet JPEG can be lossless, too (although the lossy variants are mostly used).

@porneL Nice! It seems like that's more of a hack to filter out unnecessary detail before saving the file, though. So for example, if you saved the file again, you wouldn't lose any more data (unlike JPG). Is that right?

@DjangoReinhardt the filter hack would introduce even more loss when you re-save the image. However, I don't think that's a good definition of lossy format or encoder, AFAIK JPEG's DCT is reversible, so a good encoder could re-save JPEG without introducing further loss.

@sudo No, BMP sure is easy to decode from a processing perspective, but unless it's stored locally on an SSD, I'd assume getting the file to the CPU to process will be slower than just processing a JPG, especially on a properly-written JPG decoder that uses hardware instructions that have been available for a decade or two.

image - PNG vs. GIF vs. JPEG vs. SVG - When best to use? - Stack Overf...

image svg png jpeg gif
Rectangle 27 2

This version replaces an earlier version that wasn't truly cross-browser. I realized that I didn't need separate shapes for the various circles, I could clone, reposition and recolor the original shape within the filter.

<svg x="0px" y="0px" width="600px" height="600px" viewbox="0 0 600 600">

<defs>
<filter id="B4" x="-150%" width="400%" y="-150%" height="400%">
  <feOffset in="SourceGraphic" result="pre-red" dx="-70" dy="-120"/>
  <feColorMatrix in="pre-red" type="matrix" result="red" values="0 0 0 0 1
                                                   0 0 0 0 0 
                                                   0 0 0 0 0 
                                                   0 0 0 1 0"/>

  <feOffset in="SourceGraphic" result="pre-blue" dx="70" dy="-120"/>
  <feColorMatrix in="pre-blue" type="matrix" result="blue" values="0 0 0 0 0
                                                   0 0 0 0 0 
                                                   0 0 0 0 1 
                                                   0 0 0 1 0"/> 
  <feBlend mode="screen" in="red" in2="blue" result="main"/>
  <feBlend mode="screen" in="main" in2="SourceGraphic"/>
</filter>
</defs>

   <circle filter="url(#B4)" cx="200" cy="250" r="100"  fill="#00FF00"  />

</svg>

Not working on Mozilla and Webkit is definitely a deal breaker. Could you point me in the direction of more info (or explain a bit more) relating to "in filter" effects (the first example)? Looking at it, I feel like I basically get it but I don't see anything that would create the yellow or the cyan (I assume 'final' is white) nor do I see a black stroke (is that because there is no width assigned to the stroke?). Thanks.

I saw the 'enable-background' references and the notes, I'll go back and read them more carefully though, thanks.

graphics - Using SVG for additive color mixing (additive blending) - S...

graphics colors svg transparency svg-filters
Rectangle 27 16

In recent versions of Chrome, Firefox and Opera you could make use of the Path2D API to draw Paths in Canvas based on SVG path data. The Path2D constructor can optionally take a SVG path data as its input and generate the equivalent path on Canvas.

As at the time of writing, it doesn't seem like this API is supported by Internet Explorer and Safari. So, if you need to support those browsers then you have to convert the SVG path into the equivalent Canvas commands using any one of the converter and then put it to use.

window.onload = function() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  ctx.strokeStyle = '#000';
  ctx.lineWidth = 1;
  ctx.fillStyle = '#000'

  var p = new Path2D("m 75.8672,80.4954 c -0.6572,-0.0938 -2.252,-0.4688 -2.627,-0.375 -1.0313,0.1875 0.375,1.501 -0.1875,1.876 -0.4688,0.1875 -1.0313,-0.0938 -1.501,0.0938 -0.1875,0.0938 0.0938,0.4688 -0.0938,0.6572 -0.9375,0.75 -2.4385,0.8438 -3.377,1.6875 -1.125,1.0322 2.0635,3.6582 0.6572,4.6904 -3.2832,2.3447 -1.2197,-1.5947 -3.377,1.501 -0.2813,0.375 0.8438,0.4688 1.0313,0.9375 0.1875,0.376 -0.9375,0.2822 -0.9375,0.6572 0,0.1875 0.9375,1.4072 0.5625,1.876 -1.0313,1.0313 -2.5322,-0.5635 -3.4707,-0.5635 -1.4063,0 1.3135,1.3135 -1.0313,0.6572 -0.6572,-0.1875 -1.501,-1.2197 -1.7822,-1.7822 -0.1875,-0.1875 -0.376,-0.751 -0.4697,-0.5625 -0.375,0.5625 -0.2813,1.2188 -0.6563,1.6875 -1.2188,1.5947 -2.9082,0 -4.3145,0.4697 -0.1875,0.0938 0.1875,0.4688 0,0.6563 -0.8447,0.4688 -2.5332,0.375 -3.377,0 0,0 0,-0.0938 0.0938,-0.0938 0.4688,-0.4688 1.0313,-0.9375 1.2197,-1.4072 3.2822,0.1875 -0.4697,-2.0635 -1.4072,-2.626 -0.6563,-0.375 0.375,-1.5947 0.1875,-2.0635 -0.1875,-0.4697 -1.6885,0.8438 -1.3135,-0.376 0.2813,-0.8438 2.0635,-1.5938 1.4072,-2.1572 -0.4688,-0.375 -2.627,-0.1875 -2.4385,-1.3125 0.1875,-1.501 2.5322,-1.126 2.7197,-3.002 -0.0938,0 -0.0938,0 -0.1875,0 0.5625,0.0938 1.126,0.1875 1.7822,0.2813 0.75,0.0938 1.501,0.6563 2.251,0.75 0.751,0.0938 1.501,-0.4688 2.252,-0.375 0.4688,0.0938 0.75,0.751 1.2188,0.751 0.1875,0.0938 0.0938,-0.4697 0.2813,-0.5635 0.4697,-0.4688 1.2197,-0.6563 1.6885,-1.2188 0.376,-0.2822 0.0938,-0.9385 0.376,-1.2197 1.7813,-1.4072 3.6582,0.375 5.3457,-0.375 0.5635,-0.2813 0.6572,-1.126 1.2197,-1.3135 0.0938,-0.0938 3.1895,0.375 3.2832,0.2813 0.2813,-0.0938 -0.2813,-0.4688 -0.375,-0.75 -0.2813,-1.501 0.8438,-1.876 2.251,-1.876 0.3752,0 1.1262,2.9072 3.0959,4.502 l 0,0 z");
  ctx.stroke(p);
  ctx.fill(p);
}
<canvas id='canvas' height='600px' width='600px'></canvas>
<svg viewBox='0 0 600 600' height='600px' width='600px'>
  <path class="state-x-area" d="m 75.8672,80.4954 c -0.6572,-0.0938 -2.252,-0.4688 -2.627,-0.375 -1.0313,0.1875 0.375,1.501 -0.1875,1.876 -0.4688,0.1875 -1.0313,-0.0938 -1.501,0.0938 -0.1875,0.0938 0.0938,0.4688 -0.0938,0.6572 -0.9375,0.75 -2.4385,0.8438 -3.377,1.6875 -1.125,1.0322 2.0635,3.6582 0.6572,4.6904 -3.2832,2.3447 -1.2197,-1.5947 -3.377,1.501 -0.2813,0.375 0.8438,0.4688 1.0313,0.9375 0.1875,0.376 -0.9375,0.2822 -0.9375,0.6572 0,0.1875 0.9375,1.4072 0.5625,1.876 -1.0313,1.0313 -2.5322,-0.5635 -3.4707,-0.5635 -1.4063,0 1.3135,1.3135 -1.0313,0.6572 -0.6572,-0.1875 -1.501,-1.2197 -1.7822,-1.7822 -0.1875,-0.1875 -0.376,-0.751 -0.4697,-0.5625 -0.375,0.5625 -0.2813,1.2188 -0.6563,1.6875 -1.2188,1.5947 -2.9082,0 -4.3145,0.4697 -0.1875,0.0938 0.1875,0.4688 0,0.6563 -0.8447,0.4688 -2.5332,0.375 -3.377,0 0,0 0,-0.0938 0.0938,-0.0938 0.4688,-0.4688 1.0313,-0.9375 1.2197,-1.4072 3.2822,0.1875 -0.4697,-2.0635 -1.4072,-2.626 -0.6563,-0.375 0.375,-1.5947 0.1875,-2.0635 -0.1875,-0.4697 -1.6885,0.8438 -1.3135,-0.376 0.2813,-0.8438 2.0635,-1.5938 1.4072,-2.1572 -0.4688,-0.375 -2.627,-0.1875 -2.4385,-1.3125 0.1875,-1.501 2.5322,-1.126 2.7197,-3.002 -0.0938,0 -0.0938,0 -0.1875,0 0.5625,0.0938 1.126,0.1875 1.7822,0.2813 0.75,0.0938 1.501,0.6563 2.251,0.75 0.751,0.0938 1.501,-0.4688 2.252,-0.375 0.4688,0.0938 0.75,0.751 1.2188,0.751 0.1875,0.0938 0.0938,-0.4697 0.2813,-0.5635 0.4697,-0.4688 1.2197,-0.6563 1.6885,-1.2188 0.376,-0.2822 0.0938,-0.9385 0.376,-1.2197 1.7813,-1.4072 3.6582,0.375 5.3457,-0.375 0.5635,-0.2813 0.6572,-1.126 1.2197,-1.3135 0.0938,-0.0938 3.1895,0.375 3.2832,0.2813 0.2813,-0.0938 -0.2813,-0.4688 -0.375,-0.75 -0.2813,-1.501 0.8438,-1.876 2.251,-1.876 0.3752,0 1.1262,2.9072 3.0959,4.502 l 0,0 z"
  style="fill:#000;stroke:#fff;stroke-width:1" />
</svg>
moveTo

html5 - Draw path in Canvas with SVG Path data (SVG paths to Canvas pa...

html5 canvas svg html5-canvas
Rectangle 27 6

You are unable to create a dotted border using the method given in question because there the shape itself is created using borders. The border-top and border-bottom that produce the hexagon. When you set the dotted border-style to it, all you'd get is very large dots that wouldn't be like the expected output. While you can use other CSS methods to create the required shape + border (like described in other answers), it is better to use SVG for such complex shapes because it is easy.

You can do this easily with SVG using a single path element. The path is simple to create once you have a good understanding of the commands that are used in creating it.

  • M5,30 - This command Moves the imaginary pen to the point represented by (5,30).
  • L50,0 - This draws a Line from the previous point (5,30) to the point (50,0).
  • 95,30 95,70 50,100 5,70 - These are same as the previous commands and draw lines to the respective points. The command (L) itself need not be repeated as it is the same.

The dotted border is achieved by setting the correct values for the stroke-dasharray and stroke-linecap properties (as mentioned in web-tiki's answer).

svg {
  height: 200px;
  width: 200px;
}
path {
  fill: green;
  stroke: red;
  stroke-dasharray: 0.1, 3;
  stroke-linecap: round;
}
<svg viewBox='0 0 100 100'>
  <path d='M5,30 L50,0 95,30 95,70 50,100 5,70z' />
</svg>

html - CSS3 Hexagon with dotted border - Stack Overflow

html css css3 svg css-shapes
Rectangle 27 8

You can generally achieve the same results with either. Both end up drawing pixels to the screen for the user. The major differentiators are that HTML5 Canvas gives you pixel-level control over the results (both reading and writing), while SVG is a retained-mode graphics API that makes it very easy to handle events or manipulate the artwork with JavaScript or SMIL Animation and have all redrawing taken care of for you.

  • have a very large number of data points that will be presented once (and perhaps panned), but are otherwise static
  • want complex objects drawn on the screen to be associated with events (e.g. move over a data point to see a tooltip)
  • need to animate the shapes of various graph parts independently
  • will be including text in your output that you want to be indexed by search engines

javascript - Pie, bar, line: SVG/VML better than Canvas - Stack Overfl...

javascript canvas charts svg pie-chart
Rectangle 27 3

You can't do different shapes for MultiPoint geoJSON with d3.geo.path. You can change the radius based on a function, but it looks like you can only set it per feature and not per point, so you'd have to break your set of points into multiple features and lose any performance benefit from using the single element.

However, there are other ways to go about doing this.

One option, as you mentioned, is to create a nested selection with a separate <path> element for each point, and draw each path using a d3.svg.symbol() function. You can then customize the symbol function to be based on data or index.

var trips = d3.select("svg").selectAll("g.trips")
               .data(/*The data you were currently using for each path,
                       now gets to a group of paths */)
               .attr("class", "trips"); 
               //also set any other properties for the each trip as a whole

var pointSymbol = d3.svg.symbol().type(function(d,i){
                    if (i === 0) 
                        //this is the first point within its groups
                        return "cross";
                    if ( this === this.parentNode.querySelector("path:last-of-type") )
                        //this is the last point within its group
                        return "square";
                    //else:
                    return "circle";
               });


var points = trips.selectAll("path")
               .data(function(d) {
                   return get_activity_coords_(d.activities);
                   //return the array of point objects
                })
               .attr("transform", function(d){ 
                         /* calculate the position of the point using 
                            your projection function directly */
                })
               .attr("d", pointSymbol);

connect the points as the vertices of a single, invisible <path> element and use line markers to draw the point symbols

Your approach would be:

Create a <defs> element within your SVG (either hard-coded or dynamically with d3), and define the start, middle and end marker points within them. (You can use d3.svg.symbol() functions to draw the paths, or make your own, or use images, it's up to you.)

Use a d3.svg.line() function to create the path's "d" attribute based on your array of point coordinates; the x and y accessor functions for the line should use the projection function that you're using for the map to get the x/y position from the coordinates of that point. To avoid calculating the projection twice, you can save the projected coordinates in the data object:

var multipointLine = d3.svg.line()
                      .x(function(d,i) {
                          d.projectedCoords = projection(d);
                          return d.projectedCoords[0];
                        })
                      .y(function(d){ return d.projectedCoords[1];});

(You can't use your d3.geo.path() function to draw the lines as a map feature, because it will break the line into curves to match the curves of longitude and latitude lines in your map projection; to get the line markers to work, the path needs to be just a simple straight-line connection between points.)

Set the style on that path to be no stroke and no fill, so the line itself doesn't show up, but then set the marker-start, marker-mid and marker-end properties on the line to reference the id values of the correct marker element.

thanks a lot. My user is hooked to curved connectors, also because they show the approx. travel path. But it was good to learn about line-markers and tinker with them!!

d3.js - in d3.geo MultiPoint how can I provide different shapes for di...

d3.js geo
Rectangle 27 1

It's possible to place any piece of markup where you want by using some scripting. E.g you can use yourTextElm.getBBox() for finding where the text ends or if you need to be a bit more specific you can use the SVG DOM methods for text elements.

If you need it to be fully static without scripting, then defining a font to have the shapes you need is probably the way to go. You can make a custom truetype font if need be, that would work for all the browsers that support webfonts (if you're worried about firefox not supporting svgfonts currently).

How to place a symbol (path) relative to the far end of svg text? - St...

text svg
Rectangle 27 66

I tried to find a good tutorial to link to, but couldn't find anything that really covered all the issues, so I'm going to write it out step-by-step myself.

First, you need to clearly understand what you're trying to accomplish. This is different for the two types of zooming. I don't really like the terminology Mike Bostock has introduced, (it's not entirely consistent with non-d3 uses of the terms) but we might as well stick with it to be consistent with other d3 examples.

In "geometric zooming" you are zooming the entire image. Circles and lines get bigger as well as farther apart. SVG has an easy way to accomplish this through the "transform" attribute. When you set transform="scale(2)" on an SVG element, it is drawn as if everything was twice as big. For a circle, it's radius gets drawn twice a big, and it's cx and cy positions get plotted twice the distance from the (0,0) point. The entire coordinate system changes, so one unit is now equal to two pixels on screen, not one.

Likewise, transform="translate(-50,100)" changes the entire coordinate system, so that the (0,0) point of the coordinate system gets moved 50 units to the left and 100 units down from the top-left corner (which is the default origin point).

If you both translate and scale an SVG element, the order is important. If translate is before scale, than the translation is in the original units. If translate is after scale, than the translation is in the scaled units.

The d3.zoom.behavior() method creates a function that listens for mouse wheel and drag events, as well as for touch screen events associated with zooming. It converts these user events into a custom "zoom" event.

The zoom event is given a scale factor (a single number) and a translate factor (an array of two numbers), which the behaviour object calculates from the user's movements. What you do with these numbers is up to you; they don't change anything directly. (With the exception of when you attach a scale to the zoom behaviour function, as described later.)

For geometric zooming, what you usually do is set a scale and translate transform attribute on a <g> element that contains the content you want to zoom. This example implements that geometric zooming method on a simple SVG consisting of evenly placed gridlines:http://jsfiddle.net/LYuta/2/

The zoom is accomplished by setting the transform attribute on "vis", which is a d3 selection containing a <g> element which itself contains all the content we want to zoom. The translate and scale factors come directly from the zoom event that the d3 behaviour created.

The result is that everything gets bigger or smaller -- the width of the gridlines as well as the spacing between them. The lines still have stroke-width:1.5; but the definition of what 1.5 equals on the screen has changed for them and anything else within the transformed <g> element.

For every zoom event, the translate and scale factors are also logged to the console. Looking at that, you'll notice that if you're zoomed out the scale will be between 0 and 1; if you're zoomed in it will be greater than 1. If you pan (drag to move) the graph, the scale won't change at all. The translate numbers, however, change on both pan and zoom. That's because the translate represents the position of the (0,0) point in the graph relative to the position of the top-left-corner of the SVG. When you zoom, the distance between (0,0) and any other point on the graph changes. So in order to keep the content under the mouse or finger-touch in the same position on the screen, the position of the (0,0) point has to move.

There are a number of other things you should pay attention to in that example:

geometric zooming method, applied to a simplified version of your force layout

I've reduced the number of nodes and links, and taken away the node-drag behaviour and the node-expand/collapse behaviour, so you can focus on the zoom. I've also changed the "friction" parameter so that it takes longer for the graph to stop moving; zoom it while it's still moving, and you'll see that everything will keep moving as before .

"Geometric zooming" of the image is fairly straightforward, it can be implemented with very little code, and it results in fast, smooth changes by the browser. However, often the reason you want to zoom in on a graph is because the datapoints are too close together and overlapping. In that case, just making everything bigger doesn't help. You want to stretch the elements out over a larger space while keeping the individual points the same size. That's where "semantic zooming" comes into place.

This is done by changing the way the position of elements is calculated, as well as the length of any lines or paths that connect objects, without changing the underlying coordinate system that defines how big a pixel is for the purpose of setting line width or the size of shapes or text.

You can do these calculations yourself, using the translate and scale values to position the objects based on these formulas:

semantic zooming in this version of the gridlines example

For the vertical lines, they were originally positioned like this

In the zoom function, that gets changed to

vLines.attr("x1", function(d){
        return d3.event.translate[0] + d*d3.event.scale;
    })
    .attr("y1", d3.event.translate[1])
    .attr("x2", function(d){
        return d3.event.translate[0] + d*d3.event.scale;
    })
    .attr("y2", d3.event.translate[1] + h*d3.event.scale);

The horizontal lines are changed similarly. The result? The position and length of the lines changes on the zoom, without the lines getting thicker or thinner.

It gets a little complicated when we try to do the same for the force layout. That's because the objects in the force layout graph are also being re-positioned after every "tick" event. In order to keep them positioned in the correct places for the zoom, the tick-positioning method is going to have to use the zoomed-position formulas. Which means that:

  • The scale and translation have to be saved in a variable that can be accessed by the tick function; and,
  • There needs to be default scale and translation values for the tick function to use if the user hasn't zoomed anything yet.

The default scale will be 1, and the default translation will be [0,0], representing normal scale and no translation.

semantic zooming on the simplified force layout

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);
    scaleFactor = d3.event.scale;
    translation = d3.event.translate;
    tick(); //update positions
}

It sets the scaleFactor and translation variables, then calls the tick function. The tick function does all the positioning: at initialization, after force-layout tick events, and after zoom events. It looks like

function tick() {
    linkLines.attr("x1", function (d) {
            return translation[0] + scaleFactor*d.source.x;
        })
        .attr("y1", function (d) {
            return translation[1] + scaleFactor*d.source.y;
        })
        .attr("x2", function (d) {
            return translation[0] + scaleFactor*d.target.x;
        })
        .attr("y2", function (d) {
            return translation[1] + scaleFactor*d.target.y;
        });

    nodeCircles.attr("cx", function (d) {
            return translation[0] + scaleFactor*d.x;
        })
        .attr("cy", function (d) {
            return translation[1] + scaleFactor*d.y;
        });
}

Every position value for the circles and the links is adjusted by the translation and the scale factor. If this makes sense to you, this should be sufficient for your project and you don't need to use scales. Just make sure that you always use this formula to convert between the data coordinates (d.x and d.y) and the display coordinates (cx, cy, x1, x2, etc.) used to position the objects.

Where this gets complicated is if you need to do the reverse conversion from display coordinates to data coordinates. You need to do this if you want the user to be able to drag individual nodes -- you need to set the data coordinate based on the screen position of the dragged node. (Note that this wasn't working properly in either of your examples).

For geometric zoom, converting between screen position and data position can be down with d3.mouse(). Using d3.mouse(SVGElement) calculates the position of the mouse in the coordinate system used by that SVGElement. So if we pass in the element representing the transformed visualization, it returns coordinates that can be used directly to set the position of the objects.

The drag function is:

For semantic zoom, however, the SVG coordinates returned by d3.mouse() no longer directly correspond to the data coordinates. You have to factor in the scale and translation. You do this by re-arranging the formulas given above:

zoomedPositionX = d3.event.translate[0] + d3.event.scale * dataPositionX 

zoomedPositionY = d3.event.translate[1] + d3.event.scale * dataPositionY
dataPositionX = (zoomedPositionX - d3.event.translate[0]) / d3.event.scale

dataPositionY = (zoomedPositionY - d3.event.translate[1]) / d3.event.scale

The drag function for the semantic zoom example is therefore

Looking at all the data-to-display conversion functions above, doesn't it make you think "wouldn't it be easier to have a function to do this each time?" That's what the the d3 scales are for: to convert data values to display values.

You don't often see scales in the force-layout examples because the force layout object allows you to set a width and height directly, and then creates d.x and d.y data values within that range. Set the layout width and height to your visualization width and height, and you can use the data values directly for positioning objects in the display.

However, when you zoom in on the graph, you switch from having the entire extent of the data visible to only having a portion visible. So the data values no longer directly correspond to positioning values, and we need to convert between them. And a scale function would make that a lot easier.

In D3 terminology, the expected data values are the domain and the desired output/display values are the range. The initial domain of the scale will therefore by the expected maximum and minimum values from the layout, while the initial range will be the maximum and minimum coordinates on the visualization.

When you zoom, the relationship between domain and range changes, so one of those values will have to change on the scale. Luckily, we don't have to figure out the formulas ourselves, because the D3 zoom behaviour calculates it for us -- if we attach the scale objects to the zoom behaviour object using its .x() and .y() methods.

As a result, if we change the drawing methods to use the scales, then all we have to do in the zoom method is call the drawing function.

semantic zoom of the grid example implemented using scales

/*** Configure zoom behaviour ***/
var zoomer = d3.behavior.zoom()
                .scaleExtent([0.1,10])
        //allow 10 times zoom in or out
                .on("zoom", zoom)
        //define the event handler function
                .x(xScale)
                .y(yScale);
        //attach the scales so their domains
        //will be updated automatically

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);

    //the zoom behaviour has already changed
    //the domain of the x and y scales
    //so we just have to redraw using them
    drawLines();
}
function drawLines() {
    //put positioning in a separate function
    //that can be called at initialization as well
    vLines.attr("x1", function(d){
            return xScale(d);
        })
        .attr("y1", yScale(0) )
        .attr("x2", function(d){
            return xScale(d);
        })
        /* etc. */

The d3 zoom behaviour object modifies the scales by changing their domain. You could get a similar effect by changing the scale range, since the important part is changing the relationship between domain and range. However, the range has another important meaning: representing the maximum and minimum values used in the display. By only changing the domain side of the scale with the zoom behaviour, the range still represents the valid display values. Which allows us to implement a different type of zoom, for when the user re-sizes the display. By letting the SVG change size according to the window size, and then setting the range of the scale based on the SVG size, the graph can be responsive to different window/device sizes.

semantic zoom grid example, made responsive with scales

I've given the SVG percentage-based height and width properties in CSS, which will over-ride the attribute height and width values. In the script, I've moved all the lines which relate to the display height and width into a function that checks the actual svg element for it's current height and width. Finally, I've added a window resize listener to call this method (which also triggers a re-draw).

/* Set the display size based on the SVG size and re-draw */
function setSize() {
    var svgStyles = window.getComputedStyle(svg.node());
    var svgW = parseInt(svgStyles["width"]);
    var svgH = parseInt(svgStyles["height"]);

    //Set the output range of the scales
    xScale.range([0, svgW]);
    yScale.range([0, svgH]);

    //re-attach the scales to the zoom behaviour
    zoomer.x(xScale)
          .y(yScale);

    //resize the background
    rect.attr("width", svgW)
            .attr("height", svgH);

    //console.log(xScale.range(), yScale.range());
    drawLines();
}

//adapt size to window changes:
window.addEventListener("resize", setSize, false)

setSize(); //initialize width and height

The same ideas -- using scales to layout the graph, with a changing domain from the zoom and a changing range from window resize events -- can of course be applied to the force-layout. However, we still have to deal with the complication discussed above: how to reverse the conversion from data values to display values when dealing with node-drag events. The d3 linear scale has a convenient method for that, too: scale.invert(). If w = scale(x) then x = scale.invert(w).

In the node-drag event, the code using scales is therefore:

function dragged(d){
    if (d.fixed) return; //root is fixed

    //get mouse coordinates relative to the visualization
    //coordinate system:
    var mouse = d3.mouse(vis.node());
    d.x = xScale.invert(mouse[0]); 
    d.y = yScale.invert(mouse[1]); 
    tick();//re-position this node and any links
}

semantic zoom force-layout example, made responsive with scales

I'm sure that was a lot longer a discussion than you were expecting, but I hope it helps you understand not only what you need to do, but also why you need to do it. I get really frustrated when I see code that has obviously been cut-and-pasted together from multiple examples by someone who doesn't actually understand what the code does. If you understand the code, it's a lot easier to adapt it to your needs. And hopefully, this will serve as a good reference for other people trying to figure out how to do similar tasks.

Thanks! For "semantic zooming on the simplified force layout", why don't you directly change node x,y attribute in zoom function, instead of changing position in tick() function?

To avoid code duplication. Whenever you are doing something multiple times, try to only write it out once, and then use that function whenever needed. I could have changed the name of tick() to something more meaningful, like redraw(), but the structure is the same. Every time the force-layout issues a tick event, or the zoom behaviour issues a zoom event, all the elements need to be positioned according to both their current d.x and d.y and the current zoom parameters. For the drag events, it is easier, if a bit extra, to update everything than to find the links which need updating.

What exactly does zoom.translate return? In d3, it says Specifies the current zoom translation vector. If not specified, returns the current translation vector, which defaults to [0, 0].. But at which step, is translation vector specified?

The values of d3.event.translate and d3.event.scale are both set inside the zoom behaviour object when it creates the zoom event object. If you ever wanted to change the zoom or scale of the image directly in your program (i.e., not in response to user zoom actions) then you would need to tell the zoom behaviour what translation and scale to use, hence zoom.translate(). For example, if the user picked a city name out of a list, you might want to zoom a map to focus on that city. Otherwise, you won't set the values directly.

You can look at the source code for d3.mouse or d3.behavior.zoom.

svg - semantic zooming of force directed graph in d3 - Stack Overflow

svg d3.js zoom force-layout
Rectangle 27 66

I tried to find a good tutorial to link to, but couldn't find anything that really covered all the issues, so I'm going to write it out step-by-step myself.

First, you need to clearly understand what you're trying to accomplish. This is different for the two types of zooming. I don't really like the terminology Mike Bostock has introduced, (it's not entirely consistent with non-d3 uses of the terms) but we might as well stick with it to be consistent with other d3 examples.

In "geometric zooming" you are zooming the entire image. Circles and lines get bigger as well as farther apart. SVG has an easy way to accomplish this through the "transform" attribute. When you set transform="scale(2)" on an SVG element, it is drawn as if everything was twice as big. For a circle, it's radius gets drawn twice a big, and it's cx and cy positions get plotted twice the distance from the (0,0) point. The entire coordinate system changes, so one unit is now equal to two pixels on screen, not one.

Likewise, transform="translate(-50,100)" changes the entire coordinate system, so that the (0,0) point of the coordinate system gets moved 50 units to the left and 100 units down from the top-left corner (which is the default origin point).

If you both translate and scale an SVG element, the order is important. If translate is before scale, than the translation is in the original units. If translate is after scale, than the translation is in the scaled units.

The d3.zoom.behavior() method creates a function that listens for mouse wheel and drag events, as well as for touch screen events associated with zooming. It converts these user events into a custom "zoom" event.

The zoom event is given a scale factor (a single number) and a translate factor (an array of two numbers), which the behaviour object calculates from the user's movements. What you do with these numbers is up to you; they don't change anything directly. (With the exception of when you attach a scale to the zoom behaviour function, as described later.)

For geometric zooming, what you usually do is set a scale and translate transform attribute on a <g> element that contains the content you want to zoom. This example implements that geometric zooming method on a simple SVG consisting of evenly placed gridlines:http://jsfiddle.net/LYuta/2/

The zoom is accomplished by setting the transform attribute on "vis", which is a d3 selection containing a <g> element which itself contains all the content we want to zoom. The translate and scale factors come directly from the zoom event that the d3 behaviour created.

The result is that everything gets bigger or smaller -- the width of the gridlines as well as the spacing between them. The lines still have stroke-width:1.5; but the definition of what 1.5 equals on the screen has changed for them and anything else within the transformed <g> element.

For every zoom event, the translate and scale factors are also logged to the console. Looking at that, you'll notice that if you're zoomed out the scale will be between 0 and 1; if you're zoomed in it will be greater than 1. If you pan (drag to move) the graph, the scale won't change at all. The translate numbers, however, change on both pan and zoom. That's because the translate represents the position of the (0,0) point in the graph relative to the position of the top-left-corner of the SVG. When you zoom, the distance between (0,0) and any other point on the graph changes. So in order to keep the content under the mouse or finger-touch in the same position on the screen, the position of the (0,0) point has to move.

There are a number of other things you should pay attention to in that example:

geometric zooming method, applied to a simplified version of your force layout

I've reduced the number of nodes and links, and taken away the node-drag behaviour and the node-expand/collapse behaviour, so you can focus on the zoom. I've also changed the "friction" parameter so that it takes longer for the graph to stop moving; zoom it while it's still moving, and you'll see that everything will keep moving as before .

"Geometric zooming" of the image is fairly straightforward, it can be implemented with very little code, and it results in fast, smooth changes by the browser. However, often the reason you want to zoom in on a graph is because the datapoints are too close together and overlapping. In that case, just making everything bigger doesn't help. You want to stretch the elements out over a larger space while keeping the individual points the same size. That's where "semantic zooming" comes into place.

This is done by changing the way the position of elements is calculated, as well as the length of any lines or paths that connect objects, without changing the underlying coordinate system that defines how big a pixel is for the purpose of setting line width or the size of shapes or text.

You can do these calculations yourself, using the translate and scale values to position the objects based on these formulas:

semantic zooming in this version of the gridlines example

For the vertical lines, they were originally positioned like this

In the zoom function, that gets changed to

vLines.attr("x1", function(d){
        return d3.event.translate[0] + d*d3.event.scale;
    })
    .attr("y1", d3.event.translate[1])
    .attr("x2", function(d){
        return d3.event.translate[0] + d*d3.event.scale;
    })
    .attr("y2", d3.event.translate[1] + h*d3.event.scale);

The horizontal lines are changed similarly. The result? The position and length of the lines changes on the zoom, without the lines getting thicker or thinner.

It gets a little complicated when we try to do the same for the force layout. That's because the objects in the force layout graph are also being re-positioned after every "tick" event. In order to keep them positioned in the correct places for the zoom, the tick-positioning method is going to have to use the zoomed-position formulas. Which means that:

  • The scale and translation have to be saved in a variable that can be accessed by the tick function; and,
  • There needs to be default scale and translation values for the tick function to use if the user hasn't zoomed anything yet.

The default scale will be 1, and the default translation will be [0,0], representing normal scale and no translation.

semantic zooming on the simplified force layout

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);
    scaleFactor = d3.event.scale;
    translation = d3.event.translate;
    tick(); //update positions
}

It sets the scaleFactor and translation variables, then calls the tick function. The tick function does all the positioning: at initialization, after force-layout tick events, and after zoom events. It looks like

function tick() {
    linkLines.attr("x1", function (d) {
            return translation[0] + scaleFactor*d.source.x;
        })
        .attr("y1", function (d) {
            return translation[1] + scaleFactor*d.source.y;
        })
        .attr("x2", function (d) {
            return translation[0] + scaleFactor*d.target.x;
        })
        .attr("y2", function (d) {
            return translation[1] + scaleFactor*d.target.y;
        });

    nodeCircles.attr("cx", function (d) {
            return translation[0] + scaleFactor*d.x;
        })
        .attr("cy", function (d) {
            return translation[1] + scaleFactor*d.y;
        });
}

Every position value for the circles and the links is adjusted by the translation and the scale factor. If this makes sense to you, this should be sufficient for your project and you don't need to use scales. Just make sure that you always use this formula to convert between the data coordinates (d.x and d.y) and the display coordinates (cx, cy, x1, x2, etc.) used to position the objects.

Where this gets complicated is if you need to do the reverse conversion from display coordinates to data coordinates. You need to do this if you want the user to be able to drag individual nodes -- you need to set the data coordinate based on the screen position of the dragged node. (Note that this wasn't working properly in either of your examples).

For geometric zoom, converting between screen position and data position can be down with d3.mouse(). Using d3.mouse(SVGElement) calculates the position of the mouse in the coordinate system used by that SVGElement. So if we pass in the element representing the transformed visualization, it returns coordinates that can be used directly to set the position of the objects.

The drag function is:

For semantic zoom, however, the SVG coordinates returned by d3.mouse() no longer directly correspond to the data coordinates. You have to factor in the scale and translation. You do this by re-arranging the formulas given above:

zoomedPositionX = d3.event.translate[0] + d3.event.scale * dataPositionX 

zoomedPositionY = d3.event.translate[1] + d3.event.scale * dataPositionY
dataPositionX = (zoomedPositionX - d3.event.translate[0]) / d3.event.scale

dataPositionY = (zoomedPositionY - d3.event.translate[1]) / d3.event.scale

The drag function for the semantic zoom example is therefore

Looking at all the data-to-display conversion functions above, doesn't it make you think "wouldn't it be easier to have a function to do this each time?" That's what the the d3 scales are for: to convert data values to display values.

You don't often see scales in the force-layout examples because the force layout object allows you to set a width and height directly, and then creates d.x and d.y data values within that range. Set the layout width and height to your visualization width and height, and you can use the data values directly for positioning objects in the display.

However, when you zoom in on the graph, you switch from having the entire extent of the data visible to only having a portion visible. So the data values no longer directly correspond to positioning values, and we need to convert between them. And a scale function would make that a lot easier.

In D3 terminology, the expected data values are the domain and the desired output/display values are the range. The initial domain of the scale will therefore by the expected maximum and minimum values from the layout, while the initial range will be the maximum and minimum coordinates on the visualization.

When you zoom, the relationship between domain and range changes, so one of those values will have to change on the scale. Luckily, we don't have to figure out the formulas ourselves, because the D3 zoom behaviour calculates it for us -- if we attach the scale objects to the zoom behaviour object using its .x() and .y() methods.

As a result, if we change the drawing methods to use the scales, then all we have to do in the zoom method is call the drawing function.

semantic zoom of the grid example implemented using scales

/*** Configure zoom behaviour ***/
var zoomer = d3.behavior.zoom()
                .scaleExtent([0.1,10])
        //allow 10 times zoom in or out
                .on("zoom", zoom)
        //define the event handler function
                .x(xScale)
                .y(yScale);
        //attach the scales so their domains
        //will be updated automatically

function zoom() {
    console.log("zoom", d3.event.translate, d3.event.scale);

    //the zoom behaviour has already changed
    //the domain of the x and y scales
    //so we just have to redraw using them
    drawLines();
}
function drawLines() {
    //put positioning in a separate function
    //that can be called at initialization as well
    vLines.attr("x1", function(d){
            return xScale(d);
        })
        .attr("y1", yScale(0) )
        .attr("x2", function(d){
            return xScale(d);
        })
        /* etc. */

The d3 zoom behaviour object modifies the scales by changing their domain. You could get a similar effect by changing the scale range, since the important part is changing the relationship between domain and range. However, the range has another important meaning: representing the maximum and minimum values used in the display. By only changing the domain side of the scale with the zoom behaviour, the range still represents the valid display values. Which allows us to implement a different type of zoom, for when the user re-sizes the display. By letting the SVG change size according to the window size, and then setting the range of the scale based on the SVG size, the graph can be responsive to different window/device sizes.

semantic zoom grid example, made responsive with scales

I've given the SVG percentage-based height and width properties in CSS, which will over-ride the attribute height and width values. In the script, I've moved all the lines which relate to the display height and width into a function that checks the actual svg element for it's current height and width. Finally, I've added a window resize listener to call this method (which also triggers a re-draw).

/* Set the display size based on the SVG size and re-draw */
function setSize() {
    var svgStyles = window.getComputedStyle(svg.node());
    var svgW = parseInt(svgStyles["width"]);
    var svgH = parseInt(svgStyles["height"]);

    //Set the output range of the scales
    xScale.range([0, svgW]);
    yScale.range([0, svgH]);

    //re-attach the scales to the zoom behaviour
    zoomer.x(xScale)
          .y(yScale);

    //resize the background
    rect.attr("width", svgW)
            .attr("height", svgH);

    //console.log(xScale.range(), yScale.range());
    drawLines();
}

//adapt size to window changes:
window.addEventListener("resize", setSize, false)

setSize(); //initialize width and height

The same ideas -- using scales to layout the graph, with a changing domain from the zoom and a changing range from window resize events -- can of course be applied to the force-layout. However, we still have to deal with the complication discussed above: how to reverse the conversion from data values to display values when dealing with node-drag events. The d3 linear scale has a convenient method for that, too: scale.invert(). If w = scale(x) then x = scale.invert(w).

In the node-drag event, the code using scales is therefore:

function dragged(d){
    if (d.fixed) return; //root is fixed

    //get mouse coordinates relative to the visualization
    //coordinate system:
    var mouse = d3.mouse(vis.node());
    d.x = xScale.invert(mouse[0]); 
    d.y = yScale.invert(mouse[1]); 
    tick();//re-position this node and any links
}

semantic zoom force-layout example, made responsive with scales

I'm sure that was a lot longer a discussion than you were expecting, but I hope it helps you understand not only what you need to do, but also why you need to do it. I get really frustrated when I see code that has obviously been cut-and-pasted together from multiple examples by someone who doesn't actually understand what the code does. If you understand the code, it's a lot easier to adapt it to your needs. And hopefully, this will serve as a good reference for other people trying to figure out how to do similar tasks.

Thanks! For "semantic zooming on the simplified force layout", why don't you directly change node x,y attribute in zoom function, instead of changing position in tick() function?

To avoid code duplication. Whenever you are doing something multiple times, try to only write it out once, and then use that function whenever needed. I could have changed the name of tick() to something more meaningful, like redraw(), but the structure is the same. Every time the force-layout issues a tick event, or the zoom behaviour issues a zoom event, all the elements need to be positioned according to both their current d.x and d.y and the current zoom parameters. For the drag events, it is easier, if a bit extra, to update everything than to find the links which need updating.

What exactly does zoom.translate return? In d3, it says Specifies the current zoom translation vector. If not specified, returns the current translation vector, which defaults to [0, 0].. But at which step, is translation vector specified?

The values of d3.event.translate and d3.event.scale are both set inside the zoom behaviour object when it creates the zoom event object. If you ever wanted to change the zoom or scale of the image directly in your program (i.e., not in response to user zoom actions) then you would need to tell the zoom behaviour what translation and scale to use, hence zoom.translate(). For example, if the user picked a city name out of a list, you might want to zoom a map to focus on that city. Otherwise, you won't set the values directly.

You can look at the source code for d3.mouse or d3.behavior.zoom.

svg - semantic zooming of force directed graph in d3 - Stack Overflow

svg d3.js zoom force-layout