Skip to content

Commit

Permalink
fix(fabric.Text): Text on path, fix non linear distance of chars over…
Browse files Browse the repository at this point in the history
… path (fabricjs#6671)
  • Loading branch information
asturur authored and shanicerae committed Jan 16, 2021
1 parent 432cb43 commit c0b58e9
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 24 deletions.
76 changes: 52 additions & 24 deletions src/util/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,13 @@
return segsNorm;
};


/**
* This function take a parsed SVG path and make it simpler for fabricJS logic.
* simplification consist of: only UPPERCASE absolute commands ( relative converted to absolute )
* S converted in C, T converted in Q, A converted in C.
* @param {Array} path the array of commands of a parsed svg path for fabric.Path
* @return {Array} the simplified array of commands of a parsed svg path for fabric.Path
*/
function makePathSimpler(path) {
// x and y represent the last point of the path. the previous command point.
// we add them to each relative command to make it an absolute comment.
Expand Down Expand Up @@ -455,6 +461,8 @@
};
}

// this will run over a path segment ( a cubic or quadratic segment) and approximate it
// with 100 segemnts. This will good enough to calculate the length of the curve
function pathIterator(iterator, x1, y1) {
var tempP = { x: x1, y: y1 }, p, tmpLen = 0, perc;
for (perc = 0.01; perc <= 1; perc += 0.01) {
Expand All @@ -465,7 +473,43 @@
return tmpLen;
}

//measures the length of a pre-simplified path
/**
* Given a pathInfo, and a distance in pixels, find the percentage from 0 to 1
* that correspond to that pixels run over the path.
* The percentage will be then used to find the correct point on the canvas for the path.
* @param {Array} segInfo fabricJS collection of information on a parsed path
* @param {Number} distance from starting point, in pixels.
* @return {Number} length of segment
*/
function findPercentageForDistance(segInfo, distance) {
var perc = 0, tmpLen = 0, iterator = segInfo.iterator, tempP = { x: segInfo.x, y: segInfo.y },
p, nextLen, nextStep = 0.01;
// nextStep > 0.0001 covers 0.00015625 that 1/64th of 1/100
// the path
while (tmpLen < distance && perc <= 1 && nextStep > 0.0001) {
p = iterator(perc);
nextLen = calcLineLength(tempP.x, tempP.y, p.x, p.y);
// compare tmpLen each cycle with distance, decide next perc to test.
if ((nextLen + tmpLen) > distance) {
// we discard this step and we make smaller steps.
nextStep /= 2;
perc -= nextStep;
}
else {
tempP = p;
perc += nextStep;
tmpLen += nextLen;
}
}
return p;
}

/**
* Run over a parsed and simplifed path and extrac some informations.
* informations are length of each command and starting point
* @param {Array} path fabricJS parsed path commands
* @return {Array} path commands informations
*/
function getPathSegmentsInfo(path) {
var totalLength = 0, len = path.length, current,
//x2 and y2 are the coords of segment start
Expand Down Expand Up @@ -500,6 +544,7 @@
current[5],
current[6]
);
tempInfo.iterator = iterator;
tempInfo.length = pathIterator(iterator, x1, y1);
x1 = current[5];
y1 = current[6];
Expand All @@ -513,6 +558,7 @@
current[3],
current[4]
);
tempInfo.iterator = iterator;
tempInfo.length = pathIterator(iterator, x1, y1);
x1 = current[3];
y1 = current[4];
Expand Down Expand Up @@ -546,6 +592,7 @@
}
var segInfo = infos[i], segPercent = distance / segInfo.length,
command = segInfo.command, segment = path[i];

switch (command) {
case 'M':
return { x: segInfo.x, y: segInfo.y };
Expand All @@ -555,35 +602,15 @@
new fabric.Point(segInfo.destX, segInfo.destY),
segPercent
);
break;
case 'L':
return new fabric.Point(segInfo.x, segInfo.y).lerp(
new fabric.Point(segment[1], segment[2]),
segPercent
);
break;
case 'C':
return getPointOnCubicBezierIterator(
segInfo.x,
segInfo.y,
segment[1],
segment[2],
segment[3],
segment[4],
segment[5],
segment[6]
)(segPercent);
break;
return findPercentageForDistance(segInfo, distance);
case 'Q':
return getPointOnQuadraticBezierIterator(
segInfo.x,
segInfo.y,
segment[1],
segment[2],
segment[3],
segment[4]
)(segPercent);
break;
return findPercentageForDistance(segInfo, distance);
}
}

Expand Down Expand Up @@ -685,6 +712,7 @@

/**
* Draws arc
* @deprecated
* @param {CanvasRenderingContext2D} ctx
* @param {Number} fx
* @param {Number} fy
Expand Down
Binary file added test/visual/golden/textpath3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions test/visual/text_path.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,22 @@
height: 200,
});

function textpath3(canvas, callback) {
var path = new fabric.Path('M -194 -109 C 538 -300 154 50 98 29');
var text = new fabric.Text('Testing constant distance on bezier curve.', { path: path, top: 30, left: 30 });
canvas.add(text);
canvas.renderAll();
callback(canvas.lowerCanvasEl);
}

tests.push({
test: 'Textpath on a particular bezier',
code: textpath3,
golden: 'textpath3.png',
percentage: 0.09,
width: 610,
height: 270,
});

tests.forEach(visualTestLoop(QUnit));
})();

0 comments on commit c0b58e9

Please sign in to comment.