Aligning SVG polygons - javascript

I have a list with all our team members.
In the design there are 15 different shapes, which fit into each other. So in the perfect situation you would have:
|shape1|shape2|shape3|shape4|shape5|
|shape6|shape7|shape8|shape9|shape10|
|shape11|shape12|shape13|shape14|shape15|
But the width of that section is 100% so what happens is:
|shape1|shape2|shape3|shape4|shape5|shape6|shape7|
|shape8|shape9|shape10|shape11|shape12|
|shape13|shape14|shape15|shape1|shape2|
So what happens is that shape 6 floats next to 5. But that shape6 should be shape1 again. Because that would fit.
So it would be
|shape1|shape2|shape3|shape4|shape5|shape1|shape2|
|shape6|shape7|shape8|shape9|shape10|
|shape11|shape12|shape13|shape14|shape15|
What I basicly need is that every LI element next to a LI element with shape5 turns into a shape1 instead of shape6.
the shapes are added through svg elements(this x 15)
HTML
<svg width="0" height="0">
<clipPath id="polygon-1">
<polygon fill="#939598" points="183,172.776 89.16,172.776 -6.231,189.785 -0.174,1.02 170.255,37.688 "/>
</clipPath>
</svg>
CSS
.team article.image-shape.polygon-1{
-webkit-clip-path: url(#polygon-1);
}
I am not quit sire how to tackle this problem. If you guys have any suggestions I could look into, it would be very helpful.
What have I been trying?
I have been counting the LI elements within the UL and adding the 6th one class one, but obviously that isn't working when you are scaling down because you would get:
|shape1|shape2|shape3|shape4|shape5|
|shape1|shape7|shape8|shape9|shape10|
|shape11|shape12|shape13|shape14|shape15|

Related

How can I divide an image into hoverable sub sections?

Is there someway I can have hover only access the exact dimensions of the colored areas of this head (not the excess corners of the box model)?
If I was creating a website where you could hover over the sections of the human body and click for information regarding the clicked section, would I have to piece the body together with individual divs or is there a better way to divide an image into hover selectable sections? (not sure if im using the correct terminology!)
Just to reiterate, I only want to be able to select the colored areas in their exact dimensions (not white space outside of the colored sections/outside of the box model). Any suggestions?
Maybe <map>/<area> elements could of help: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area
HTML <map> tag is what you are looking for.
An image-map is an image with clickable areas.
The required name attribute of the element is associated with
the 's usemap attribute and creates a relationship between the
image and the map.
The element contains a number of elements, that defines
the clickable areas in the image map.
You can have your detailed step-by-step turorial HERE and some detailed explanation HERE.
If your image would consist of strict rectangles only there would be easier options.
Anyway, you can load the image into a vector drawing application like Illustrator and trace the outlines of the shapes by hand. After that's done export the drawing as a .svg.
If you take a look at the svg file you will see that the individual shapes are stored as tags. For example:
<path fill="#44CACF" d="M42.124,213.07l64.5-1l-1.5,66l-51.5,35C53.624,313.07,14.124,246.57,42.124,213.07z"/>
If you give these an id attribute
<path fill="#44CACF" id="partC" d="M42.124,213.07l64.5-1l-1.5,66l-51.5,35C53.624,313.07,14.124,246.57,42.124,213.07z"/>
you can access it using javascript and add mouse events as you desire.
Here's an example:
function enter(e) {
e.target.style.opacity = 0.5;
}
function leave(e) {
e.target.style.opacity = 1;
}
document.getElementById("partA").addEventListener("mouseenter", enter);
document.getElementById("partB").addEventListener("mouseenter", enter);
document.getElementById("partC").addEventListener("mouseenter", enter);
document.getElementById("partD").addEventListener("mouseenter", enter);
document.getElementById("partE").addEventListener("mouseenter", enter);
document.getElementById("partA").addEventListener("mouseleave", leave);
document.getElementById("partB").addEventListener("mouseleave", leave);
document.getElementById("partC").addEventListener("mouseleave", leave);
document.getElementById("partD").addEventListener("mouseleave", leave);
document.getElementById("partE").addEventListener("mouseleave", leave);
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="247.741" height="344.813" viewBox="0 0 247.741 344.813" overflow="visible" enable-background="new 0 0 247.741 344.813" xml:space="preserve">
<path fill="#CF454B" id="partA" d="M14.624,131.57c0,0-2-44,13.5-67s32.5-39,55-45.5s40-7.5,55.5-5s36,4,46,12.5s30,30,36.5,39
s15.5,26.5,15.5,35.5s2.5,15,0.5,19.5s-15.5,14-21,15s-201.5,2-201.5,2V131.57z"/>
<path fill="#7F45CF" id="partB" d="M14.124,140.07l203-2.5c0,0,17-9,20-12.5s0.5,11.5,0.5,11.5l-4.5,18l-5.5,23.5l-15,29.5l-170,2.5
C42.624,210.07,5.624,178.57,14.124,140.07z"/>
<path fill="#44CACF" id="partC" d="M42.124,213.07l64.5-1l-1.5,66l-51.5,35C53.624,313.07,14.124,246.57,42.124,213.07z"/>
<path fill="#44CACF" id="partD" d="M142.124,211.57l70.5-0.5c0,0,1.5,88.5-20,104.5s-25-42-51-56L142.124,211.57z"/>
<path fill="#45CE7C" id="partE" d="M108.624,213.57l-1.5,63.5l-53.5,36c0,0,49.5,57.5,124.5,10c0,0-13-23.5-17-32.5s-20.5-24.5-22.5-26
s2-49.5,2-49.5"/>
<g>
<g>
<path fill="none" d="M59.624,333.57c-9-11.5-17-22-23-35.5c-2.5-5.5-5-11.5-6.5-17.5c-0.5-3-0.5-7-2-9.5c-1.5-5-6.5-7.5-9.5-12.5
c-7.5-12.5-22-47.5-15.5-62c3.5-7.5,12.5-5,14.5-13c2-7.5-5.5-17-6.5-24.5c-1-10.5,0-21.5,0-32c0-10-1.5-21,0-31
c2.5-19,20-37.5,31.5-52c11.5-15,27-23.5,44.5-31c13-5.5,28-10.5,42-11.5c10-0.5,19.5,3.5,29,6c20,5.5,38,12,52,28
c13,15.5,21,31,27,50c5,17,10.5,33.5,9,51.5c-2,19.5-16.5,33-21,51.5c6.5,2.5,12.5-1.5,16,6.5c2.5,6.5,0,17-1,23.5
c-1.5,9-2.5,19.5-6.5,27.5c-5,10-14.5,16-19.5,26c-7.5,15.5-1.5,35.5-16.5,47.5c-11.5,9-28.5,13.5-42,18.5c-17,5.5-34,5-52,5
c-15,0-32-0.5-44.5-10.5"/>
<path d="M60.762,332.604c-9.078-11.613-17.466-23.343-23.539-36.839c-3.107-6.904-5.255-13.645-6.37-21.11
c-0.841-5.631-4.562-9.469-8.302-13.416c-6.492-6.854-10.047-17.929-13.238-26.606c-3.649-9.927-7.015-21.035-6.2-31.732
c0.423-5.549,2.972-8.828,7.999-11.088c3.796-1.707,6.611-3.603,7.912-7.725c1.328-4.208-0.39-8.836-1.904-12.738
c-1.039-2.677-2.266-5.276-3.301-7.955c-1.67-4.322-1.583-8.987-1.648-13.553c-0.107-7.544,0.4-15.087,0.439-22.631
c0.042-7.941-0.794-15.854-0.619-23.801c0.327-14.881,7.469-27.208,16.122-38.86c7.877-10.607,16.325-22.004,26.267-30.735
c10.153-8.917,22.654-14.903,35.021-20.081c14.179-5.937,30.953-12.106,46.565-10.361c7.387,0.826,14.504,3.658,21.641,5.597
c6.507,1.769,12.952,3.758,19.259,6.148c12.759,4.834,24.456,12.177,33.223,22.746c8.938,10.775,15.94,22.884,21.225,35.825
c5.654,13.847,9.963,28.944,12.437,43.696c1.249,7.448,1.627,15.179,0.458,22.656c-1.124,7.188-4.419,13.957-7.797,20.319
c-4.792,9.026-10.096,17.707-12.687,27.692c-0.198,0.763,0.053,1.646,0.882,1.917c3.994,1.309,8.962,0.2,12.6,2.284
c5.723,3.278,3.348,14.424,2.539,19.606c-1.952,12.5-2.695,26.565-9.734,37.418c-6.213,9.581-15.568,16.064-19.245,27.304
c-3.225,9.854-2.545,20.52-5.956,30.342c-4.303,12.391-17.536,17.938-28.839,22.344c-12.355,4.817-25.046,9.401-38.232,11.149
c-13.678,1.813-27.839,1.399-41.609,1.084c-12.773-0.292-25.747-2.187-36.148-10.153c-1.531-1.172-3.236,1.277-1.712,2.445
c9.989,7.649,22.168,9.926,34.471,10.567c13.874,0.724,28.131,0.622,41.97-0.614c13.793-1.232,26.558-5.946,39.475-10.632
c10.981-3.983,23.473-8.927,30.333-18.921c5.991-8.729,5.857-20.339,7.578-30.378c1.116-6.509,3.493-12.179,7.467-17.46
c4.03-5.354,9.028-9.867,12.893-15.365c7.248-10.313,7.711-24.044,9.79-36.05c1.379-7.964,4.44-22.313-5.488-26.183
c-3.481-1.357-7.519-0.41-11.113-1.587c0.294,0.639,0.588,1.278,0.881,1.917c3.188-12.286,10.521-22.688,15.877-34.045
c5.766-12.226,6.275-24.966,4.288-38.176c-2.108-14.005-6.555-28.165-11.494-41.417c-4.563-12.247-10.906-23.686-18.659-34.191
c-7.413-10.045-16.488-18.284-27.638-24.006c-11.763-6.036-24.382-9.234-36.968-13.017c-6.304-1.895-12.56-3.687-19.155-4.15
c-7.216-0.506-14.777,1.344-21.713,3.096C97.538,6.597,84.376,12.106,72.02,18.524c-10.839,5.629-20.65,12.879-28.608,22.182
C34.829,50.739,26.2,61.18,19.102,72.324c-7.223,11.34-10.278,22.922-10.105,36.327c0.095,7.343,0.71,14.653,0.604,22.003
c-0.111,7.689-0.652,15.384-0.306,23.075c0.272,6.046,1.634,10.702,4.006,16.22c2.646,6.154,5.938,14.373-1.634,18.324
c-3.538,1.846-6.774,2.956-9.169,6.337c-2.165,3.056-2.443,7.521-2.493,11.111c-0.136,9.845,2.954,19.747,6.165,28.942
c2.935,8.405,6.375,17.402,11.316,24.872c3.981,6.018,9.499,9.415,10.644,17.082c1.08,7.229,3.453,13.727,6.397,20.428
c6.045,13.762,14.749,25.707,23.959,37.49C59.671,336.053,61.946,334.12,60.762,332.604L60.762,332.604z"/>
</g>
</g>
</svg>

How to manipulate the layer of a element in an existing SVG?

I'm using d3 to manipulate an existing svg. The svg appears to have multiple layers. I'm able to get a handle to an expected element and manipulate it with d3. However, the element is on a lower layer. For example, I can set stroke (border color) and stroke-width on the element through d3 and I can see the updated border expanding out from beneath a higher layer with the same shape.
I need to figure out how to dynamically change element layers as needed on the fly. I tried setting z-index style and attr to 999 for the layer I'm trying to raise. No other z-index attrs exist in the svg so my assumption was that setting an element z-index to 999 would most likely raise it to the top but this did not happen. This assumption was mainly based on my background in html/css.
Can you recommend some basic troubleshooting steps for this? Is svg layering implementation and manipulation more complex than what I have in mind? Can you recommend any resources or possible shortcuts?
There is no z-index in a SVG. In an SVG, the order of the elements defines the order of the "painting", and the order of the painting defines who goes on top. The specs are clear:
Elements in an SVG document fragment have an implicit drawing order, with the first elements in the SVG document fragment getting "painted" first. Subsequent elements are painted on top of previously painted elements.
Therefore, you'll have to reposition the elements. There is a very simple solution, just do:
selection.raise();
Raise re-inserts each selected element, in order, as the last child of its parent.
Here is a demo, hover over the circle to bring it to the top:
d3.selectAll("circle").on("mouseover", function(){
d3.select(this).raise()
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="400" height=200>
<circle cy="100" cx="80" r="60" fill="blue"></circle>
<circle cy="100" cx="160" r="60" fill="yellow"></circle>
<circle cy="100" cx="240" r="60" fill="red"></circle>
<circle cy="100" cx="320" r="60" fill="green"></circle>
</svg>
Note that raise() will only work for elements in the same level (that is, having the same parent element).

Adapt Div area to Background img size

I haven't found any solution for this problem but I'm sure that one exists.
I have 4 divs with background images, together comprising a graphical option wheel.
The problem is that each div's background image is triangular, but the area that is occupied by the div is bigger and is a square. When I pass the cursor over each div, it is not working well, because the first div overlaps on top of the second, the second over the third, etc.
I thought about using z-index but that won't work because the first and the second div overlap one another, and so do the third and fourth.
I'm not sure if I've explained my problem very well. If you don't understand something, please let me know.
Here is an image to help you understand what I mean.
Thanks!!
UPDATE WITH CODE
Here is my code:
https://jsfiddle.net/ialex90/x7mx1zqu/
You can only achieve this effect with SVG. See the following base implementation that illustrates how you can both apply CSS and JS to irregular shapes using SVG:
$('path').click(function(e) {
alert(e.target.id);
});
path {
stroke:red;
stroke-width:1;
fill:rgba(255,0,0,0.15);
transition:fill 0.5s;
}
path:hover {
fill:rgba(255,0,0,0.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg viewBox="0 0 500 250" width="500" height="250">
<g>
<path id="quadrant1" d="M0,250 A250,250 0 0,1 73,73 L161,161 A125,125 0 0,0 125,250 z" />
<path id="quadrant2" d="M73,73 A250,250 0 0,1 250,0 L250,125 A125,125 0 0,0 161,161 z" />
</g>
</svg>

SVG Scaling and Coordinates

This is my first SVG project, and I’m not a programmer, but I dabble in interactive infographics. My previous experience in this area comes from working with ActionScript.
I’m using plain SVG (no Raphael, D3, etc.) and trying to create an interactive barchart. After some initial difficulty with the SVG coordinate system and scaling, I found some code online that handles the postscaling translation:
<text x="x_coord0" y="y_coord0" transform="scale(x_scale, y_scale) translate(-x_coord0*(x_scale-1)/x_scale, -y_coord0*(y_scale-1)/y_scale)" …>text</text>
And I converted it into this JavaScript:
var translationfactor = ((0 - y_position)*(y_scalefactor - 1) / y_scalefactor);
var matrix = "scale(1," + y_scalefactor + ") translate(0," + Number(translationfactor) + ")";
targetbar.setAttribute("transform", matrix);
The problem is that I need the bars “translated” back to the chart’s baseline, not the original locations of their topmost points. Currently the correctly scaled bars are hugging the top of the chart:
http://billgregg.net/miscellany/upsidedown-barchart.png
I’ve tried several fixes, including plugging the bars’ ”missing height” into translationfactor (the bars start out the full height of the chart and get scaled down dynamically). Nothing has worked. Part of my problem is that, besides being new to SVGs, I can stare at that code all day and my brain still can’t parse it. Multiplying negative numbers is too abstract and at a fundamental level I just don’t “get” the math, which of course makes modifying the code difficult.
My questions:
(1) What’s the fix for the code above to position the bars back on the baseline of the chart?
(2) Is there a more transparent, more pedestrian way of accomplishing the translation? My first thought along these lines was that if a bar’s height is reduced to 40% of its original value, then multiplying the original Y coordinate value by 250% should reset the bar to its original location (at least its topmost point), but that doesn’t seem to work.
(3) Is there a way to set a bar’s point of origin to its bottom? In Flash it’s possible, though as far as I know it’s a manual, not a programmatic task.
(4) Is there a method similar to .localToGlobal() in ActionScript that would allow me to avoid having to mess with the local coordinate system at all?
Behind the scenes there is matrix math going on and it can be hard to get your head around the pre and post multiplication of arrays.
It's not entirely clear what you are trying to achieve, but reading between the lines, it sounds like you are wanting to provide graph coordinates in their raw(ish) form and have the SVG scale and position them for you(?)
If that's the case, then I think the solution is simpler than what you think.
Assuming I'm right, we'll start with something that looks like this:
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="">
<rect x="0" width="1" height="5" fill="red"/>
<rect x="1" width="1" height="11" fill="green"/>
<rect x="2" width="1" height="12" fill="orange"/>
<rect x="3" width="1" height="8" fill="blue"/>
</g>
</svg>
Where x is obvious and the bar length is in height. y defaults to 0, so we don't need it here.
You basically want to know what goes in the transform to scale and position the bars on your page. The fact that your graph is "upside-down" helps a little. Because the origin in an SVG is at the top left.
First apply a scale. Let's make the bars 20 pixels wide, and scale the lengths up by 10.
<g transform="scale(20,10)">
Next you want to position the graph on the page. Let's put the top-left corner at (40,40).
In SVG the transformations are concatenated in order (post-multiplied) so in order for the translation to be what you specify and not be multiplied by the scale, you should put it first.
<g transform="translate(40,40) scale(20,10)">
So the final SVG looks like this:
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="translate(40,40) scale(20,10)">
<rect x="0" width="1" height="5" fill="red"/>
<rect x="1" width="1" height="11" fill="green"/>
<rect x="2" width="1" height="12" fill="orange"/>
<rect x="3" width="1" height="8" fill="blue"/>
</g>
</svg>
The above has been simplified by assuming you have already subtracted the values from your base 20%. If you wanted to keep the pure raw values, it's possible, but things get a bit trickier. You would need to either tinker with both the y and height value of each bar, or use clipping to hide the part of the bar above 20%.
For "right way up"/normal graphs. All you need to do is make the y scale negative and translate the graph so that the bottom-left is where you want it.
<g transform="translate(40,140) scale(20,-10)">
Hope this helps.

Sorting animated GIFs in SVG without stopping the animation using D3/JavaScript

My D3-based visualization produces an HTML5 SVG element with animated GIFs in it. For simplicity, take this example:
<svg>
<image href="animated.gif"></image>
<svg>
Upon mouseover, I'd like to highlight the image by putting a circle behind with a fading gradient for a glow effect. As SVG renders elements on top of each other, the output must look like this:
<svg>
<circle class="gloweffect"></circle>
<image href="animated.gif"></image>
<svg>
After two wasted days, I gave up trying to insert the circle element at the correct position immediately with D3's insert. It just didn't work, esp. since the visualization contains lots of other stuff and the insert position is hard to express.
So instead, I use D3's append to add the circle at the end. Then I call a sorter which removes all elements from the SVG, sorts them, and re-appends them in the correct order:
<svg>
<image href="animated.gif"></image>
<circle class="gloweffect"></circle>
<svg>
--> remove everything
<svg>
<svg>
--> sort and reinsert
<svg>
<circle class="gloweffect"></circle>
<image href="animated.gif"></image>
<svg>
And here comes the challenge: This works fine in all browsers, except ... wait for it ... IE9. (Okay, lame wait.)
As soon as the image element is removed, IE9 stops the GIF animation and does not restart or continue it upon reinsertion. The image simply gets stuck at the first frame and stays that way.
So my question: Is there a way to make IE9 continue the animation after reinsertion? I found plenty of old threads regarding regular img elements, esp. suggesting to reset the picture in a delayed thread, but none of them seems to work for the image element in SVG.
--Florian
Instead of inserting and removing elements (which is expensive and error prone) just make them invisible, you could do something such as...
<svg>
<g class="glow">
<circle class="gloweffect"></circle>
<image href="animated.gif"></image>
</g>
<svg>
And then in your CSS:
g.glow .gloweffect {
opacity: 0;
}
g.glow:hover .gloweffect {
opacity: 1;
}

Categories