我正在制作一个 vue.js 应用程序,它使用大量不同的图标,因此我决定在 node.js 中制作一个小图标生成器来标准化它们的使用,并且它还会“裁剪”每个 svg 以使其适合其父级(使用viewbox 属性)。
虽然我偶然发现了一个问题,但缩放图标以适应正方形会使更多的“方形”看起来比圆形更大,所以我想以编程方式将它们缩放为圆形:
* {
box-sizing: border-box;
}
.container {
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: 2rem;
}
.title {
font-family: sans-serif;
font-weight: 600;
font-size: 24px;
margin: 0;
}
.grid {
display: flex;
gap: 1.5rem;
}
.group {
display: flex;
flex-direction: column;
gap: 1rem;
text-align: center;
}
.bg {
width: 80px;
height: 80px;
background-color: lightgray;
}
.zone {
border: 2px solid red;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.zone--wanted {
border-radius: 50%;
}
.icon {
width: 100%;
height: 100%;
background-color: coral;
}
.icon--rounded {
border-radius: 20%;
}
.icon--circle {
border-radius: 50%;
}
.icon-scaled--rounded {
width: 80%;
height: 80%;
}
.icon-scaled--square {
width: 70.71068%;
height: 70.71068%;
}
<div class="container">
<p class="title">What I have :</p>
<div class="grid">
<div class="bg">
<div class="zone">
<div class="icon"></div>
</div>
</div>
<div class="bg">
<div class="zone">
<div class="icon icon--rounded"></div>
</div>
</div>
<div class="bg">
<div class="zone">
<div class="icon icon--circle"></div>
</div>
</div>
</div>
<p class="title">What I want :</p>
<div class="grid">
<div class="bg">
<div class="zone zone--wanted">
<div class="icon icon-scaled--square"></div>
</div>
</div>
<div class="bg">
<div class="zone zone--wanted">
<div class="icon icon--rounded icon-scaled--rounded"></div>
</div>
</div>
<div class="bg">
<div class="zone zone--wanted">
<div class="icon icon--circle"></div>
</div>
</div>
</div>
</div>
你们知道如何做到这一点吗?
这是一个非常原始的想法:由于很容易获得元素的水平和垂直范围,因此如果我们旋转它并在每次旋转时对宽度和高度进行采样,我们就可以得到最大直径的合理估计
d
。按 r/d
缩放(其中 r
是要放入的圆的直径)应该效果不错。这对于您的示例特别有效,因为我们知道最大直径位于 45 度旋转处,因此我们不需要很多样本;一个就够了。如果您有一个奇怪的形状,例如直径为 10 度,则您将需要更多样本来近似最大值。如果采样不足,SVG 元素会从圆圈中露出一点。
function unitCircleScale(element, angles=4) {
if (typeof angles == "number") {
angles = Array.from({length: angles}, (_, i) => i * 90 / angles)
}
const initTransform = element.style.transform
const transform = initTransform.replace(/rotate\([^)]*\)\s*;?/, '')
const sizes = []
for (const angle of angles) {
element.style.transform = `rotate(${angle}deg) ${transform}`
const bbox = element.parentNode.getBBox()
sizes.push(bbox.width, bbox.height)
}
element.style.transform = initTransform
return 1 / Math.max(...sizes)
}
const [rect, square, circle] = document.querySelectorAll("rect, circle")
// to fit into a 100-radius (200-diameter) circle, scale by:
// rough idea, at just 0deg and 45deg
console.log("rect at 2 samples:", 100 * unitCircleScale(rect, 2))
// better idea, with more samples
console.log("rect at 16 samples:", 100 * unitCircleScale(rect, 16))
// 45deg is ideal for a square, test just that
console.log("square at 45deg:", 100 * unitCircleScale(square, [45]))
// circle is same however you turn it, just do a single sample (i.e. 0deg)
console.log("circle at 1 sample:", 100 * unitCircleScale(circle, 1))
<svg width="400" height="120" xmlns="http://www.w3.org/2000/svg">
<g>
<rect width="150" height="50" x="10" y="10" style="fill:none; stroke:red" />
</g>
<g>
<rect width="100" height="100" x="170" y="10" style="fill:none; stroke:blue" />
</g>
<g>
<circle cx="330" cy="60" r="50" style="fill:none; stroke:green" />
</g>
Sorry, your browser does not support inline SVG.
</svg>
请注意,这不会很快,因为它会交替更改属性然后读取边界框,这意味着它将强制每次迭代重新布局。理想情况下,您希望计算一次理想的缩放比例,并将其缓存。