svg appending text element - gives me wrong width - javascript

i'm appending a text element to a svg via javascript. After appending i wanna set x and y coordinate, however, it returns me the wrong width of the text element when using it to calculate x.
Interesting:
In Chrome, when actualize the page via F5 or button it returns wrong width, when pressing enter in the adress bar, the width is right - strange!
Here is the small code:
var capt = document.createElementNS("http://www.w3.org/2000/svg", "text");
// Set any attributes as desired
capt.setAttribute("id","capt");
capt.setAttribute("font-family","Righteous");
capt.setAttribute("font-size","30px");
capt.setAttribute("fill", "rgb(19,128,183)");
var myText = document.createTextNode(this.options.captTxt);
capt.appendChild(myText);
this.elements.jSvgElem.append(capt);
capt.setAttribute("x", this.options.windowWidth-this.options.spacer-document.getElementById("capt").offsetWidth);
capt.setAttribute("y", this.options.captY+$('#capt').height());

OK, the problem seems to be that the browser doesn't calculate the correct width when using an other font. Not setting a font results in a correct width.
I solved the problem by setting the reference point ("alignment-point") to the upper right corner ot the text element by setting attributes:
capt.setAttribute("text-anchor", "end");
capt.setAttribute("alignment-baseline", "hanging");
This way i do not have to subtract the width and add the height of the element!

There is a bug:http://code.google.com/p/chromium/issues/detail?id=140472
it just pre init some functions that calculates text width so you should call this function before(i'm sure that there is several extra lines that can be deleted):
fixBug = function () {
var text = makeSVG("text", { x: 0, y: 0, fill: "#ffffff", stroke: '#ffffff'});
text.textContent = "";
var svg = $("svg")[0];
svg.appendChild(text);
var bbox = text.getBBox();
var Twidth = bbox.width;
var Theight = bbox.height;
svg.removeChild(text);
}
$("svg") - Jquery selector

Related

Why size and position of html elements are changed when it is embeded in the foreign object of SVG?

I want to embed Amchart3/Webix charts into the SVG element. First of all I created foreign object in the SVG document, then set its position and size. Then appended Webix chart (which is html element like div and canvas) into the foreign object. It was displayed, however, the size of the chart got bigger. I tried amchart3, it also displayed larger than usual. What is the problem?
var elem = this.el(animEl);
var parent = elem.parentNode;
var foreignObject = document.createElementNS('http://www.w3.org/2000/svg', "foreignObject");
var x = (elem.getAttribute('x'));
var y = (elem.getAttribute('y'));
var width = (elem.getAttribute('width'));
var height = (elem.getAttribute('height'));
foreignObject.setAttribute("x", x);
foreignObject.setAttribute("y", y);
foreignObject.setAttribute("height", height);
foreignObject.setAttribute("width", width);
foreignObject.appendChild(chart.getNode());
parent.appendChild(foreignObject);

Set width of ace editor instance according to the length of characters in it

I am working on the project where I have created a custom Rich Text Editor using contenteditable attribute. In this rich text editor I want insert single line ace editor instance of which width will be set according to the number of characters in it.
For restricting the ace editor instance to single line I have handled the "Enter" key event which does not let the ace instance to insert new line.
var editor = ace.edit(script_editor);
editor.commands.on("exec", function (e) {
editor.container.querySelector(".ace_content").style.transform = "none";
if (e.args && e.args.charCodeAt(0) == 10) {
e.preventDefault();
e.stopPropagation();
console.log("vdgscript-mode.js")
}
});
Now, the problem I am facing is that I want the ace instance width to adjust according to the number of character in it instead to have full width.
For that I am taking a canvas object and calculating the width of the text. But the problem with this code is, it is giving me the expected width on every key press but the css left property of the ace editor does not stay '0px' which makes the text in the ace editor instance to hide at the left side.
Code for setting the width is as follows:
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
ctx.font = "15px sans-serif";
var width = ctx.measureText(code).width;
editor.container.style.width = (width + 3) + "px";
Actual Result: .
Expected Result: .
The black in the below image the ace instance in which I have entered an array.
you can use a method similar to the one used by the tree rename editor in cloud9 https://github.com/c9/core/blob/master/plugins/node_modules/ace_tree/lib/ace_tree/edit.js
<style>
#inlineEditor {
display: inline-block;
vertical-align: middle;
}
</style>
<div>inline editor <span id=inlineEditor>txt</span></div>
<script src=https://ajaxorg.github.io/ace-builds/src-noconflict/ace.js>
</script>
<script>
var inlineEditor = ace.edit("inlineEditor", {
maxLines: 1,
showGutter: false,
showPrintMargin: false,
theme: "ace/theme/solarized_light",
})
// make cursor movement nicer for
inlineEditor.renderer.screenToTextCoordinates = function(x, y) {
var pos = this.pixelToScreenCoordinates(x, y);
return this.session.screenToDocumentPosition(
Math.min(this.session.getScreenLength() - 1, Math.max(pos.row, 0)),
Math.max(pos.column, 0)
);
};
inlineEditor.renderer.on("beforeRender", updateSize)
function updateSize(e, renderer) {
var text = renderer.session.getLine(0);
var chars = renderer.session.$getStringScreenWidth(text)[0];
var width = Math.max(chars, 2) * renderer.characterWidth // text size
+ 2 * renderer.$padding // padding
+ 2 // little extra for the cursor
+ 0 // add border width if needed
// update container size
renderer.container.style.width = width + "px";
// update computed size stored by the editor
renderer.onResize(false, 0, width, renderer.$size.height);
}
updateSize(null, inlineEditor.renderer)
</script>

Drawing an SVG dynamically using javascript

I'm trying to draw an isoceles triangle with it's top vertex in the middle of the screen.
I want to use JavaScript as screen sizes of different users can be different and hence, center of the screen must first be found out.
Here is my code:
function drawRect(){
var w = $(document).width();
var h = $(window).height();
var halfh = h/2;
var halfw = w/2;
var svg = document.createElement("svg");
var poly = document.createElement("polygon");
svg.setAttribute("style", "position: fixed;");
svg.setAttribute("height", ""+h);
svg.setAttribute("width", ""+w);
poly.setAttribute("points", ""+halfw+","+halfh+" 0,"+h+" "+w+","+h);
poly.setAttribute("style", "fill:lime;stroke:purple;stroke-width:1");
svg.appendChild(poly);
var svgplace = document.getElementById("carpet");
svgplace.appendChild(svg);
}
No triangle appears on the screen. But, if I open the 'Inspect Element' console on chrome, and I modify the created html element slightly, it appears! (Any kind of small mod. Like adding a space in the middle somewhere)
Please help!
Thanks in advance
You need to be using
document.createElementNS("http://www.w3.org/2000/svg", "polygon");
SVG is in its own namespace aside from HTML. Therefore, you need to create elements that are in the SVG namespace using createElementNS.
Consider the following example that works in Chrome and Firefox
var newItem = document.createElementNS("http://www.w3.org/2000/svg", "circle");
newItem.setAttribute("cx", ((16 * 1) + 8));
newItem.setAttribute("cy", "50%");
newItem.setAttribute("r", 4);
newItem.setAttribute("fill", "#333333");
document.getElementById("target").appendChild(newItem);
<svg id="target">
</svg>

Size of character in pixels

I am trying to create text box in the HTML5 canvas, I know you can't actually do this, so I am creating a box and creating some text at the same location. But, I want to make sure the text stays in the box so I need to know when part of the text is extending out of the box. I figure I should be able to measure the text in terms of pixels and then compare it to the box. My question is, using javascript, how can I measure the size of the characters for any given font?
You can use context.measureText to get the width of your specified text:
// set the font
context.font = "14px verdana";
// use measureText to get the text width
var textWidth = context.measureText("Measure this!").width;
Text wrap would look something like this:
function wrapText(context, text, x, y, maxWidth, fontSizeFace) {
var words = text.split(' ');
var line = '';
var lineHeight=measureTextHeight(fontSizeFace);
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if(testWidth > maxWidth) {
context.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
}
else {
line = testLine;
}
}
context.fillText(line, x, y);
}
You can use as markE mention in his answer the measureText() function.
The specifications defines its result (a TextMetrix):
interface TextMetrics {
// x-direction
readonly attribute double width; // advance width
readonly attribute double actualBoundingBoxLeft;
readonly attribute double actualBoundingBoxRight;
// y-direction
readonly attribute double fontBoundingBoxAscent;
readonly attribute double fontBoundingBoxDescent;
readonly attribute double actualBoundingBoxAscent;
readonly attribute double actualBoundingBoxDescent;
readonly attribute double emHeightAscent;
readonly attribute double emHeightDescent;
readonly attribute double hangingBaseline;
readonly attribute double alphabeticBaseline;
readonly attribute double ideographicBaseline;
};
The problem however is that only width is implemented in the major browsers so you cannot get the height (ascent + descent) with this function yet (and I wouldn't be surprised if a canvas based word processor from at least one of the "major 3" shows up right before this gets fully implemented... but that's a regression and a speculation :-) ).
In order to measure the font you will have to use a DOM element, and this little trick will allow you to measure a font's width and height:
Online demo here (open console to see result).
function measureText(font, txt) {
var el = document.createElement('div'),
cs, res;
el.style.cssText = 'position:fixed;left:-4000px;top:-4000px;padding:0;margin:0;font:' + font;
el.innerHTML = txt;
document.body.appendChild(el);
cs = getComputedStyle(el);
res = {width: cs.getPropertyValue('width'),
height: cs.getPropertyValue('height')};
document.body.removeChild(el);
return res;
}
The function creates a div element, applies some basic styles to it to place it outside window. This is because we have to attach it to the DOM tree in order to use getComputedStyle() - we also have to get the property values before we remove the element again.
Pass arguments for font as you would with the context (ie. 20px sans-serif) and the text.
It comes with a small performance penalty obviously (though using fixed positioned elements won't cause any re-flow so it's not so bad) so use sparsely.

How would you create a JQuery / svg click-drag select outline effect?

Not sure exactly what to call it, but I am looking for a way to create a dotted outline/selection box effect via javascript/svg when you click and drag over an area, and then goes away on mouseUp (that could be added if it wasn't an original part) .
A jQuery library would be nice if it exists. I've done some looking around, and haven't found exactly what I am looking for.
I guess the theory would be get the coord from the first click, track the mouse coord moment and adjust the box accordingly.
But not writing it from scratch would be nice.
Here's a demo I made just for you :)
Demo (Static): http://jsfiddle.net/HNH2f/1/
Demo (Animated): http://jsfiddle.net/HNH2f/2/
You can use CSS to control the visual style of the marquee.
You can pass one or two functions to the trackMarquee method; both will be called with four arguments: the x1,y1,x2,y2 bounds of the marquee. The first function will be called when the marquee is released. The second function (if present) will be called each time the marquee moves (so that you can, for example, calculate what items are within that bounding box).
When you start dragging on the SVG document (or whatever element you choose to track) it will create a <rect class="marquee" />; during dragging it will adjust the size of the rectangle. Use CSS (as seen in the demo) to style this rectangle however you want. I'm using the stroke-dasharray property to make the border dotted.
For Stack Overflow posterity, here's the code (on the off chance that JSFiddle is down):
(function createMarquee(global){
var svgNS = 'http://www.w3.org/2000/svg',
svg = document.createElementNS(svgNS,'svg'),
pt = svg.createSVGPoint();
// Usage: trackMarquee( mySVG, function(x1,y1,x2,y2){}, function(x1,y1,x2,y2){} );
// The first function (if present) will be called when the marquee is released
// The second function (if present) will be called as the marquee is changed
// Use the CSS selector `rect.marquee` to select the marquee for visual styling
global.trackMarquee = function(forElement,onRelease,onDrag){
forElement.addEventListener('mousedown',function(evt){
var point0 = getLocalCoordinatesFromMouseEvent(forElement,evt);
var marquee = document.createElementNS(svgNS,'rect');
marquee.setAttribute('class','marquee');
updateMarquee(marquee,point0,point0);
forElement.appendChild(marquee);
document.documentElement.addEventListener('mousemove',trackMouseMove,false);
document.documentElement.addEventListener('mouseup',stopTrackingMove,false);
function trackMouseMove(evt){
var point1 = getLocalCoordinatesFromMouseEvent(forElement,evt);
updateMarquee(marquee,point0,point1);
if (onDrag) callWithBBox(onDrag,marquee);
}
function stopTrackingMove(){
document.documentElement.removeEventListener('mousemove',trackMouseMove,false);
document.documentElement.removeEventListener('mouseup',stopTrackingMove,false);
forElement.removeChild(marquee);
if (onRelease) callWithBBox(onRelease,marquee);
}
},false);
};
function callWithBBox(func,rect){
var x = rect.getAttribute('x')*1,
y = rect.getAttribute('y')*1,
w = rect.getAttribute('width')*1,
h = rect.getAttribute('height')*1;
func(x,y,x+w,y+h);
}
function updateMarquee(rect,p0,p1){
var xs = [p0.x,p1.x].sort(sortByNumber),
ys = [p0.y,p1.y].sort(sortByNumber);
rect.setAttribute('x',xs[0]);
rect.setAttribute('y',ys[0]);
rect.setAttribute('width', xs[1]-xs[0]);
rect.setAttribute('height',ys[1]-ys[0]);
}
function getLocalCoordinatesFromMouseEvent(el,evt){
pt.x = evt.clientX; pt.y = evt.clientY;
return pt.matrixTransform(el.getScreenCTM().inverse());
}
function sortByNumber(a,b){ return a-b }
})(window);
You are lucky I just made this myself. I'm using jQuery SVG plugin ( http://keith-wood.name/svg.html )
$("#paper2").mousedown(function(ev) {
ev.preventDefault();
var pX= (ev.pageX - this.offsetLeft) * viewBox[2]/parseInt($("#paper2").css("width"));
var pY= (ev.pageY - this.offsetTop) * viewBox[3]/parseInt($("#paper2").css("height"));
var rect = svg2.rect(
pX, //X
pY, //Y
1,1, //width and height
{ //Settings, you can make the box dotted here
fill: 'black', "fill-opacity": 0.3, stroke: 'red', strokeWidth: 3, id:rect
}
)
$("#paper2").mousemove(function(ev) {
ev.preventDefault();
var rect= $('#rect');
var pX= (ev.pageX - this.offsetLeft) * viewBox[2]/parseInt($("#paper2").css("width")) - rect.attr("x");
var pY= (ev.pageY - this.offsetTop) * viewBox[3]/parseInt($("#paper2").css("height")) - rect.attr("y");
rect.attr("width", pX);
rect.attr("height", pY);
});
$("#paper2").mouseup(function(ev) {
ev.preventDefault();
var div= $("#paper2");
div.unbind('mousemove');
div.unbind('mouseup');
})
});
paper2 is a div in which I have an svg element (so the svg element and the div have the same height/width). This is how I created the svg2 element:
var svg2;
var root2;
$(document).ready(function() {
$("#paper2").svg({
onLoad: function() {
svg2= $("#paper2").svg('get');
svg2.configure({id: 'svg2'});
var div= $("#paper2");
root2= svg2.root();
$("#svg2").attr("viewBox", viewBox[0]+','+viewBox[1]+','+viewBox[2]+','+viewBox[3]);
},
settings: {}
});
}
If you not using viewbox on the svg element you don't need this on the calculations:
* viewBox[2]/parseInt($("#paper2").css("*****"));
viewbox[2] would be the viewbox width and viewbox[3] would be the viewbox height.

Categories

Resources