programing

SVG 둥근 모서리

oldcodes 2023. 10. 21. 10:50
반응형

SVG 둥근 모서리

다음 SVG가 있습니다.

<svg>
  <g>
    <path id="k9ffd8001" d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z" stroke="#808600" stroke-width="0" transform="rotate(0 0 0)" stroke-linecap="square" stroke-linejoin="round" fill-opacity="1" stroke-opacity="1" fill="#a0a700"></path>
    <path id="kb8000001" d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z" stroke="#808600" stroke-width="0" transform="rotate(0 0 0)" stroke-linecap="square" stroke-linejoin="round" fill-opacity="1" stroke-opacity="1" fill="url(#k9ffb0001)"></path>
  </g>
</svg>

CSS 같은 것을 사고 싶습니다.border-top-right-radius그리고.border-top-bottom-radius영향.

어떻게 하면 그 둥근 모서리 효과를 얻을 수 있을까요?

SVG Path로 둥근 직사각형을 만드는 방법은 다음과 같습니다.

<path d="M100,100 h200 a20,20 0 0 1 20,20 v200 a20,20 0 0 1 -20,20 h-200 a20,20 0 0 1 -20,-20 v-200 a20,20 0 0 1 20,-20 z" />

설명.

m100,100: 점으로 이동(100,100)

h200: 우리가 있는 곳에서 200 px의 가로선을 긋다.

a20,200 2010,20: X축과 Y축에서 20px 차이가 나는 점에 시계 방향으로 20px X 반경, 20px Y 반경으로 호를 그립니다.

v200: 우리가 있는 곳에서 200 px의 수직선을 그립니다.

a20,2001 -20,20: 시계 방향으로 20px X와 Y 반경으로 X는 -20px, Y축은 20px 차이가 나는 지점까지 호를 그립니다.

h-200: 현재 위치에서 -200 px의 가로선을 그립니다.

a20,2001 -20,-20: 시계 방향으로 20px X와 Y 반지름이 20px X와 Y 축이 -20px 차이가 나는 지점까지 호를 그립니다.

v-200: 현재 위치에서 -200 px 수직선을 그립니다.

a20,200 2010,-20: 시계 방향으로 20px X와 Y 반지름이 20px X와 Y 축이 -20px 차이가 나는 지점에 호를 그립니다.

z: 길을 막다

<svg width="440" height="440">
  <path d="M100,100 h200 a20,20 0 0 1 20,20 v200 a20,20 0 0 1 -20,20 h-200 a20,20 0 0 1 -20,-20 v-200 a20,20 0 0 1 20,-20 z" fill="none" stroke="black" stroke-width="3" />
</svg>

왜 아무도 실제 SVG 답변을 올리지 않았는지 잘 모르겠습니다.상단에 둥근 모서리(반지름 3)가 있는 SVG 직사각형은 다음과 같습니다.

<path d="M0,0 L0,27 A3,3 0 0,0 3,30 L7,30 A3,3 0 0,0 10,27 L10,0 Z" />

이동 위치(M), 선 위치(L), 호 위치(A), 선 위치(L), 호 위치(L), 호 위치(A), 선 위치(L), 경로 닫기(Z)입니다.

쉼표로 구분된 숫자는 절대 좌표입니다.원호는 반지름과 원호 유형을 지정하는 추가 파라미터로 정의됩니다.이는 상대 좌표(L과 A의 소문자 사용)로도 수행할 수 있습니다.

해당 명령에 대한 전체 참조는 W3C SVG 경로 페이지에 있으며, SVG 경로에 대한 추가 참조 자료는 문서에서 확인할 수 있습니다.

경로/다각형에 둥근 모서리 적용에 대한 답변에서 언급한 바와 같이 SVG 경로의 모서리를 일반적으로 반올림하는 루틴을 자바스크립트로 작성했습니다. 예를 들어 http://plnkr.co/edit/kGnGGyoOCKil02k04snu .

이 기능은 사용자가 가질 수 있는 뇌졸중 효과와 독립적으로 작동합니다.사용하려면 Plnkr에서 around.js 파일을 포함하고 다음과 같이 함수를 호출합니다.

roundPathCorners(pathString, radius, useFractionalRadius)

결과는 둥근 경로가 됩니다.

결과는 다음과 같습니다.

SVG Path Rounding Examples

사용자가 다음을 명시적으로 설정했습니다.stroke-linejoin.round하지만 당신의stroke-width.0, 물론 라운드할 스트로크가 없다면 라운드진 모서리를 볼 수 없습니다.

다음은 획으로 만든 둥근 모서리를 가진 수정된 예입니다.

<path d="M64.5 45.5 82.5 45.5 82.5 64.5 64.5 64.5 z"
      stroke-width="5"
      stroke-linejoin="round"
      stroke="#808600"
      fill="#a0a700" />

그렇지 않으면, 둥글고 지방이 많은 획이 아니라 실제 둥근 모양을 채워야 하는 경우 @Jlange가 말하는 대로 하고 실제 둥근 모양을 만들어야 합니다.

일반적인 오래된 것을 사용하는 것도 고려해 보겠습니다.<rect>이것은 다음을 제공합니다.rx그리고.ry특성

MDN SVG 문서 <- 두 번째로 그린 직선 요소 참고

<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
  <rect x="5"   y="5"   width="100" height="100" fill="white" stroke="black" strokeWidth="2" rx="0" ry="0" />
  <rect x="115" y="5"   width="100" height="100" fill="white" stroke="black" strokeWidth="2" rx="30" ry="15" />
  <rect x="5"   y="115" width="100" height="100" fill="white" stroke="black" strokeWidth="2" rx="15" ry="30" />
  <rect x="115" y="115" width="100" height="100" fill="white" stroke="black" strokeWidth="2" rx="30" ry="30" />
</svg>

저도 오늘 우연히 이 문제를 발견했고 작은 자바스크립트 함수를 작성해서 해결했습니다.

SVG 둥근 모서리에 경로 요소를 제공하는 방법은 테두리만 둥글게 해야 하는 경우를 제외하고는 없습니다. 이 경우 (CSS) 특성이 적용됩니다.stroke,stroke-width그리고 가장 중요한 것은stroke-linejoin="round"완벽하게 충분합니다.

그러나 제 경우에는 경로 객체를 사용하여 특정 색으로 채워지고 에 보이는 테두리가 없는 n개의 모서리로 사용자 지정 도형을 만들었습니다.

enter image description here

SVG 경로에 대한 좌표 배열을 취하고 완성된 경로 문자열을 반환하여 입력하는 빠른 함수를 작성하는 데 성공했습니다.dpath html 요소의 속성입니다.결과적인 모양은 다음과 같습니다.

enter image description here

기능은 다음과 같습니다.

/**
 * Creates a coordinate path for the Path SVG element with rounded corners
 * @param pathCoords - An array of coordinates in the form [{x: Number, y: Number}, ...]
 */
function createRoundedPathString(pathCoords) {
    const path = [];
    const curveRadius = 3;

    // Reset indexes, so there are no gaps
    pathCoords = pathCoords.slice();

    for (let i = 0; i < pathCoords.length; i++) {

      // 1. Get current coord and the next two (startpoint, cornerpoint, endpoint) to calculate rounded curve
      const c2Index = ((i + 1) > pathCoords.length - 1) ? (i + 1) % pathCoords.length : i + 1;
      const c3Index = ((i + 2) > pathCoords.length - 1) ? (i + 2) % pathCoords.length : i + 2;

      const c1 = pathCoords[i];
      const c2 = pathCoords[c2Index];
      const c3 = pathCoords[c3Index];

      // 2. For each 3 coords, enter two new path commands: Line to start of curve, bezier curve around corner.

      // Calculate curvePoint c1 -> c2
      const c1c2Distance = Math.sqrt(Math.pow(c1.x - c2.x, 2) + Math.pow(c1.y - c2.y, 2));
      const c1c2DistanceRatio = (c1c2Distance - curveRadius) / c1c2Distance;
      const c1c2CurvePoint = [
        ((1 - c1c2DistanceRatio) * c1.x + c1c2DistanceRatio * c2.x).toFixed(1),
        ((1 - c1c2DistanceRatio) * c1.y + c1c2DistanceRatio * c2.y).toFixed(1)
      ];

      // Calculate curvePoint c2 -> c3
      const c2c3Distance = Math.sqrt(Math.pow(c2.x - c3.x, 2) + Math.pow(c2.y - c3.y, 2));
      const c2c3DistanceRatio = curveRadius / c2c3Distance;
      const c2c3CurvePoint = [
        ((1 - c2c3DistanceRatio) * c2.x + c2c3DistanceRatio * c3.x).toFixed(1),
        ((1 - c2c3DistanceRatio) * c2.y + c2c3DistanceRatio * c3.y).toFixed(1)
      ];

      // If at last coord of polygon, also save that as starting point
      if (i === pathCoords.length - 1) {
        path.unshift('M' + c2c3CurvePoint.join(','));
      }

      // Line to start of curve (L endcoord)
      path.push('L' + c1c2CurvePoint.join(','));
      // Bezier line around curve (Q controlcoord endcoord)
      path.push('Q' + c2.x + ',' + c2.y + ',' + c2c3CurvePoint.join(','));
    }
    // Logically connect path to starting point again (shouldn't be necessary as path ends there anyway, but seems cleaner)
    path.push('Z');

    return path.join(' ');
}

원곡선 반지름 변수를 상단에 설정하여 반올림 강도를 결정할 수 있습니다.100x100(뷰포트) 좌표계의 기본값은 3이지만 SVG의 크기에 따라 이를 조정해야 할 수도 있습니다.

내 경우에는 시작과 끝을 반경으로 돌려야 합니다.path:

enter image description here

와 함께stroke-linecap: round;원하는 대로 변경합니다.

enter image description here

이 질문은 구글에서 "svg 둥근 모서리 경로"를 검색하는 첫 번째 결과입니다.Phrogz를 을 합니다.stroke몇 가지 제한이 있습니다(즉, 스트로크를 다른 용도로 사용할 수 없으며 스트로크 폭에 대해 치수를 수정해야 함).

곡선을 사용하는 것이 더 낫지만 그다지 구체적이지는 않습니다.저는 둥근 모서리를 그리기 위해 2차 베지어 곡선을 사용하게 되었습니다.파란색 점과 두 개의 빨간색 점이 인접한 가장자리에 표시된 모서리의 그림을 생각해 보십시오.

corner of a figure marked blue with two points on the adjacent edges

그 두개의 선들은 그것으로 만들어 질 수 있었습니다.L지휘.이 뾰족한 모서리를 둥근 모서리로 바꾸려면 왼쪽 빨간색 점에서 곡선 그리기를 시작합니다(사용).M x,y그 지점으로 이동합니다).이제 2차 베지어 곡선에는 파란색 점에 설정해야 하는 단일 관리점만 있습니다.곡선의 끝을 오른쪽 빨간색 점으로 설정합니다.두 빨간색 점의 접선이 이전 선의 방향이므로 "원형 모서리"라는 유창한 전환을 볼 수 있습니다.

이제 둥근 모서리 뒤의 모양을 계속하려면 두 모서리 사이의 선 위에 컨트롤 포인트를 설정하여 베지어 곡선의 직선을 만들 수 있습니다.

경로를 결정하는 데 도움이 되기 위해 가장자리와 반지름을 수용하는 파이썬 스크립트를 작성했습니다.벡터 수학은 이것을 실제로 매우 쉽게 만듭니다.출력 결과 이미지:

shape created from script output

#!/usr/bin/env python
# Given some vectors and a border-radius, output a SVG path with rounded
# corners.
#
# Copyright (C) Peter Wu <peter@lekensteyn.nl>

from math import sqrt

class Vector(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def sub(self, vec):
        return Vector(self.x - vec.x, self.y - vec.y)

    def add(self, vec):
        return Vector(self.x + vec.x, self.y + vec.y)

    def scale(self, n):
        return Vector(self.x * n, self.y * n)

    def length(self):
        return sqrt(self.x**2 + self.y**2)

    def normal(self):
        length = self.length()
        return Vector(self.x / length, self.y / length)

    def __str__(self):
        x = round(self.x, 2)
        y = round(self.y, 2)
        return '{},{}'.format(x, y)

# A line from vec_from to vec_to
def line(vec_from, vec_to):
    half_vec = vec_from.add(vec_to.sub(vec_from).scale(.5))
    return '{} {}'.format(half_vec, vec_to)

# Adds 'n' units to vec_from pointing in direction vec_to
def vecDir(vec_from, vec_to, n):
    return vec_from.add(vec_to.sub(vec_from).normal().scale(n))

# Draws a line, but skips 'r' units from the begin and end
def lineR(vec_from, vec_to, r):
    vec = vec_to.sub(vec_from).normal().scale(r)
    return line(vec_from.add(vec), vec_to.sub(vec))

# An edge in vec_from, to vec_to with radius r
def edge(vec_from, vec_to, r):
    v = vecDir(vec_from, vec_to, r)
    return '{} {}'.format(vec_from, v)


# Hard-coded border-radius and vectors
r = 5
a = Vector(  0,  60)
b = Vector(100,   0)
c = Vector(100, 200)
d = Vector(  0, 200 - 60)

path = []
# Start below top-left edge
path.append('M {} Q'.format(a.add(Vector(0, r))))

# top-left edge...
path.append(edge(a, b, r))
path.append(lineR(a, b, r))
path.append(edge(b, c, r))
path.append(lineR(b, c, r))
path.append(edge(c, d, r))
path.append(lineR(c, d, r))
path.append(edge(d, a, r))
path.append(lineR(d, a, r))

# Show results that can be pushed into a <path d="..." />
for part in path:
    print(part)

탭의 경로는 다음과 같습니다.

https://codepen.io/mochime/pen/VxxzMW

<!-- left tab -->
<div>
  <svg width="60" height="60">
    <path d="M10,10 
             a10 10 0 0 1 10 -10
             h 50   
             v 47
             h -50
             a10 10 0 0 1 -10 -10
             z"
      fill="#ff3600"></path>
  </svg>
</div>

<!-- right tab -->
<div>
  <svg width="60" height="60">
    <path d="M10 0   
             h 40
             a10 10 0 0 1 10 10
             v 27
             a10 10 0 0 1 -10 10
             h -40
             z"
      fill="#ff3600"></path>
  </svg>
</div>

<!-- tab tab :) -->
<div>
  <svg width="60" height="60">
    <path d="M10,40 
             v -30
             a10 10 0 0 1 10 -10
             h 30
             a10 10 0 0 1 10 10
             v 30
             z"
      fill="#ff3600"></path>
  </svg>
</div>

다른 대답들은 역학에 대해 설명했습니다.특히 호세인 막투비안의 대답이 마음에 들었습니다.

펜 안의 경로는 작업의 대부분을 차지하며, 값은 원하는 차원에 맞게 수정될 수 있습니다.

이것은 기본적으로 Mvins의 답변과 동일하지만, 더 압축되고 단순화된 버전입니다.모서리에 인접한 선들의 반지름의 거리를 거슬러 올라가 제어점이 원래 모서리 지점에 있는 베지어 곡선으로 양 끝을 연결하여 작동합니다.

function createRoundedPath(coords, radius, close) {
  let path = ""
  const length = coords.length + (close ? 1 : -1)
  for (let i = 0; i < length; i++) {
    const a = coords[i % coords.length]
    const b = coords[(i + 1) % coords.length]
    const t = Math.min(radius / Math.hypot(b.x - a.x, b.y - a.y), 0.5)

    if (i > 0) path += `Q${a.x},${a.y} ${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`

    if (!close && i == 0) path += `M${a.x},${a.y}`
    else if (i == 0) path += `M${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`

    if (!close && i == length - 1) path += `L${b.x},${b.y}`
    else if (i < length - 1) path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`
  }
  if (close) path += "Z"
  return path
}

모서리 반지름이 다른 직사각형을 생성하기 위한 반응 코드 조각은 다음과 같습니다.

const Rect = ({width, height, tl, tr, br, bl}) => {
    const top = width - tl - tr;
    const right = height - tr - br;
    const bottom = width - br - bl;
    const left = height - bl - tl;
    const d = `
        M${tl},0
        h${top}
        a${tr},${tr} 0 0 1 ${tr},${tr}
        v${right}
        a${br},${br} 0 0 1 -${br},${br}
        h-${bottom}
        a${bl},${bl} 0 0 1 -${bl},-${bl}
        v-${left}
        a${tl},${tl} 0 0 1 ${tl},-${tl}
        z
    `;
    return (
        <svg width={width} height={height}>
            <path d={d} fill="black" />
        </svg>
    );
};

ReactDOM.render(
    <Rect width={200} height={100} tl={20} tr={0} br={20} bl={60} />,
    document.querySelector('#app'),
);

https://jsfiddle.net/v1Ljpxh7/

답변 of @hmak.me 을 구현하는 것을 간단히 하기 위해, 둥근 직사각형을 생성하기 위한 React 코드의 주석이 있습니다.

const Rect = ({width, height, round, strokeWidth}) => {
    // overhang over given width and height that we get due to stroke width
    const s = strokeWidth / 2;

    // how many pixels do we need to cut from vertical and horizontal parts
    // due to rounded corners and stroke width
    const over = 2 * round + strokeWidth;

    // lengths of straight lines
    const w = width - over;
    const h = height - over;

    // beware that extra spaces will not be minified
    // they are added for clarity
    const d = `
        M${round + s},${s}
        h${w}
        a${round},${round} 0 0 1 ${round},${round}
        v${h}
        a${round},${round} 0 0 1 -${round},${round}
        h-${w}
        a${round},${round} 0 0 1 -${round},-${round}
        v-${h}
        a${round},${round} 0 0 1 ${round},-${round}
        z
    `;
    return (
        <svg width={width} height={height}>
            <path d={d} fill="none" stroke="black" strokeWidth={strokeWidth} />
        </svg>
    );
};

ReactDOM.render(
    <Rect width={64} height={32} strokeWidth={2} round={4} />,
    document.querySelector('#app'),
);

Jsfiddle 링크.

나는 이 작은 활자 함수를 써서 디바와 비슷한 복잡한 둥근 직사각형의 경로를 동적으로 만들 수 있게 했습니다.border-radius.

export function roundedRectPath( x: number, y: number, width: number, height: number, bevel: [number, number, number, number] = [3, 3, 3, 3] ): string { return "M" + x + "," + y + `m 0 ${bevel[0]}` + `q 0 -${bevel[0]} ${bevel[0]} -${bevel[0]}` + `l ${width - bevel[0] - bevel[1]} 0` + `q ${bevel[1]} 0 ${bevel[1]} ${bevel[1]}` + `l 0 ${height - bevel[1] - bevel[2]}` + `q 0 ${bevel[2]} -${bevel[2]} ${bevel[2]}` + `l -${width - bevel[2] - bevel[3]} 0` + `q -${bevel[3]} 0 -${bevel[3]} -${bevel[3]}` + `z`; }

해결책을 찾았는데, 조금 딱딱해서 항상 안 될 수도 있어요.아주 작은 값을 가진 호(A 또는 a)가 있으면 한 지점에 곡선을 만들게 하고 따라서 둥근 코머를 형성한다는 것을 발견했습니다.

<svg viewBox="0 0 1 0.6" stroke="black" fill="grey" style="stroke-width:0.05px;">
  <path d="M0.7 0.2 L0.1 0.1 A0.0001 0.0001 0 0 0 0.099 0.101 L0.5 0.5Z"></path>
</svg>

경로 요소를 사용하고 있는데, 경로에 곡선을 주면 어떨까요?경로 요소를 사용하여 곡선을 만드는 방법은 여기를 참조하십시오. http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

언급URL : https://stackoverflow.com/questions/10177985/svg-rounded-corner

반응형