What I have:
Text along a path made out of circle. It uses Raphael.js and a function called textOnPath (found here: Raphael JS Text Along path ):
var pathTest = r.path(getCircletoPath(286, 322, radius)).attr({stroke:"#b9b9b9"});
textOnPath(message, pathTest, fontSize, fontSpacing, kerning, kerning, point, textFill, fontNormal, fontFamily);
JSFiddle: http://jsfiddle.net/zorza/62hDH/1/
What I need:
The text to be centered on top of the circle.
My approach:
Try to calculate where the text should start depending on the arc size and text width. I tried to calculate the text width by creating it's invisible clone with text() function and get it's BBox width.
It doesn't quite work and the results vary depending on the web browser, font used and number of letters and spaces:
var length = r.text(100,400,message)
.attr({"font-size":fontSize,'opacity': 0, 'font-family': fontFamily})
.getBBox()
.width;
var point = (Math.PI*radius - length*fontSpacing)/2;
JSFiddle: http://jsfiddle.net/zorza/k8vBy/3/
Could anyone point me in the right direction?
The easiest way, IMHO, is to create additional helper path that is raised by half of text size. http://jsfiddle.net/p84VQ/
Also, I find it a bit more convenient to define a circle and then get points at specified angle:
var Circle = function(cx, cy, r) {
return function (a) {
return {
x: cx + r*Math.sin(Math.PI*-a/180),
y: cy - r*Math.cos(Math.PI*-a/180)
}
}
};
Related
Creating a rectangle in FabricJs is straightforward using top, left, width and height values.
After moving, scaling and rotating the rectangle around, one can get the rectangle's definitive coordinate using the aCoords of the object, returning bl, br, tl, tr x and y coordinates of the rectangles four edges.
How can one recreate the same rectangle using only these four coordinates, which also include rotation and scaling?
I was only able to recreate the rectangle after it has been moved and scaled, but not with rotations.
This above image is a screenshot of the following jsFiddle: https://jsfiddle.net/7neukojd/32/
As you can see, the left side is the original rectangle (blue), which has been scaled, moved and rotated. The right side rectangle (red) is me, trying to copy it using only the bl, br, tl and tr coordinates.
Thanks to asturur, this issue has been solved with the following code
let rectObj = canvas1.getObjects()[0];
const tl = rectObj.aCoords.tl;
const tr = rectObj.aCoords.tr;
const bl = rectObj.aCoords.bl;
let rectCopy = new fabric.Rect({
left: rectObj.aCoords.tl.x,
top: rectObj.aCoords.tl.y,
width: (new fabric.Point(tl.x, tl.y).distanceFrom(tr)),
height: (new fabric.Point(tl.x, tl.y).distanceFrom(bl)),
angle: fabric.util.radiansToDegrees(Math.atan2(tr.y - tl.y, tr.x - tl.x)),
fill: "red",
})
Source: https://github.com/fabricjs/fabric.js/discussions/6834#discussioncomment-314599
I'm trying to place text dynamically into an svg created by Snap, this is what I tried:
this.setContent(
`<svg id="${this.svgId}"></svg>`
);
var snap = Snap($(`#${this.svgId}`)[0]);
text = "asdfsdfsdsfd";
var rect = snap.paper.rect(0, 0, 50, text.length*3 + 4, 10);
snap.text(1.5,10, text);
console.log("rect", rect);
console.log("snap", snap);
rect.attr({
fill: "#FFFFFF",
fillOpacity: 0.6,
});
I get this:
I want the rectangle to be just a little bigger than the text, but there must be a better way to do it than to calculate the length and height of the text, and that's assuming the font size won't change.
This is the only result I found regarding text in the snap docs: http://snapsvg.io/docs/#Paper.text
You could try using getBBox() on the text element, and use that to figure the size of the rect. getBBox() wll give you the x,y,width,height,x2,y2 figures to help.
var text = s.text(0,0,'blah blah')
var bb = text.getBBox();
var rect = s.rect(bb.x, bb.y, bb.width, bb.height )
Adjusting with offsets for whatever padding etc that you want. You may also need to allow for stroke widths, as I don't think that's included.
OK, so I have a group of four elements rotating 90 degrees as I want them to, around an origin point in the middle of the four elements.
I would like to scale the top left block before and after spinning as well, outward from said origin point, but I'm having much difficulty doing so.
Here is a fiddle for my sample (read: overly simplified) progress so far:
http://jsfiddle.net/Vac2Q/2843/
The fiddle's javascript:
/* create an svg drawing */
var draw = SVG('drawing')
/* draw rectangle */
var dial = draw.circle(60)
.move(125,125)
.fill('#0099ff')
var rect_yellow = draw.rect(50,50)
.move(100,100)
.fill('gold')
var rect_blue = draw.rect(50,50)
.move(160,100)
.fill('navy')
var rect_black = draw.rect(50,50)
.move(160,160)
.fill('black')
var rect_green = draw.rect(50,50)
.move(100,160)
.fill('green')
var blades = draw.group()
.add(rect_yellow)
.add(rect_blue)
.add(rect_black)
.add(rect_green)
.back()
var angle = 0
var rotation = 90
var spin = document.getElementById('spin')
var spun = 0
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
/* calculate new ending orientation for blades */
angle += rotation
var new_rotate = angle
/* rotate the blade group */
blades.animate(1000, '>')
.rotate(new_rotate, 155, 155)
++spun
})
And here is a slightly more glamorous example of what I'm trying to do re: scaling:
The first issue is being able to determine which blade is in the top left position after a given rotation. The second issue is scaling itself; I've gotten the blade to scale, but then it goes crazy and moves off the screen at the same time. I'm not sure how to get it to scale properly from the specified origin point (the middle of the center circle).
You can use the .after() function to chain animations.
I'm not sure if I am using svg.js correctly, but here's what I did:
var rects = [rect_yellow, rect_green, rect_black, rect_blue];
// define the animations
var enlarge_blade = function() {
rects[spun % 4].animate(250, '<')
.scale(1.25, 1.25)
.translate(-38,-38);
};
function spin_anim() {
rects[spun % 4].animate(250, '>')
.scale(1, 1)
.translate(0,0)
.after(rotate_blades);
};
var rotate_blades = function() {
blades.animate(1000, '>')
.rotate(angle, 155, 155)
.after(function() {
++spun;
enlarge_blade();
title.text('spun ' + spun + ' times');
});
};
// Pre-enlarge the first (yellow) rect
enlarge_blade();
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
angle += rotation
spin_anim();
})
Demo here
I see you're using svg.js. I don't know how it treats transforms internally. But in any case. To scale an element, you typically use its center point as a reference. Therefore you should find the center point of each rect and scale it using that point. (I assume svg.js performs the required translation internally).
I'm trying to create a simple pdf doc using javascript. I found jsPDF but I don't figure out how to center text. Is it possible?
Yes it's possible. You could write a jsPDF plugin method to use.
One quick example is this:
(function(API){
API.myText = function(txt, options, x, y) {
options = options ||{};
/* Use the options align property to specify desired text alignment
* Param x will be ignored if desired text alignment is 'center'.
* Usage of options can easily extend the function to apply different text
* styles and sizes
*/
if( options.align == "center" ){
// Get current font size
var fontSize = this.internal.getFontSize();
// Get page width
var pageWidth = this.internal.pageSize.width;
// Get the actual text's width
/* You multiply the unit width of your string by your font size and divide
* by the internal scale factor. The division is necessary
* for the case where you use units other than 'pt' in the constructor
* of jsPDF.
*/
txtWidth = this.getStringUnitWidth(txt)*fontSize/this.internal.scaleFactor;
// Calculate text's x coordinate
x = ( pageWidth - txtWidth ) / 2;
}
// Draw text at x,y
this.text(txt,x,y);
}
})(jsPDF.API);
And you use it like this
var doc = new jsPDF('p','in');
doc.text("Left aligned text",0.5,0.5);
doc.myText("Centered text",{align: "center"},0,1);
This works in the scratchpad on the jsPdf homepage:
var centeredText = function(text, y) {
var textWidth = doc.getStringUnitWidth(text) * doc.internal.getFontSize() / doc.internal.scaleFactor;
var textOffset = (doc.internal.pageSize.width - textWidth) / 2;
doc.text(textOffset, y, text);
}
I have found that the current version of jsPdf supports a parameter 'align' with the function signature like this:
API.text = function (text, x, y, flags, angle, align)
So the following should give you a center-aligned text:
doc.text('The text', doc.internal.pageSize.width, 50, null, null, 'center');
However, at the current point in time, an error is thrown in the library when strict mode is on because a 'var' is missing.
There is an issue and pull request for it, but the fix hasn't made it in:
https://github.com/MrRio/jsPDF/issues/575
Whoever is looking for this, one day, you might be able to use this to make it easier to center text.
WootWoot, just in case you need more layout options, you could also take a look at my pdfmake library
It supports:
text alignments, lists, margins
styling (with style inheritance)
tables with auto/fixed/star sized columns, auto-repeated headers, col/row spans
page headers and footers
font embedding, and a couple of other options
It works on client-side (pure JS) or server-side (an npm module)
Take a look at the playground to see what's possible
Good luck
I had the same problem and a lot of others while creating PDF-Files (e.g. auto-pagebreak, total-pageCount). So i started writing a little lib, which depends on jsPDF but gives you a lot of features in a way you know them (form HTML/CSS and jQuery). You can find it on GitHub. I hope it makes PDF-Creating easier... =)
Based on #Tsilis answer I have snippet out a plugin here https://gist.github.com/Purush0th/7fe8665bbb04482a0d80 which can align the text left, right and center in the given text container width.
(function (api, $) {
'use strict';
api.writeText = function (x, y, text, options) {
options = options || {};
var defaults = {
align: 'left',
width: this.internal.pageSize.width
}
var settings = $.extend({}, defaults, options);
// Get current font size
var fontSize = this.internal.getFontSize();
// Get the actual text's width
/* You multiply the unit width of your string by your font size and divide
* by the internal scale factor. The division is necessary
* for the case where you use units other than 'pt' in the constructor
* of jsPDF.
*/
var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor;
if (settings.align === 'center')
x += (settings.width - txtWidth) / 2;
else if (settings.align === 'right')
x += (settings.width - txtWidth);
//default is 'left' alignment
this.text(text, x, y);
}
})(jsPDF.API, jQuery);
Usage
var doc = new jsPDF('p', 'pt', 'a4');
//Alignment based on page width
doc.writeText(0, 40 ,'align - center ', { align: 'center' });
doc.writeText(0, 80 ,'align - right ', { align: 'right' });
//Alignment based on text container width
doc.writeText(0, 120 ,'align - center : inside container',{align:'center',width:100});
maybe... just for easy way, you can read this jsPdf text api doc
doc.text(text, x, y, optionsopt, transform)
where optionspot is an option objet, so, you can do this
{align:"center"}
i.e:
doc.text("Hello Sun", doc.internal.pageSize.getWidth()/2, 10, { align: "center" })
where: doc.internal.pageSize.getWidth() is the page width for pdf sheet
doc.text(text,left,top,'center') can be used to center text. It can be used with array of lines as well but when it is used with array the center does not work right so I have used it in a loop for every object in the array.
var lMargin=15; //left margin in mm
var rMargin=15; //right margin in mm
var pdfInMM=210; // width of A4 in mm
var pageCenter=pdfInMM/2;
var doc = new jsPDF("p","mm","a4");
var paragraph="Apple's iPhone 7 is officially upon us. After a week of pre-orders, the latest in the iPhone lineup officially launches today.\n\nEager Apple fans will be lining up out the door at Apple and carrier stores around the country to grab up the iPhone 7 and iPhone 7 Plus, while Android owners look on bemusedly.\n\nDuring the Apple Event last week, the tech giant revealed a number of big, positive changes coming to the iPhone 7. It's thinner. The camera is better. And, perhaps best of all, the iPhone 7 is finally water resistant.\n\nStill, while there may be plenty to like about the new iPhone, there's plenty more that's left us disappointed. Enough, at least, to make smartphone shoppers consider waiting until 2017, when Apple is reportedly going to let loose on all cylinders with an all-glass chassis design.";
var lines =doc.splitTextToSize(paragraph, (pdfInMM-lMargin-rMargin));
var dim = doc.getTextDimensions('Text');
var lineHeight = dim.h
for(var i=0;i<lines.length;i++){
lineTop = (lineHeight/2)*i
doc.text(lines[i],pageCenter,20+lineTop,'center'); //see this line
}
doc.save('Generated.pdf');
Do this:
First get the page width, get half the page width and use it as the x value, use y value of your choice and pass center as the third param to center your text.
Read more from documentation
let doc = new jsPDF();
let pageWidth = doc.internal.pageSize.getWidth();
doc.text("My centered text",pageWidth / 2, 20, 'center');
This will work fine.
let pdf = new jspdf('p', 'mm', 'a4');
pdf.text("Text to display", pdf.internal.pageSize.getWidth() / 2, 50, null, 'center');
50 is the height i.e. y-axis.
This worked for me:
doc.styles.tableBodyEven = {
alignment: 'center'
}
doc.styles.tableBodyOdd = {
alignment: 'center',
color: '#555555',
fillColor: '#dedede'
}
how I can change the width and the height of icons from free Raphael icons ?
I tried to use attr, tried to use % , like this var paper = Raphael("canvas", 100%, 100%);.
I need to do this: if I change the size of the parent block, the size of my icon changes too.
upd: i tried use "scale" and "transform", but icon resize from centre and not fit into the parent correctly
According to the Raphael.js documentation
var el = paper.rect(10, 20, 300, 200);
// translate 100, 100, rotate 45°, translate -100, 0
el.transform("t100,100r45t-100,0");
// if you want you can append or prepend transformations
el.transform("...t50,50");
el.transform("s2...");
// or even wrap
el.transform("t50,50...t-50-50");
// to reset transformation call method with empty string
el.transform("");
// to get current value call it without parameters
console.log(el.transform());
Check this Fiddle:
Demonstration of all the transformations
var icon = paper.rect(100,200,100,100);
var anim = Raphael.animation({
"10%":{transform:'t100,0'}, //transform on x-axis
"20%":{transform:'...t0,100'},//transform on y-axis
"30%":{transform:'...t-100,0'},//transform on x-axis(negative)
"40%":{transform:'...t0,-100'},//transform on y-axis(negative)
"50%":{transform:'...t200,200'},//transform diagonally
"60%":{transform:'...t-100,-100'},//transform diagonally(negative)
"70%":{transform:'...s1,1.5'},//scale y-axis
"80%":{transform:'...s1.5,1'},//scale x-axis
"90%":{transform:'...s2'},//scale in both direction
"100%":{transform:'...r45'},//rotate
},5000);
icon.animate(anim.delay(1000));
So in your case you'll have to do this:
var somename = paper.path("path coordinates").transform('s2,3');
where 2 is for width & 3 is for height.