Say that I have an SVG container with a couple of shapes:
<svg>
<g class="container">
<rect id="first" x="0" y="0" width="100" height="100" fill="red" />
<rect id="second" x="100" y="0" width="100" height="100" fill="green" />
<rect id="third" x="200" y="0" width="100" height="100" fill="blue" />
</g>
</svg>
Using D3 I will manipulate the width of these shapes, for example in transitions. How do I make sure that the rects will always stay in this order, without any space between them? That is, if I modify the width of first, x of second and third will update instantaneously.
Option A: Create a treemap and set the sticky option to true: .sticky(true). The treemap layout provides you with x, y, width, and heigth values that you can use to manipulate your DOM/SVG. The sticky option takes care of smooth transitions.
Option B: Use plain html elements, such as div instead of svg:rect elements. If you really just manipulate the width, that should be the more reasonable option:
<style>
#container div{ float: left; }
</style>
<div id="container">
<div id="first" style="width:100px; height:100px; background-color:red;" ></div>
<div id="second" style="width:100px; height:100px; background-color:green;" ></div>
<div id="third" style="width:100px; height:100px; background-color:blue;" ></div>
</div>
Using plain html you can manipulate the width and the browser's layout/CSS engine handles the floating. D3 is not restricted to SVG, it can also handle normal html elements (the treemap example also uses div elements).
Btw: In d3 you should not manipulate the DOM directly. Always think of the underlying data and make the updates data driven, i.e., when using a treemap you would the set the item.value of a data item in your source data, e.g.:
data = [ {value: 100}, {value:200}, {value:100} ]
//...
updateData() //changes some values in your data
drawChart() //draws the whole chart based on the data, e.g., computes the x, y,
//width, height from the `item.value` (e.g., via d3.layout.treemap)
//and manipulates/animates the DOM via d3
I did a variation of a tree layout to make it into a flow layout. You can see the demo here and view the Gist here.
Related
I'm trying to create a text based SVG fill pattern that is dynamic (any text could be used).
I want the text to repeat horizontally and vertically, without having to define width/height of the text object in the pattern definition. This Answer provided a lot of info regarding patternUnits, but it wasn't enough to answer my specific question. The pattern seems to require either absolute width/height (doesn't work with dynamic content) or relative % values that are based on the SVG canvas.
Is it possible for a pattern definition to be sized dynamically to fit its children's bounding box (supporting arbitrary text), while using userSpaceOnUse for the pattern's children to set pixel values?
The usecase is consuming user-provided text, so predefined widths/sizes do not work; the solution needs to be agnostic to the text content.
svg{width:100%;height:100%;}
body{height: 100vh;}
body, html{padding:0;margin:0;}
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="GPattern" x="0" y="0" width="100%" height="100%" patternUnits="userSpaceOnUse">
<text id="text" x="0" y="34" width="100%" height="100%"
style="font-family: Arial;
font-size : 34;
font-weight: 800;
stroke : #000000;
fill : #00ff00;
"
>This Text Should Be Dynamic</text>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#GPattern)"/>
</svg>
Goal Output:
Right now the solution I am considering would be to use JS to calculate an absolute width based on content string.length and manually edit the pattern's width attribute, but would prefer if SVG can calculate this automagically.
Leveraging the answer in https://stackoverflow.com/a/13873631/1577447, you can use Javascript to adjust the size of your pattern.
You can trigger adjustPatternSize() every time your code changes. I’ve baked in the default values in case you want to run this in a context where JS isn’t available.
svg{width:100%;height:100%;}
body{height: 100vh;}
body, html{padding:0;margin:0;}
input { position: absolute; left: 10%; bottom: 10%; }
<input id="textInput" type="text" value="This Text Is Dynamic">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="GPattern" x="-4" y="-1" width="483" height="46" patternUnits="userSpaceOnUse">
<text id="text" x="0" y="34" width="100%" height="100%"
style="font-family: Arial;
font-size : 34;
font-weight: 800;
stroke : #000000;
fill : #00ff00;
"
></text>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#GPattern)"/>
<script type="text/javascript">
const padding = 4
const text = document.getElementById("text");
const rect = document.getElementById("GPattern");
const input = document.getElementById("textInput");
input.oninput = handleInput;
function handleInput(e) {
adjustPatternSize(e.target.value)
}
function adjustPatternSize(string) {
text.textContent = string;
const bbox = text.getBBox();
rect.setAttribute("x",bbox.x - padding);
rect.setAttribute("y",bbox.y - padding );
rect.setAttribute("width",bbox.width + 2*padding);
rect.setAttribute("height",bbox.height + 2*padding);
}
adjustPatternSize("This Text Is Dynamic");
</script>
</svg>
I'm working on some Pen (I wan to create Depth of Field effect), I have html code inside foreign object and I want to apply filter + mask to to that element, for this I need to clone the element and mask it (or is there other way to apply mask to filter?)
I've tried with svg:use
<foreignobject id="code">
<body xmlns="http://www.w3.org/1999/xhtml">
<figure class="highlight">
<!-- HTML -->
</figure>
</body>
</foreignobject>
<use x="0" y="0" width="100%" height="100%"
filter="url(#blur)" mask="url(#mask)" xlink:href="#code"/>
but it don't work with foreign objects:
Here is my pen: https://codepen.io/jcubic/pen/VwwzVXO
Is there any other way I can use foreign objec to mirror it so I can apply filter+mask?
PART 1 -
So I have a bunch of <circle>'s inside a SVG, and I want those circles to be checkboxes. And after that I want to:
PART 2 -
When circle 1 (which is now a checkbox) is clicked, then it is checked. But all the other circles now get unchecked.
This is what I've already tried:
PART 1 - Turning the SVG into a checkbox:
<circle opacity="0.5" cx="842" cy="451.814" r="25.582" class="svg_spot" id="1" fill="#FFB60C" >
<animate attributeName="r" values="25.582; 33; 25.582" keyTimes="0; 0.5; 1" begin="0s" dur="1s" repeatCount="indefinite" calcMode="linear" />
<input type="checkbox" id="spot1" name="spot" class="common_selector spot_id" value="spot1">
</circle>
PART 2 -
$('input[name=spot]').click (function (){
$(this).attr('checked', true);
$('input[name=spot]').not(this).attr('checked', false);
});
Thanks for your time guys. Would appreciate any help!
It is possible to get this working without any Javascript.
How this works:
Put the SVG in the label of the input. Clicking on the SVG (ie the label) will thus activate the field.
Make the actual <input> field invisible, so that all you see is the label.
Style the SVG based on whether the field is selected, by using the :checked pseudo-selector.
// just here to prove that the form value is changing
// prints value of spot field when inputs change
document.querySelectorAll("#spot1, #spot2, #spot3")
.forEach((elem) => elem.addEventListener("change", (evt) => console.log(document.myform.spot.value)));
.common_selector {
position: absolute;
opacity: 0;
}
.common_selector + label svg {
width: 50px;
height: 50px;
fill: red;
}
.common_selector:checked + label svg {
fill: green;
}
<form name="myform">
<input type="radio" id="spot1" name="spot" class="common_selector spot_id" value="spot1">
<label for="spot1">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
<input type="radio" id="spot2" name="spot" class="common_selector spot_id" value="spot2">
<label for="spot2">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
<input type="radio" id="spot3" name="spot" class="common_selector spot_id" value="spot3">
<label for="spot3">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
</form>
<input> is not a valid SVG element - it is a HTML element, so this won't work. You can either:
wrap an input element inside a <foreignObject> element and do it that way, or
you could use positioning to place the input element over the circle. But fair warning - form elements haven't always played well when they're positioned over other types of elements. Or
You can manually draw SVG that looks like an input element and use JavaScript to make it behave like one. or
Since you just need a circle, why not wrap the input element in a Div with an appropriate border radius and make a circle that way.
PART 1:
Use <foreignObect> to display any HTML element inside an SVG:
<foreignObject x="20" y="20" width="100" height="100">
<input type="checkbox" id="spot1" name="spot" class="common_selector spot_id"
value="spot1">
</foreignObject>
Then you can use css to hide default styling of this input field and position your circle over it. You can read about it here: https://www.w3schools.com/howto/howto_css_custom_checkbox.asp
PART 2:
Use Radio Buttons instead of Checkboxes. Checkboxes allow more than one selection. Radio buttons are what you need here. Read about it here: https://www.w3schools.com/tags/att_input_type_radio.asp
I hope I understand your question. You can not change an svg element into an input however you can try to mimic one.
// selects all the circles with a class of radio
let inputs = document.querySelectorAll(".radio")
// for every circle
inputs.forEach(i =>{
//when the circle is clicked
i.addEventListener("click", ()=>{
// remove the class checked from all the circles but the clicked one
inputs.forEach(j =>{if(i!==j)j.classList.remove("checked") })
// toggle the class checked on the clicked one
i.classList.toggle("checked")
})
})
svg{border:1px solid}
.checked{fill:red}
<svg id="theSVG" viewBox="800 410 300 85" width="300">
<circle class="radio" opacity="0.5" cx="842" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
<circle class="radio" opacity="0.5" cx="950" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
<circle class="radio" opacity="0.5" cx="1050" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
</svg>
It sounds like you want to use an SVG as a type of "selector" like a color selector or, in my case a planet selector. Here I have a SVG representing the solar system where the user clicks on a planet to select it. Since it sounds like you only want to select a single item then a radio button is the better option, however the method below should work with either.
As others have suggested the foreignObject element can be used. I had issues getting it work without specifically declaring a prefixed namespaces for the svg and saving the file with a .xhtml extension.
Of course there are plenty of ways to do something similar with javaScript, however a big advantage of using an underlying radio button is that it takes care of all the logic and makes validation easier.
I used an empty div over the circle that is acting as a label for the radio button. Since clicking on the label activates the radio button the circle now functions as a radio button!
The SVG does not show up in the snippet, so the div's are shaded for visibility. The actual radio buttons can be hidden if desired.
div{
border: red solid 1px;
background-color: black;
opacity: 20%;
}
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
<p>Select a planet</p>
<form name="planetData">
<input id="venus" name="planet" type="radio" value="venus" />
<label for="venus">Venus</label>
<input id="earth" name="planet" type="radio" value="earth" />
<label for="venus">Earth</label>
<input id="mars" name="planet" type="radio" value="mars" />
<label for="venus">Mars</label>
</form>
<svg:svg height="100" width="100" viewbox="0 0 100 100">
<svg:circle cx="0" cy="50" r="10" fill="yellow"/>
<svg:circle cx="30" cy="75" r="10" fill="green"/>
<svg:circle cx="60" cy="30" r="10" fill="blue"/>
<svg:circle cx="90" cy="60" r="10" fill="red"/>
<svg:foreignObject x="20px" y="65px" height="20px" width="20px">
<label for="venus">
<div style="height: 30px; width: 30px;">
</div>
</label>
</svg:foreignObject>
<svg:foreignObject x="50" y="20" height="20" width="20">
<label for="earth">
<div style="height:20px; width: 20px;"></div>
</label>
</svg:foreignObject>
<svg:foreignObject x="80" y="50" height="20" width="20">
<label for="mars">
<div style="height:40px; width: 40px;"></div>
</label>
</svg:foreignObject>
</svg:svg>
</html>
I'm working on a mapping project and using SVG <image> elements as tiles always shows a seam between them. I can avoid the seams if I use <rect> elements with the css property "shape-rendering: crispEdges". However, when I try to fill these <rect> elements with a pattern that contains the tile image, there is some inconsistency in the alignment of the pattern in Chrome and FF (I'm using Chrome 45 and FF 41 right now).
I've isolated the issue in this jsfiddle. All of the transforms, svg size, rect size, etc are directly from the project I'm working on and it should be assumed they can't be changed. I can only change the pattern (or if there's a attribute/property like "shape-rendering: crispEdges" for <image> elements I can change that).
How can I get the pattern tile image to fully cover, and be properly aligned with, the <rect>?
HTML
<!-- change background from white to black to see what's going on -->
<!-- The SVG is large. The <rect> can be located a bit to the right of the center of the SVG -->
<!-- When BG is black, you can see a white edge on the left and bottom -->
<!-- When BG is white, you can see a dark edge on the top and right -->
<body style="background: white;">
<div style="position: fixed; x: 0; y: 0;">
<button onclick="whiteBG()">White BG</button>
<button onclick="blackBG()">Black BG</button>
</div>
<svg height="1965" version="1.1" width="5760" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern x="0" y="0" width="100%" height="100%" patternContentUnits="objectBoundingBox" id="patternSieyywid32k">
<!-- image is base64 encoded 415 x 512 image -->
<image xlink:href="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAIAAcMDAREAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAcIBv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAG8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKAAAAAAAAAAAAAAAAAAAADrTrQAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAADB6AAAAAAAAAAAAAAAAAAAAC8F4UAAAAAAYPQAAAAAAAAAAAAAAAAAAAAXgvCgAAAAAD/8QAHBAAAgMBAAMAAAAAAAAAAAAAB1AABRcGBDCQ/9oACAEBAAEFAvlZpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJpdZNLrJQ9L43RORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXORc5FzkXe//xAAUEQEAAAAAAAAAAAAAAAAAAACw/9oACAEDAQE/AWQP/8QAFBEBAAAAAAAAAAAAAAAAAAAAsP/aAAgBAgEBPwFkD//EACYQAAAFBQABBAMBAAAAAAAAAAIFUIPCAAQzktIBAxETYRQwMZD/2gAIAQEABj8C/wArMF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqsF3oHqvX/GB6oPh9vf5fHjx/ff7+lkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbksmbclkzbl+/8A/8QAGhAAAQUBAAAAAAAAAAAAAAAAEQExUXGQsP/aAAgBAQABPyHNZEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSJEiRIkSK+M0YAVLm1Xd3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d//aAAwDAQACAAMAAAAQJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJAAAAAAAAAAAAAAAAAAAAABJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJAAAAAAAAAAAAAAAAAAAAAJJJJJJJIAAAAAAAAAAAAAAAAAAAABJJJJJJJ/8QAGREAAwEBAQAAAAAAAAAAAAAAARFQADCQ/9oACAEDAQE/EPKx555555555555555555555555555555555555555552hZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkWRZFkd//8QAFBEBAAAAAAAAAAAAAAAAAAAAsP/aAAgBAgEBPxBkD//EABsQAAIBBQAAAAAAAAAAAAAAABExASFBkLDR/9oACAEBAAE/EMa1mzZs2bNmzZs2bNmzZs2bNmzZs2bNmzZs2bNmzZs2bNmzZs2bPXuyTRWIcO2tpMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz/AP/Z"
x="0" y="0" width="1" height="1" preserveAspectRatio="none"></image>
</pattern>
</defs>
<g transform="matrix(0.0049,0,0,0.0049,101.5334,3349.9742)" style="display: inline;">
<rect x="641958.5514" y="-516667.078" width="28275.123699999996" height="32099.475199999986" fill="url('#patternSieyywid32k')" style="shape-rendering: crispEdges;"></rect>
</g>
</svg>
</body>
JS
var whiteBG = function() {
document.querySelector('body').style.background = '#FFF';
}
var blackBG = function() {
document.querySelector('body').style.background = '#000';
}
The best solution I could come up with for this issue was to go back to using image elements, but instead of letting the browser handle the transform, I removed the transform from the group element and used some JS code to apply the transforms manually and update the x, y, width, and height attributes of each image element. Doing it this way, I have control over how to round the result to achieve pixel perfect tiles.
Why would a demo such as this: http://jsbin.com/ejorus/2/edit, have an <svg> element nested inside another <svg> element?
<svg class="graph">
<svg viewBox="0 0 1000 1000" preserveAspectRatio="none">
<g transform="translate(30,0)">
<!-- ... -->
</g>
</svg>
</svg>
The JS Bin is a modified version of the demo in this blog post: http://meloncholy.com/blog/making-responsive-svg-graphs/
Nesting SVG elements can be useful to group SVG shapes together, and
position them as a collection. All shapes nested inside an svg element
will be positioned (x, y) relative to the position (x, y) of its
enclosing svg element. By moving the x and y coordinates of the
enclosing svg element, you move all the nested shapes too.
Here is an example where two rectangles are nested inside two svg
elements. Except for the colors the two rectangles have the same
definitions for x, y, height, and width. The enclosing svg
elements have different x-values. Since the x-position of the
rectangles are interpreted relative to their enclosing svg elements
x-position, the two rectangles are displayed at different
x-positions.
- By Jakob Jenkov
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<svg x="10">
<rect x="10" y="10" height="100" width="100"
style="stroke:#ff0000; fill: #0000ff"/>
</svg>
<svg x="200">
<rect x="10" y="10" height="100" width="100"
style="stroke:#009900; fill: #00cc00"/>
</svg>
</svg>
Credits
You're right (as you say in Mr. Alien's answer) that both SVG elements have the same relative positions, and indeed the graph displays fine without the outer SVG.
The reason I added it is because the JavaScript (which I needed to stop the labels getting squished) uses the SVG element's transform matrix (caused by the applied viewBox attribute) to unscale the text.
Unfortunately the returned matrix doesn't take account of transformations applied to the SVG element itself, so I needed to get the transform matrix relative to an outer element that used the initial coordinate system instead. Hope that helps.
You can define a new viewport & viewbox. With this option, you can use relative positions like as css. For more information, you can see this online article.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Nested SVG</title>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<svg width="100%" height="100%">
<rect x="25%" y="25%" width="50%" height="50%" style="fill: dodgerblue;" />
<svg x="25%" y="25%" width="50%" height="50%">
<rect x="25%" y="25%" width="50%" height="50%" style="fill: tomato;" />
</svg>
</svg>
</body>
</html>
I am about to do this for an entirely different reason: website implementation efficiency.
I have several SVG files that I insert into my web page after downloading them via AJAX. I want to consolidate them into one file because that's better for downloading. I could do this with a text file and then insert the SVG text into a web page element's innerHTML property, but .svg files offer two additional advantages over .txt files:
1) The standard .svgz format allows you to store pre-zipped files on the web server, no need for mod_deflate. SVG files zip very efficiently, I'm seeing 70-85% compression.
2) The web browser parses the SVG outside of my javascript code, hopefully more efficiently. To insert the svg element into the HTML, I use the parent element's insertBefore or appendChild method instead of innerHTML.