I need to write some svgs on pdf file at precise position using javascript.
My webapp let the users make their drawings then I crop these drawings to remove unused white space and save them on a pdf.
Cropping svg adds viewBox attribute which isn't supported by the majority of js pdf library available.
This means that position and scale factor are wrong!
So my attempt to solve the problem was simplify the svg before put it on pdf file.
As far as I know there are few utils that could do this task:
svgcleaner
svgo
scour
Unfortunately none of them completely remove viewbox attribute.
Consider that this task should be completely automated so using programs like inkscape or Adobe Illustrator is not a possible solution.
As I've commented:
The value of the viewBox attribute is a list of four numbers: min-x, min-y, width and height. If min-x = min-y = 0 you can use the other 2 numbers as the width and the height of the svg element. However when you are cropping the svg element the min-x & min-y won't be 0. This means that you will need to wrap everything in a group and translate the group in the opposite direction: - min-x & - min-y.
Alternatively you can first convert the d attribute to all relative commands and instead of translating you can change the first move-to (M) values, thus recalculating the path.
Please read the comments in my code.
// get the viewBox of the original svg element and split it by space or commas
let vb = original.getAttribute("viewBox").split(/[ ,]+/);
//console.log(vb); // ["103", "118", "94", "83"]
//set the width and the height of the copy
copy.setAttribute("width",vb[2]);
copy.setAttribute("height",vb[3]);
// get the d attribute of the original
let path = document.querySelector("#original path").getAttribute("d");
let pathRel = Snap.path.toRelative(path);
//console.log(pathRel)
//change the coords of the first move to command
pathRel[0][1] -= vb[0];
pathRel[0][2] -= vb[1];
//a variable to be used as the d attribute for the copy path
let d = ""
pathRel.forEach(p=>{d+=(p.join(" "))})
//console.log(d)
//set the d attribute of the copy path
document.querySelector("#copy path").setAttribute("d",d)
#original{width:94px;}
svg{border:solid}
<script src="https://cdn.jsdelivr.net/snap.svg/0.3.0/snap.svg.js"></script>
<svg id="original" viewBox="103 118 94 83"><path d="M144.974,122.645Q150,114,155.026,122.645L194.974,191.355Q200,200,190,200L110,200Q100,200,105.026,191.355Z"></path></svg>
<svg id="copy" ><path></path></svg>
Related
I need to replace part of SVG image (map of hall seats) and it doesn't work as I would expect.
I have <g transform="translate(383.25,-48.882672)"><g .. node containing few rect and path elements which build an icon. I can find g by class with jQuery and .html(other_path_content) which makes content appear in the original size (too big to fit the original item spot). When I resize new icon <path d=.. manually and html it again, it goes off the place and appears in different location than original icon.
The problem here is that SVG images I get are random so I can't calculate place of the original icon and transform it to be there as it will be different everytime.
Is that possible to somehow calculate position of the original element and its size (mind it contains of few rects and pathes) and put new one on exact same place? Or maybe I'm missing something in my approach (do I need to re-init svg after appending new node etc.)?
I want to create an animation of a path, like a journey/timeline. The user is shown a circle (eventually to be an image), when they click this circle the animation begins and shows a path animating/traveling to another circle with a fade in effect. I have attached an image which I think explains my idea best.
My question is - what would be the recommended way of doing this? css animation or is there a jquery library that would be helpful?
Thank you
I would take svg as base. With Inkscape (or similar) like that, you can design the path visually and include the blue circle.
Than you can inject the svg-code in your html like so (copy the svg code from the generated file):
<div class="svg-container">
<svg>…</svg>
</div>
Finally you can use javascript to reference the circle and the path:
var path = document.querySelector('.path'), //these selectors are just arbitrary
circle = document.querySelector('.circle');
To get a point on the path, you can use:
var point = path.getPointAtLength();
For animation, I assume that you basically know how to do that, since this would be too much to explain here. But lets say that p is the progress of you animation and will be in the range [0,1]. To calculate a point at a given p could be done like so:
let pointAtT = (path, t) => {
let l_total = path.getTotalLength();
return path.getPointAtLength(l_total * t);
}
Having that, you can use the x and y coordinate to manipulate the circle. Be aware of possibly applied transformations, that is why I recommend to transform everything to global coordinate space, calculate there and transform the result back to the item's coordinate space.
Documentation on mdn
There are a some svg libraries that might help you: svg.js, snap.svg and Raphaël.
I am wondering if it would be possible to resize a path using values inputted by the user after he draws the path. Setting the width and height for example.
I am currently using the transform attribute within a JS function to do that but while the path gets resized, the handles of the path stay in the original position which makes the thing a total mess.
Any ideas?
Assuming the path is is selected:
svgCanvas.changeSelectedAttribute("transform", "scale(0.5, 1)");
Is there any accurate way to get the real size of a svg element that includes stroke, filters or other elements contributing to the element's real size from within Javascript?
I have tried pretty much everything coming to my mind and now I feel I'm coming to a dead end :-(
Updated question to add more context (Javascript)
You can't get the values directly. However, you can get the dimensions of the bounding rectangle:
var el = document.getElementById("yourElement"); // or other selector like querySelector()
var rect = el.getBoundingClientRect(); // get the bounding rectangle
console.log( rect.width );
console.log( rect.height);
It is supported at least in the actual versions of all major browser.
Check fiddle
Both raphael js http://dmitrybaranovskiy.github.io/raphael/ and d3 js http://d3js.org/ have various methods to find the size of an svg object or sets of svg object. It depends on if it's a circle, square, path, etc... as to which method to use.
I suspect you are using complex shapes, so in that case bounding box would be your best bet http://raphaeljs.com/reference.html#Element.getBBox
(Edit: updated reference site.) http://dmitrybaranovskiy.github.io/raphael/reference.html#Element.getBBox
Here is an example using D3.js:
Starting with a div:
<div style="border:1px solid lightgray;"></div>
The javascript code looks like this:
var myDiv = d3.select('div');
var mySvg = myDiv.append('svg');
var myPath = mySvg.append('path');
myPath.attr({
'fill': '#F7931E',
'd': 'M37,17v15H14V17H37z M50,0H0v50h50V0z'
});
// Get height and width.
console.log(myPath.node().getBBox());
If it is an SVG used as a CSS background image and you're using React you can use background-image-size-hook.
import { useBackgroundImageSize } from 'background-image-size-hook'
const App = () => {
const [ref, svg] = useBackgroundImageSize()
console.log(svg) // { width, height, src }
return <SVGBackgroundImageComponent ref={ref} />
}
You didn't specify any programming language. So I can suggest to use Inkscape.
In the file menu you find document's properties and in the first page there's "resize page to content" command. In this way you remove all the white space around your draw and you see the real size. After width and height values apprear inside the header of svg.
I know that Inkscape supports scripting and command line operations but I don't know if it's possible to do the trimming operatation in this way. But if it's possible you can do that from every programming language.
Quick question involving javascript canvas... I have a set points (connected with a line) I want to graph on a 400x300 canvas element. I will constantly be adding more points. I need the line to stretch to fill the entire canvas (leaving no unnecessary space).
Example:
into this:
Thanks! C.Ruhl
You want to find the step by doing canvasWidth / (number of points - 1)
and adding X += step each time.
Example here:
http://jsfiddle.net/pDDTQ/
Distinguish between internal canvas size and visible size. 400x300 is your visible size and set by style="width:400px; height:300px". Everytime there is new point (e.g. 400,500) you set canvas.width=400; canvas.height=500; and replot the whole graph. From a certain point you might want to adjust the width of the line.