如何調整文本大小以適合 D3js 中的任何給定多邊形?
我需要圖片中的東西:

我發現了類似的主題,但沒有可用的解決方案:太舊/不推薦使用/示例不起作用。
uj5u.com熱心網友回復:
這個問題基本上歸結為在多邊形內找到一個最大矩形,在這種情況下與水平軸對齊并且具有固定的縱橫比,由文本給出。
以有效的方式找到這個矩形并不是一件容易的事,但有可用的演算法。例如d3plus-librarylargestRect中的方法。這篇博文中描述了這個演算法的細節(它找到了一個好的但不是最優的矩形)。
使用矩形的坐標,您可以轉換文本,使其包含在矩形中,即
- 平移到矩形的左下角,然后
- 按矩形寬度和文本寬度的比例縮放。
如果您不想在依賴項串列中添加額外的庫,并且您正在考慮的多邊形(幾乎)是凸的并且不是高度不規則的,您可以嘗試自己找到一個“令人滿意的矩形”。下面,我對以多邊形質心為中心的矩形進行了二分搜索。在每次迭代中,我使用d3-polygond3.polygonContains方法檢查四個角是否在多邊形內。生成的矩形為綠色以進行比較。當然,這只是一個起點。
const dim = 500;
const svg = d3.select("svg").attr("width", dim).attr("height", dim);
const text = svg.append("text").attr("x", 0).attr("y", 0);
const polygon = svg.append("polygon").attr("fill", "none").attr("stroke", "blue");
const rectangle = svg.append("polygon").attr("fill", "none").attr("stroke", "red");
const rectangle2 = svg.append("polygon").attr("fill", "none").attr("stroke", "green");
d3.select("input").on("change", fitText);
d3.select("button").on("click", drawPolygon);
// Draw random polygon
function drawPolygon() {
const num_points = 3 Math.ceil(7 * Math.random());
points = [];
for (let i = 0; i < num_points; i ) {
const angle = 2 * Math.PI / num_points * (i 0.1 0.8 * Math.random());
const radius = dim / 2 * (0.1 0.9 * Math.random());
points.push([
radius * Math.cos(angle) dim / 2,
radius * Math.sin(angle) dim / 2,
])
}
polygon.attr("points", points.map(d => d.join()).join(' '));
fitText();
}
function fitText() {
// Set text to input value and reset transform.
text.text(d3.select("input").property("value")).attr("transform", null);
// Get dimensions of text
const text_dimensions = text.node().getBoundingClientRect();
const ratio = text_dimensions.width / text_dimensions.height;
// Find largest rectangle
const rect = d3plus.largestRect(points, {angle: 0, aspectRatio: ratio}).points;
// transform text
const scale = (rect[1][0] - rect[0][0]) / text_dimensions.width;
text.attr("transform", `translate(${rect[3][0]},${rect[3][1]}) scale(${scale})`);
rectangle.attr("points", rect.map(d => d.join()).join(' '));
// alternative
const rect2 = satisfyingRect(ratio);
rectangle2.attr("points", rect2.map(d => d.join()).join(' '));
}
function satisfyingRect(ratio) {
// center rectangle around centroid
const centroid = d3.polygonCentroid(points);
let minWidth = 0;
let maxWidth = d3.max(points, d => d[0]) - d3.min(points, d => d[0]);
let rect;
for (let i = 0; i < 20; i ) {
const width = 0.5 * (maxWidth minWidth);
rect = [
[centroid[0] - width, centroid[1] - width / ratio],
[centroid[0] width, centroid[1] - width / ratio],
[centroid[0] width, centroid[1] width / ratio],
[centroid[0] - width, centroid[1] width / ratio]
]
if (rect.every(d => d3.polygonContains(points, d)))
minWidth = width;
else
maxWidth = width;
}
return rect;
}
let points;
drawPolygon();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/d3plus-shape@1"></script>
<div>
<input type="text" value="lorem ipsum dolor">
<button>New polygon</button>
</div>
<svg></svg>
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/444180.html
