Right now I have this polygon code:
import { useMemo } from 'react'
export const SIZE = 2048
export const CENTER = SIZE / 2
const ANGLE = -Math.PI / 2 // Start the first vertex at the top center
type PolygonInput = {
sides: number
stroke?: string
strokeWidth?: number
fill?: string
rotation?: number // New prop for rotation in degrees
}
export function computePolygonPoints({
radius,
centerX,
centerY,
sides,
}: {
radius: number
centerX: number
centerY: number
sides: number
}) {
return Array.from({ length: sides }, (_, i) => {
const angle = (i * 2 * Math.PI) / sides + ANGLE
const x = centerX + radius * Math.cos(angle)
const y = centerY + radius * Math.sin(angle)
return { x, y }
})
}
const Polygon: React.FC<PolygonInput> = ({
sides,
stroke = 'black',
strokeWidth = 0,
fill = 'black',
rotation = 0, // Default rotation is 0 degrees
}) => {
if (sides < 3 || sides > 360) {
throw new Error('Number of sides must be between 3 and 360')
}
const points = useMemo(() => {
const points = computePolygonPoints({
radius: (SIZE - strokeWidth) / 2,
centerX: CENTER,
centerY: CENTER,
sides,
})
return points.map(p => `${p.x},${p.y}`).join(' ')
}, [sides])
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 2048 2048"
>
<g transform={`rotate(${rotation}, ${CENTER}, ${CENTER})`}>
<polygon
points={points}
stroke={stroke}
strokeWidth={strokeWidth}
fill={fill}
strokeLinejoin="round"
/>
</g>
</svg>
)
}
export default Polygon
I use it like this:
<Polygon
sides={3}
strokeWidth={8}
stroke="red"
/>
I wrap it in a <div>
with an explicit height/width, giving the polygon a size.
However, the stroke width is proportionally scaled down to however small the polygon is, so given my 2048 dimensions, when the polygon is 256x256px, you can barely see the stroke at 8px strokeWidth. How can I architect this so that the stroke width in my component stays the props.strokeWidth
value (in pixels), regardless of the width/height of the final rendered SVG?
It seems I need to do something like this, but having trouble thinking this fully through.
Box
component, which uses observers to get the latest computed width/height for the wrapping div.viewBox
in the SVG.But this would mean I can't let the SVG determine the aspect ratio of its own bounding box? Or say I had a 3:5 aspect ratio SVG, I should only set the width or height on the containing Box
, and then use the aspect ratio to determine the SVG bounds? And set box.style.width = fit-content
or something like that?
What is your system for handling this?
There is a property to specify that you do not want your strokes to scale in width. More info here and here.
vector-effect: non-scaling-stroke