Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curve Text Feature [$200] #729

Closed
dean555 opened this issue Jul 1, 2013 · 119 comments · Fixed by #6543
Closed

Curve Text Feature [$200] #729

dean555 opened this issue Jul 1, 2013 · 119 comments · Fixed by #6543
Assignees
Labels

Comments

@dean555
Copy link

dean555 commented Jul 1, 2013

Hi

Loving the product but we desperatly need curve text. I've seen that it's on the roadmap but would like to ask if there is any progress or anything that can be done to help?

Great work guys!

There is a $200 open bounty on this issue. Add to the bounty at Bountysource.

@mojo5000
Copy link

mojo5000 commented Jul 2, 2013

Have you looked at something like this:
http://tympanus.net/Development/Arctext/

Or is the challenge to get curved text in the canvas with object controls?

On Mon, Jul 1, 2013 at 9:20 AM, dean555 notifications@github.com wrote:

Hi

Loving the product but we desperatly need curve text. I've seen that it's
on the roadmap but would like to ask if there is any progress or anything
that can be done to help?

Great work guys!


Reply to this email directly or view it on GitHubhttps://github.com//issues/729
.

@dean555
Copy link
Author

dean555 commented Jul 2, 2013

Hi mojo5000

We need it to work with fabric :(

@kirkh34
Copy link

kirkh34 commented Jul 16, 2013

I need this functionality as well. I'm using fabric to create a T-shirt designer app. I think this is a common app using fabric. All good T-shirt apps allow text warping. It is huge in T-shirt design. Please someone write this!! Fabric is 99% perfect and this is the missing link!

@imomin
Copy link

imomin commented Jul 16, 2013

I am working on T-shirt designer app as well. I wonder if we should join hands rather than competing with each other ;). BTW check out the https://github.com/imomin/fabric.curvedText.

@kirkh34
Copy link

kirkh34 commented Jul 16, 2013

I tried the new code but still can't get it to work for me. Are you able to use this code to curve text successfully? I'm working on a t-shirt app that will have layers similar to photoshop w/ hide/lock functions for each layer/canvas item. Can you post a jsfiddle of this working for you?

@imomin
Copy link

imomin commented Jul 16, 2013

Here you go! http://jsfiddle.net/NHs8t/

@somecodemonkey
Copy link
Contributor

@imomin you are a godsend thx

@dean555
Copy link
Author

dean555 commented Jul 21, 2013

Hi @imomin

Nice work but you cannot make the text straight also you have 3 sliders. Can we make it using one single slider so that user adds a text which is straight and then he can curve it up/down using a slider.

And the curve text should also work with load from SVG and JSON and export as well.

But excellent job till now.

@imomin
Copy link

imomin commented Jul 21, 2013

@dean555
FYI, I pulled somebody else's project and made minor changes. So, the credit goes to him.
In my project I am using only spacement slider with curve up, curve down and in-line button.
Based on the option selected, curve up/down or in-line I swap between Curve Text or standard Text object.

I started my project few weeks ago and I haven't got to SVG/JSON export yet. I will make changes to it as I move forward.

@somecodemonkey
Copy link
Contributor

I noticed its a group of fabric.text objects is there any performance advantages of using this rather than rendering each letter individually?

@Jochuaf
Copy link

Jochuaf commented Sep 13, 2013

Hi all,
I've just started with fabricjs a few weeks ago. Since I want to work with curved text, I've implemented your code into my project.
My question will probably seem very stupid for you (actually, I am a beginner), but I would like to know how you can select the text group, like "canvas.getActiveObject()" for a regular text ?
I hope I will get a reply. Anyway, thanks to Imomin & Skritz for your amazing work.
ps : sorry for my english level, I am french

@EffEPi
Copy link
Contributor

EffEPi commented Sep 26, 2013

I started to work on my own curved text, I am NEW to fabric and OO javascript and HTML5/Canvas, so I am having some issues trying to make it work.

http://jsfiddle.net/t4VPQ/

Any hint would be greatly appreciated.

My Goal is to make a fabric object that acts like the others (getText, setText, set properties, export SVN, JSON, etc...)

@imomin
Copy link

imomin commented Sep 26, 2013

In your $(function(){....} on line 643 add var canvas = new fabric.Canvas('c'); and add comma after left:50,.Hopefully this will help you move forward. http://jsfiddle.net/t4VPQ/14/

@EffEPi
Copy link
Contributor

EffEPi commented Sep 26, 2013

yes, Thank you, saw that after I updated the comment :-) was a copy/paste error. The problem is that I can't seems to render the text on the main canvas.

@EffEPi
Copy link
Contributor

EffEPi commented Sep 26, 2013

Ok, a lot of functionality is ready... I just can't figure it out how to refresh the render on the fly.. dang

@EffEPi
Copy link
Contributor

EffEPi commented Sep 26, 2013

I have a almost fully working example. I need to figure out how to make the resize work as well as test the exports (SVG and jSON)

@EffEPi
Copy link
Contributor

EffEPi commented Sep 28, 2013

You want curved Text that acts like normal TEXT ??

Here it is :-D

http://jsfiddle.net/EffEPi/qpJTz/

https://github.com/EffEPi/fabric.curvedText

@tiredenzo
Copy link

Nice work @EffEPi :) I was really looking towards this feature
I was actually thinking of implementing a SVG like method http://www.svgbasics.com/text2.html
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-text-path-tutorial/

@EffEPi
Copy link
Contributor

EffEPi commented Sep 29, 2013

That gives me a new idea.. freehand Text. Using the drawing mode create a path and then set the text around that path.. but this is out of the scope of this thread

@tiredenzo
Copy link

A bit like http://csswarp.eleqtriq.com/ ? :)

@kangax
Copy link
Member

kangax commented Sep 29, 2013

Yeah, looks pretty badass. We're planning to have text-on-path sometime in
the future!

On Sun, Sep 29, 2013 at 5:22 PM, tiredenzo notifications@github.com wrote:

A bit like http://csswarp.eleqtriq.com/ ? :)


Reply to this email directly or view it on GitHubhttps://github.com//issues/729#issuecomment-25322161
.

@Jochuaf
Copy link

Jochuaf commented Oct 2, 2013

@EffEPi : Whoaaaaaw! I wish I had your skills...

@EffEPi
Copy link
Contributor

EffEPi commented Oct 2, 2013

@Jochuaf let me know if you have any improvements for it. I am already planning on a new rewrite that maybe doesn't use grouping and export to SVN as paths and also imports from path... to do that I have to learn SVN rendering a bit better

@Jochuaf
Copy link

Jochuaf commented Oct 4, 2013

EffEPi : thanks for the answer. I've managed to implement your code into my the project I am working on (light product customizer). I have to say it works very well. Attaching text to path would be also great and I think I wouldn't be the only one interested in that :-)

@dean555
Copy link
Author

dean555 commented Feb 10, 2014

Hi everyone

Have there been any developments with getting curve text to work? getting desperate now... :(

@maza23
Copy link

maza23 commented Jun 10, 2014

@EffEPi how would you do this extending from IText instead of Text? I need the text to be editable.

@goldguy23
Copy link

Has anyone solved this issue yet? My site is waiting on a solution that uses a slider to curve text. Willing to pay a developer to develop this.

@kangax
Copy link
Member

kangax commented Aug 6, 2014

@goldguy23 no solution yet and there's too much other stuff for me to go through until I can get to this. There's already $100 bounty on this, but this is almost certainly very small for a feature like this (worth at least 1K+; a lot of stuff to consider)

@goldguy23
Copy link

If I made the bounty 1000 when do you think you could work on this. We are trying to launch our new site by mid September. If you can't get to this then I understand and we would just wait another year as it's a nice to have not a need to have.

Marc Goldberg
Founder
www.tasseltoppers.com

On Aug 6, 2014, at 8:09 AM, Juriy Zaytsev notifications@github.com wrote:

@goldguy23 no solution yet and there's too much other stuff for me to go through until I can get to this. There's already $100 bounty on this, but this is almost certainly very small for a feature like this (worth at least 1K+; a lot of stuff to consider)


Reply to this email directly or view it on GitHub.

@kangax
Copy link
Member

kangax commented Aug 8, 2014

No time to work on this now, sorry.

kangax

On Wed, Aug 6, 2014 at 2:27 PM, goldguy23 notifications@github.com wrote:

If I made the bounty 1000 when do you think you could work on this. We are
trying to launch our new site by mid September. If you can't get to this
then I understand and we would just wait another year as it's a nice to
have not a need to have.

Marc Goldberg
Founder
www.tasseltoppers.com

On Aug 6, 2014, at 8:09 AM, Juriy Zaytsev notifications@github.com
wrote:

@goldguy23 no solution yet and there's too much other stuff for me to go
through until I can get to this. There's already $100 bounty on this, but
this is almost certainly very small for a feature like this (worth at least
1K+; a lot of stuff to consider)


Reply to this email directly or view it on GitHub.


Reply to this email directly or view it on GitHub
#729 (comment).

@av01d
Copy link

av01d commented Jun 10, 2018

@lucasa: Check https://jsfiddle.net/av01d/4p0syzw3/
It's a solution that works for me. Check whether it's what you need.

@Mmear
Copy link

Mmear commented Sep 13, 2018

Thanks for making this, It works well :)

@shokimble
Copy link

@av01d same thing happens to me as @sweetsmurph

If you export the canvas with a multiplier which is common for printing the text is rendered at the original pixel size not the scale up size.

I believe for normal text this is handled internally by fabric by setting a scale on the canvas but since your solution draws to a canvas which is then multiplied x times by canvas it comes out blurry.

This could probably be solved by drawing onto the ctx supplied in _render directly. I can't see why it's rendering onto a temporary canvas but guessing it's a kinda lazy way of not having to know where exactly we are drawing too. I can't can't quite work it out.

Would appreciate if you could take a look @av01d

Here's a fiddle with the multiplied document writing out to the body so scroll down a lot as you're looking at a lot bigger image. Notice normal text isn't blurry but your curved text is.

https://jsfiddle.net/mjpch7uz/

On a side note: A preferred curve text class would really involve extending text so all the standard properties can be used but seeing this issue has been open for 5 years I'll take yours 👍

@av01d
Copy link

av01d commented Dec 11, 2018

@shokimble: Here's an updated fiddle: http://jsfiddle.net/av01d/5vcmx9pt/
On line 225 you can set a zoomfactor.
I am rendering on a temp. canvas, because of the fact that I first draw a circle (which can be huge), then I draw the text along that circle, then I trim the temp. canvas (remove all transparent pixels surrounding the text). I don't know (yet) how to only draw text-along-circle without having to trim.

The default for zoomFactor is 1 (unsharp), you can increase this as needed.
This works wonders... but you might run out of memory when the curved text is drawn with a large radius.
In order to draw the curved text, I need to have a canvas that's as large as the radius the curved text is gonna have.
For example: for a 500px radius, when the zoomFactor is 2, this resulting temporary canvas is 1000px x 1000px. This adds up very quickly when the radius grows. Depending on your browser, you will eventually run out of memory.

@shokimble
Copy link

Thanks for writing that up.

Understand the concerns about memory as the radius can get very large.

The problem I have and I'm sure others will too is you may not actually know what the multiplier is ahead of time.

I think the issue you described might be a non issue as you can extend beyond the canvas and go into negative coords without an error occuring.

I'll have a play with it and report back.

Thanks again

@mahmudz
Copy link

mahmudz commented Dec 20, 2018

I tried to make a solution.
check it out... https://youtu.be/fnmhQVSJ2jg
Sorry for bad quality of the video.

@av01d
Copy link

av01d commented Dec 20, 2018

@mahmudz Looks promising, very well done! Will you share your code, as I did, when you think it's ready?

@asturur
Copy link
Member

asturur commented Dec 20, 2018

i won't accept solutions that do not cover text on path and from svg to svg export.
Curved text is less generic than text on a path and less powerfull.

I understand is a nice feature, but not worth alone.

@av01d
Copy link

av01d commented Dec 21, 2018

CleverBrush (https://cleverbrush.com/editor/) managed to implement it: text-on-path with SVG-export.
I think the SVG-part is done on the server though. It's not open source....

@asturur
Copy link
Member

asturur commented Dec 21, 2018

svg export is very easy, is the canvas rendering that i think is not. There is another lib that support text on path and i think is called paper.js?

@contact2mayurkukadiya
Copy link

In reference of below fiddle :
https://jsfiddle.net/av01d/4p0syzw3/
There is one issue that when we scale arc text with 0 width and height, at that time it will fire error in console line about getImageData

Is there any solution for prevent user to make scaling till minimum 100px or any patch for text not overlap when user scale down that circular text (prevent user to scale down when both end of text are touch up with each other)

Please help if possible and update fiddle.

@Clemweb
Copy link

Clemweb commented May 28, 2019

Is there a solution to work on Node ?

@Clemweb
Copy link

Clemweb commented Jun 1, 2019

Here is an old curved class created on Fabricjs 1.X that worked fine.
It is not possible to correcte it to work on Fabric 3.x ?

`(function (global){

"use strict";

var fabric=global.fabric||(global.fabric={}),
		extend=fabric.util.object.extend,
		clone=fabric.util.object.clone;

if(fabric.CurvedText){
	fabric.warn('fabric.CurvedText is already defined');
	return;
}
var stateProperties=fabric.Text.prototype.stateProperties.concat();
stateProperties.push(
		'radius',
		'spacing',
		'reverse',
		'effect',
		'range',
		'largeFont',
		'smallFont',
		'globalCompositeOperation'
		);
var _dimensionAffectingProps=fabric.Text.prototype._dimensionAffectingProps;
_dimensionAffectingProps['radius']=true;
_dimensionAffectingProps['spacing']=true;
_dimensionAffectingProps['reverse']=true;
_dimensionAffectingProps['fill']=true;
_dimensionAffectingProps['effect']=true;
_dimensionAffectingProps['width']=true;
_dimensionAffectingProps['height']=true;
_dimensionAffectingProps['range']=true;
_dimensionAffectingProps['fontSize']=true;
_dimensionAffectingProps['shadow']=true;
_dimensionAffectingProps['largeFont']=true;
_dimensionAffectingProps['smallFont']=true;
_dimensionAffectingProps['globalCompositeOperation']=true;


var delegatedProperties=fabric.Group.prototype.delegatedProperties;
delegatedProperties['backgroundColor']=true;
delegatedProperties['textBackgroundColor']=true;
delegatedProperties['textDecoration']=true;
delegatedProperties['stroke']=true;
delegatedProperties['strokeWidth']=true;
delegatedProperties['shadow']=true;
delegatedProperties['fontWeight']=true;
delegatedProperties['fontStyle']=true;
delegatedProperties['strokeWidth']=true;
delegatedProperties['textAlign']=true;
delegatedProperties['opacity']=true;
delegatedProperties['globalCompositeOperation']=true;

/**
 * Group class
 * @class fabric.CurvedText
 * @extends fabric.Text
 * @mixes fabric.Collection
 */
fabric.CurvedText=fabric.util.createClass(fabric.Text, fabric.Collection, /** @lends fabric.CurvedText.prototype */ {
	/**
	 * Type of an object
	 * @type String
	 * @default
	 */
	type: 'curvedText',
	/**
	 * The radius of the curved Text
	 * @type Number
	 * @default 50
	 */
	radius: 50,
	/**
	 * Special Effects, Thanks to fahadnabbasi
	 * https://github.com/EffEPi/fabric.curvedText/issues/9
	 */
	range: 5,
	smallFont: 10,
	largeFont: 30,
	effect: 'curved',
	/**
	 * Spacing between the letters
	 * @type fabricNumber
	 * @default 20
	 */
	spacing: 20,

// letters: null,

	/**
	 * Reversing the radius (position of the original point)
	 * @type Boolean
	 * @default false
	 */
	reverse: false,
	/**
	 * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
	 * as well as for history (undo/redo) purposes
	 * @type Array
	 */
	stateProperties: stateProperties,
	/**
	 * Properties that are delegated to group objects when reading/writing
	 * @param {Object} delegatedProperties
	 */
	delegatedProperties: delegatedProperties,
	/**
	 * Properties which when set cause object to change dimensions
	 * @type Object
	 * @private
	 */
	_dimensionAffectingProps: _dimensionAffectingProps,
	/**
	 *
	 * Rendering, is we are rendering and another rendering call is passed, then stop rendering the old and
	 * rendering the new (trying to speed things up)
	 */
	_isRendering: 0,
	/**
	 * Added complexity
	 */
	complexity: function (){
		this.callSuper('complexity');
	},
	initialize: function (text, options){
		options||(options={});
		this.letters=new fabric.Group([], {
			selectable: false,
			padding: 0
		});
		this.__skipDimension=true;
		this.setOptions(options);
		this.__skipDimension=false;
		
		if(parseFloat(fabric.version) >= 2) {
			this.callSuper('initialize', text, options);
		}

		this.setText(text);
		this._render();
	},
	setText: function (text){
		if(this.letters){
			while(text.length!==0&&this.letters.size()>=text.length){
				this.letters.remove(this.letters.item(this.letters.size()-1));
			}
			for(var i=0; i<text.length; i++){
				//I need to pass the options from the main options
				if(this.letters.item(i)===undefined){
					this.letters.add(new fabric.Text(text[i]));
				}else{
					this.letters.item(i).setText(text[i]);
				}
			}
		}
		this.callSuper('setText', text);
		this._render();
	},
	_initDimensions: function (ctx){
		// from fabric.Text.prototype._initDimensions
		// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		if(this.__skipDimension){
			return;
		}
		if(!ctx){
			ctx=fabric.util.createCanvasElement().getContext('2d');
			this._setTextStyles(ctx);
		}
		this._textLines=this.text.split(this._reNewline);
		this._clearCache();
		var currentTextAlign=this.textAlign;
		this.textAlign='left';
		this.width=this.getWidth();
		this.textAlign=currentTextAlign;
		this.height=this.getHeight();
		
		//this.set
		// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		this._render(ctx);
	},
	_render: function (ctx){
		var renderingCode=fabric.util.getRandomInt(100, 999);
		this._isRendering=renderingCode;
		if(this.letters){
			var curAngle=0,
					curAngleRotation=0,
					angleRadians=0,
					align=0,
					textWidth=0,
					space=parseInt(this.spacing),
					fixedLetterAngle=0;

			//get text width
			if(this.effect==='curved'){
				for(var i=0, len=this.text.length; i<len; i++){
					textWidth+=this.letters.item(i).width+space;
				}
				textWidth-=space;
			}else if(this.effect==='arc'){
				fixedLetterAngle=((this.letters.item(0).fontSize+space)/this.radius)/(Math.PI/180);
				textWidth=((this.text.length+1)*(this.letters.item(0).fontSize+space));
			}
			// Text align
			if(this.get('textAlign')==='right'){
				curAngle=90-(((textWidth/2)/this.radius)/(Math.PI/180));
			}else if(this.get('textAlign')==='left'){
				curAngle=-90-(((textWidth/2)/this.radius)/(Math.PI/180));
			}else{
				curAngle=-(((textWidth/2)/this.radius)/(Math.PI/180));
			}
			if(this.reverse)
				curAngle=-curAngle;

			var width=0,
					multiplier=this.reverse?-1:1,
					thisLetterAngle=0,
					lastLetterAngle=0;

			for(var i=0, len=this.text.length; i<len; i++){
				if(renderingCode!==this._isRendering)
					return;

				for(var key in this.delegatedProperties){
					this.letters.item(i).set(key, this.get(key));
				}

				this.letters.item(i).set('left', (width));
				this.letters.item(i).set('top', (0));
				this.letters.item(i).setAngle(0);
				this.letters.item(i).set('padding', 0);

				if(this.effect==='curved'){
					thisLetterAngle=((this.letters.item(i).width+space)/this.radius)/(Math.PI/180);
					curAngle=multiplier*((multiplier*curAngle)+lastLetterAngle);
					angleRadians=curAngle*(Math.PI/180);
					lastLetterAngle=thisLetterAngle;

					this.letters.item(i).setAngle(curAngle);
					this.letters.item(i).set('top', multiplier*-1*(Math.cos(angleRadians)*this.radius));
					this.letters.item(i).set('left', multiplier*(Math.sin(angleRadians)*this.radius));
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set('selectable', false);

				}else if(this.effect==='arc'){//arc
					curAngle=multiplier*((multiplier*curAngle)+fixedLetterAngle);
					angleRadians=curAngle*(Math.PI/180);

					this.letters.item(i).set('top', multiplier*-1*(Math.cos(angleRadians)*this.radius));
					this.letters.item(i).set('left', multiplier*(Math.sin(angleRadians)*this.radius));
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set('selectable', false);
				}else if(this.effect==='STRAIGHT'){//STRAIGHT
					//var newfont=(i*5)+15;
					//this.letters.item(i).set('fontSize',(newfont));
					this.letters.item(i).set('left', (width));
					this.letters.item(i).set('top', (0));
					this.letters.item(i).setAngle(0);
					width+=this.letters.item(i).get('width');
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set({
						borderColor: 'red',
						cornerColor: 'green',
						cornerSize: 6,
						transparentCorners: false
					});
					this.letters.item(i).set('selectable', false);
				}else if(this.effect==='smallToLarge'){//smallToLarge
					var small=parseInt(this.smallFont);
					var large=parseInt(this.largeFont);
					//var small = 20;
					//var large = 75;
					var difference=large-small;
					var center=Math.ceil(this.text.length/2);
					var step=difference/(this.text.length);
					var newfont=small+(i*step);

					//var newfont=(i*this.smallFont)+15;

					this.letters.item(i).set('fontSize', (newfont));

					this.letters.item(i).set('left', (width));
					width+=this.letters.item(i).get('width');
					//this.letters.item(i).set('padding', 0);
					/*this.letters.item(i).set({
					 borderColor: 'red',
					 cornerColor: 'green',
					 cornerSize: 6,
					 transparentCorners: false
					 });*/
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set('selectable', false);
					this.letters.item(i).set('top', -1*this.letters.item(i).get('fontSize')+i);
					//this.letters.width=width;
					//this.letters.height=this.letters.item(i).get('height');

				}else if(this.effect==='largeToSmallTop'){//largeToSmallTop
					var small=parseInt(this.largeFont);
					var large=parseInt(this.smallFont);
					//var small = 20;
					//var large = 75;
					var difference=large-small;
					var center=Math.ceil(this.text.length/2);
					var step=difference/(this.text.length);
					var newfont=small+(i*step);
					//var newfont=((this.text.length-i)*this.smallFont)+12;
					this.letters.item(i).set('fontSize', (newfont));
					this.letters.item(i).set('left', (width));
					width+=this.letters.item(i).get('width');
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set({
						borderColor: 'red',
						cornerColor: 'green',
						cornerSize: 6,
						transparentCorners: false
					});
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set('selectable', false);
					this.letters.item(i).top=-1*this.letters.item(i).get('fontSize')+(i/this.text.length);

				}else if(this.effect==='largeToSmallBottom'){
					var small=parseInt(this.largeFont);
					var large=parseInt(this.smallFont);
					//var small = 20;
					//var large = 75;
					var difference=large-small;
					var center=Math.ceil(this.text.length/2);
					var step=difference/(this.text.length);
					var newfont=small+(i*step);
					//var newfont=((this.text.length-i)*this.smallFont)+12;
					this.letters.item(i).set('fontSize', (newfont));
					this.letters.item(i).set('left', (width));
					width+=this.letters.item(i).get('width');
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set({
						borderColor: 'red',
						cornerColor: 'green',
						cornerSize: 6,
						transparentCorners: false
					});
					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set('selectable', false);
					//this.letters.item(i).top =-1* this.letters.item(i).get('fontSize')+newfont-((this.text.length-i))-((this.text.length-i));
					this.letters.item(i).top=-1*this.letters.item(i).get('fontSize')-i;

				}else if(this.effect==='bulge'){//bulge
					var small=parseInt(this.smallFont);
					var large=parseInt(this.largeFont);
					//var small = 20;
					//var large = 75;
					var difference=large-small;
					var center=Math.ceil(this.text.length/2);
					var step=difference/(this.text.length-center);
					if(i<center)
						var newfont=small+(i*step);
					else
						var newfont=large-((i-center+1)*step);
					this.letters.item(i).set('fontSize', (newfont));

					this.letters.item(i).set('left', (width));
					width+=this.letters.item(i).get('width');

					this.letters.item(i).set('padding', 0);
					this.letters.item(i).set('selectable', false);

					this.letters.item(i).set('top', -1*this.letters.item(i).get('height')/2);
				}
			}

			var scaleX=this.letters.get('scaleX');
			var scaleY=this.letters.get('scaleY');
			var angle=this.letters.get('angle');

			this.letters.set('scaleX', 1);
			this.letters.set('scaleY', 1);
			this.letters.set('angle', 0);

			// Update group coords
			this.letters._calcBounds();
			this.letters._updateObjectsCoords();
			//this.letters.saveCoords();
			// this.letters.render(ctx);

			this.letters.set('scaleX', scaleX);
			this.letters.set('scaleY', scaleY);
			this.letters.set('angle', angle);

			this.width=this.letters.width;
			this.height=this.letters.height;
			this.letters.left=-(this.letters.width/2);
			this.letters.top=-(this.letters.height/2);

// console.log('End rendering')
}
},
_renderOld: function (ctx){
if(this.letters){
var curAngle=0,
angleRadians=0,
align=0;
// Text align
var rev=0;
if(this.reverse){
rev=0.5;
}
if(this.get('textAlign')==='center'||this.get('textAlign')==='justify'){
align=(this.spacing/2)(this.text.length-rev); // Remove '-1' after this.text.length for proper angle rendering
}else if(this.get('textAlign')==='right'){
align=(this.spacing)
(this.text.length-rev); // Remove '-1' after this.text.length for proper angle rendering
}
var multiplier=this.reverse?1:-1;
for(var i=0, len=this.text.length; i<len; i++){
// Find coords of each letters (radians : angle*(Math.PI / 180)
curAngle=multiplier*(-iparseInt(this.spacing, 10)+align);
angleRadians=curAngle
(Math.PI/180);

				for(var key in this.delegatedProperties){
					this.letters.item(i).set(key, this.get(key));
				}
				this.letters.item(i).set('top', (multiplier-Math.cos(angleRadians)*this.radius));
				this.letters.item(i).set('left', (multiplier+Math.sin(angleRadians)*this.radius));
				this.letters.item(i).setAngle(curAngle);
				this.letters.item(i).set('padding', 0);
				this.letters.item(i).set('selectable', false);
			}
			// Update group coords
			this.letters._calcBounds();
			if(this.reverse){
				this.letters.top=this.letters.top-this.height*2.5;
			}else{
				this.letters.top=0;
			}
			this.letters.left=this.letters.left-this.width/2; // Change here, for proper group display
			//this.letters._updateObjectsCoords();					// Commented off this line for group misplacement
			this.letters.saveCoords();

// this.letters.render(ctx);
this.width=this.letters.width;
this.height=this.letters.height;
this.letters.left=-(this.letters.width/2);
this.letters.top=-(this.letters.height/2);
}
},
render: function (ctx, noTransform){
// do not render if object is not visible
if(!this.visible)
return;
if(!this.letters)
return;

		ctx.save();
		this.transform(ctx);

		var groupScaleFactor=Math.max(this.scaleX, this.scaleY);

		this.clipTo&&fabric.util.clipContext(this, ctx);

		//The array is now sorted in order of highest first, so start from end.
		for(var i=0, len=this.letters.size(); i<len; i++){
			var object=this.letters.item(i),
					originalScaleFactor=object.borderScaleFactor,
					originalHasRotatingPoint=object.hasRotatingPoint;

			// do not render if object is not visible
			if(!object.visible)
				continue;

// object.borderScaleFactor=groupScaleFactor;
// object.hasRotatingPoint=false;

			object.render(ctx);

// object.borderScaleFactor=originalScaleFactor;
// object.hasRotatingPoint=originalHasRotatingPoint;
}
this.clipTo&&ctx.restore();

		//Those lines causes double borders.. not sure why

// if(!noTransform&&this.active){
// this.drawBorders(ctx);
// this.drawControls(ctx);
// }
ctx.restore();
this.setCoords();
},
/**
* @Private
*/
_set: function (key, value){
this.callSuper('_set', key, value);
if(this.letters){
this.letters.set(key, value);
//Properties are delegated with the object is rendered
// if (key in this.delegatedProperties) {
// var i = this.letters.size();
// while (i--) {
// this.letters.item(i).set(key, value);
// }
// }
if(key in this._dimensionAffectingProps){
this._initDimensions();
this.setCoords();
}
}
},
toObject: function (propertiesToInclude){
var object = extend(this.callSuper('toObject', propertiesToInclude), {
radius: this.radius,
spacing: this.spacing,
reverse: this.reverse,
effect: this.effect,
range: this.range,
smallFont: this.smallFont,
largeFont: this.largeFont
//letters: this.letters //No need to pass this, the letters are recreated on the fly every time when initiated
});

		if(!this.includeDefaultValues){
			this._removeDefaultValues(object);
		}
		return object;
	},
	/**
	 * Returns string represenation of a group
	 * @return {String}
	 */
	toString: function (){
		return '#<fabric.CurvedText ('+this.complexity()+'): { "text": "'+this.text+'", "fontFamily": "'+this.fontFamily+'", "radius": "'+this.radius+'", "spacing": "'+this.spacing+'", "reverse": "'+this.reverse+'" }>';
	},
	/* _TO_SVG_START_ */
	/**
	 * Returns svg representation of an instance
	 * @param {Function} [reviver] Method for further parsing of svg representation.
	 * @return {String} svg representation of an instance
	 */
	toSVG: function (reviver){
		var markup=[
			'<g ',
			'transform="', this.getSvgTransform(),
			'">'
		];
		if(this.letters){
			for(var i=0, len=this.letters.size(); i<len; i++){
				markup.push(this.letters.item(i).toSVG(reviver));
			}
		}
		markup.push('</g>');
		return reviver?reviver(markup.join('')):markup.join('');
	}
	/* _TO_SVG_END_ */
});

/**
 * Returns {@link fabric.CurvedText} instance from an object representation
 * @static
 * @memberOf fabric.CurvedText
 * @param {Object} object Object to create a group from
 * @param {Object} [options] Options object
 * @return {fabric.CurvedText} An instance of fabric.CurvedText
 */
fabric.CurvedText.fromObject=function (object){
	return new fabric.CurvedText(object.text, clone(object));
};

fabric.util.createAccessors(fabric.CurvedText);

/**
 * Indicates that instances of this type are async
 * @static
 * @memberOf fabric.CurvedText
 * @type Boolean
 * @default
 */
fabric.CurvedText.async=false;

})(typeof exports!=='undefined'?exports:this);`

@nitin-bc
Copy link

nitin-bc commented Jul 7, 2019

Hi @asturur Any update on this?

@nitin-bc
Copy link

nitin-bc commented Sep 5, 2019

Hi @asturur Would you please confirm by when you are planning to launch updated curved text JS?

@ishaan-puniani
Copy link

Hello, I am also looking for this update

@TheQKnight
Copy link

I am also eager for an update

@gustavorps
Copy link

Bountysource decided to update their Terms of Service:

2.13 Bounty Time-Out.
If no Solution is accepted within two years after a Bounty is posted, then the Bounty will be withdrawn and the amount posted for the Bounty will be retained by Bountysource. For Bounties posted before June 30, 2018, the Backer may redeploy their Bounty to a new Issue by contacting support@bountysource.com before July 1, 2020. If the Backer does not redeploy their Bounty by the deadline, the Bounty will be withdrawn and the amount posted for the Bounty will be retained by Bountysource.

https://www.bountysource.com/issues/446987-curve-text-feature

@asturur
Copy link
Member

asturur commented Jun 19, 2020

thanks @gustavorps i ll try to save the bounty

@asturur asturur closed this as completed Jun 19, 2020
@rechardlc
Copy link

I also hope to update the arc text soon

@rifatwahid
Copy link

rifatwahid commented Aug 18, 2020

@lucasa: Check https://jsfiddle.net/av01d/4p0syzw3/
It's a solution that works for me. Check whether it's what you need.

SVG export not working :(

@melchiar
Copy link
Member

@rifatwahid sit tight, official support for textpath is currently in the works.

@rifatwahid
Copy link

rifatwahid commented Aug 20, 2020

@rifatwahid sit tight, official support for textpath is currently in the works.

@melchiar I have configured the code (https://jsfiddle.net/av01d/4p0syzw3/) in a test project but couldn't make SVG export work :( is it possible to help me with the SVG export?

I have tried with fabricjs 3.6.3 and 4.0.0 as well

@melchiar
Copy link
Member

@rifatwahid Sorry, I'm not able to help you add SVG support. The solution you're using was built by @av01d so you could perhaps reach out to him for help. I'd advise waiting though and using the official text on a path solution when it's ready, which will support SVG import/export as well as the ability to curve text along any arbitrary path, not just circles.

@rifatwahid
Copy link

@melchiar thank you so much for the reply :) any expected or idea about the release date? :)

@melchiar
Copy link
Member

@rifatwahid it will be released when it's ready 😉

@rifatwahid
Copy link

@melchiar I can't sleep for this :D hope it will be released soon. Can I get this feature here: https://github.com/fabricjs/fabric.js/releases/tag/v4.0.0 ?

Thank you so much for making this excellent canvas library 👍 😉

@asturur
Copy link
Member

asturur commented Aug 24, 2020

official progress here:
#6543

@asturur asturur mentioned this issue Aug 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.