|
function get_path_coordinates(path) { |
|
// "M 10 10 L 10 100 .." -> ["10", "10", "10", "100" ..] |
|
let coordList = path.getAttribute("d").replace(/[mlz]/gi, "").split(" ").filter((c) => c.trim() != "") |
|
// ["10", "10", "10", "100" ..] -> [{x: 10, y: 10}, {x: 10, y: 100}] |
|
let coordinates = coordList.reduce(function(result, value, index, array) { |
|
if (index % 2 === 0) result.push({x: array[index], y: array[index+1]}); |
|
return result; |
|
}, []); |
|
return coordinates; |
|
} |
|
|
|
function calculateLargestRadius(coordinates) { |
|
let largestRadius = 0; |
|
const numberOfCoordinates = coordinates.length |
|
for (let i = 0; i < numberOfCoordinates; i++) { |
|
const coorBefore = i === 0 ? coordinates[numberOfCoordinates - 1] : coordinates[i - 1]; |
|
const coor = coordinates[i]; |
|
const coorAfter = i === numberOfCoordinates - 1 ? coordinates[0] : coordinates[i + 1]; |
|
// construct temporary line path (beforLine) going from point to point before current point |
|
const lineBefore = getLine(coor, coorBefore); |
|
// construct temporary line path (afterLine) going from point to point after current point |
|
const lineAfter = getLine(coor, coorAfter); |
|
// Calculate a largest radius |
|
const maxRadius = parseInt( Math.min(lineBefore.getTotalLength(), lineAfter.getTotalLength()) / 2 ); |
|
largestRadius = maxRadius > largestRadius ? maxRadius : largestRadius; |
|
} |
|
return largestRadius |
|
} |
|
|
|
function buildLineList(coordinates) { |
|
let lines = [] |
|
const numberOfCoordinates = coordinates.length |
|
for (let i = 0; i < numberOfCoordinates; i++) { |
|
const coorBefore = i === 0 ? coordinates[numberOfCoordinates - 1] : coordinates[i - 1]; |
|
const coor = coordinates[i]; |
|
const coorAfter = i === numberOfCoordinates - 1 ? coordinates[0] : coordinates[i + 1]; |
|
// construct temporary line path (beforLine) |
|
// going from point to point before current point |
|
const lineBefore = getLine(coor, coorBefore); |
|
// construct temporary line path (afterLine) |
|
// going from point to point after current point |
|
const lineAfter = getLine(coor, coorAfter); |
|
// Line between two lines |
|
let lineBetween = getLine(coorBefore, coorAfter); |
|
let lineBetweenLength = lineBetween.getTotalLength(); |
|
let middlePoint = lineBetween.getPointAtLength(lineBetweenLength / 2); |
|
lineBetween = getLine(coor, middlePoint); |
|
// Update max radius |
|
const maxRadius = parseInt( |
|
Math.min(lineBefore.getTotalLength(), lineAfter.getTotalLength()) / 2 |
|
); |
|
// Update the lines |
|
lines.push({ |
|
lineBefore, lineAfter, coor, |
|
lineBetween, maxRadius |
|
}); |
|
} |
|
return lines |
|
} |
|
|
|
function calculateRoundedCorners(originalPath, radius, radius2) { |
|
const coordinates = get_path_coordinates(originalPath) |
|
const largestRadius = calculateLargestRadius(coordinates) |
|
const lines = buildLineList(coordinates) |
|
|
|
let d = ""; |
|
|
|
if (coordinates.length) { |
|
// for each point |
|
for (let i = 0; i < coordinates.length; i++) { |
|
let { |
|
lineBefore, |
|
lineAfter, |
|
coor, |
|
lineBetween, |
|
maxRadius |
|
} = lines[i]; |
|
const minorRadius = Math.min(radius, maxRadius); |
|
const minorRadius2 = Math.min(radius2, maxRadius); |
|
const beforePoint = lineBefore.getPointAtLength(minorRadius); |
|
const afterPoint = lineAfter.getPointAtLength(minorRadius); |
|
const beforePoint2 = lineBefore.getPointAtLength(minorRadius2); |
|
const afterPoint2 = lineAfter.getPointAtLength(minorRadius2); |
|
coor = lineBetween.getPointAtLength(minorRadius2); |
|
// generate data to new rounded path |
|
if (i === 0) d += "M"; |
|
else d += "L"; |
|
d += `${getCoordinates( beforePoint )} |
|
C |
|
${getCoordinates(beforePoint2)} |
|
${getCoordinates(afterPoint2)} |
|
${getCoordinates(afterPoint)} `; |
|
} |
|
d += " Z" |
|
} |
|
return d; |
|
} |
|
|
|
function getCoordinates(point) { |
|
return `${Math.round(point.x)} ${Math.round(point.y)}`; |
|
} |
|
|
|
function getLine(coor1, coor2) { |
|
const line = getElement("path"); |
|
line.setAttribute("d", `M ${coor1.x} ${coor1.y} L ${coor2.x} ${coor2.y}`); |
|
return line; |
|
} |
|
|
|
function getElement(tagName, attrs) { |
|
let svg = document.querySelector("svg") |
|
let SVGNS = svg.namespaceURI |
|
|
|
const ele = document.createElementNS(SVGNS, tagName); |
|
const allAttributes = { |
|
...attrs |
|
}; |
|
Object.keys(allAttributes).forEach((att) => { |
|
ele.setAttribute(att, allAttributes[att]); |
|
}); |
|
return ele; |
|
} |