Esquina redondeada SVG

84

Tengo el siguiente 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>

Quiero conseguir un CSS-como border-top-right-radiusy border-top-bottom-radiusefecto.

¿Cómo puedo lograr ese efecto de esquina redondeada?

Danis
fuente
Es una lástima que CSS border-radiusy sus variantes no funcionen en SVG.
Steven Vachon
8
Por cierto. Si tiene un rectángulo, puede agregar rx=3o ry=3redondear las esquinas. developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
Lukas Liesis

Respuestas:

126

Así es como puede crear un rectángulo redondeado con 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" />

Explicación

m100,100: mover al punto (100,100)

h200: dibuja una línea horizontal de 200px desde donde estamos

a20,20 0 0 1 20,20: dibuja un arco con un radio X de 20px, un radio Y de 20px, en el sentido de las agujas del reloj, hasta un punto con una diferencia de 20px en los ejes X e Y

v200: dibuja una línea vertical de 200px desde donde estamos

a20,20 0 0 1-20,20: dibuja un arco con un radio de 20px X e Y, en el sentido de las agujas del reloj, hasta un punto con una diferencia de -20px en X y una diferencia de 20px en el eje Y

h-200: dibuja una línea horizontal de -200px desde donde estamos

a20,20 0 0 1-20, -20: dibuja un arco con un radio X e Y de 20px, en el sentido de las agujas del reloj, hasta un punto con una diferencia de -20px en X y una diferencia de -20px en el eje Y

v-200: dibuja una línea vertical de -200px desde donde estamos

a20,20 0 0 1 20, -20: dibuja un arco con un radio X e Y de 20px, en el sentido de las agujas del reloj, hasta un punto con una diferencia de 20px en X y una diferencia de -20px en el eje Y

z: cierra el camino

<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>

hmak.me
fuente
3
Para cualquier persona interesada en obtener más detalles sobre el arco, esta es la API: A rx ry x-axis-rotation large-arc-flag sweep-flag x y( developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths )
Nic Scozzaro
Si solo desea un rectángulo redondeado y no una forma más compleja (que es como encontré esto mientras buscaba en Google), un enfoque más simple podría ser usar <svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg"> `<rect x =" 5 "y =" 5 "width =" 100 "height = "100" rx = "15" style = "stroke: # 000000; fill: #FFFFFF" /> `</svg>
John Sibly
58

No estoy seguro de por qué nadie publicó una respuesta SVG real. Aquí hay un rectángulo SVG con esquinas redondeadas (radio 3) en la parte superior:

<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" />

Se trata de Mover a (M), Línea a (L), Arco a (A), Línea a (L), Arco a (A), Línea a (L), Cerrar ruta (Z).

Los números delimitados por comas son coordenadas absolutas. Los arcos se definen con parámetros adicionales que especifican el radio y el tipo de arco. Esto también podría lograrse con coordenadas relativas (use letras minúsculas para L y A).

La referencia completa para esos comandos se encuentra en la página W3C SVG Paths , y en este artículo se puede encontrar material de referencia adicional sobre las rutas SVG .

vallismortis
fuente
12
Esta no es directamente la respuesta que estaba buscando, pero Dios mío si no es útil. Siempre me pregunté para qué eran las letras.
Alex McCabe
1
Gracias por la explicación :)
Osman Erdi
47

Como se menciona en mi respuesta a Aplicar esquinas redondeadas a rutas / polígonos , he escrito una rutina en javascript para redondear esquinas genéricamente de rutas SVG, con ejemplos, aquí: http://plnkr.co/edit/kGnGGyoOCKil02k04snu .

Funcionará independientemente de cualquier efecto de accidente cerebrovascular que pueda tener. Para usar, incluya el archivo rounding.js de Plnkr y llame a la función así:

roundPathCorners(pathString, radius, useFractionalRadius)

El resultado será el camino redondeado.

Los resultados se ven así:

Ejemplos de redondeo de ruta SVG

Yona Appletree
fuente
Agradable, aunque la compatibilidad con comandos relativos sería aún mejor.
Joachim Breitner
1
Estoy de acuerdo :) Esto fue solo una pequeña ocasión para resolver mi problema, no un intento de una biblioteca completa. ¡Daría la bienvenida a una bifurcación con esa funcionalidad!
Yona Appletree
¿Tiene un repositorio con esto? Es genial, muchas gracias por hacerlo.
Djave
1
Nunca me molesté en hacer un repositorio ... aunque probablemente debería hacerlo.
Yona Appletree
Me gustaría volver a la pregunta de @ Djave sobre el repositorio; -]
t3chb0t
37

Ha configurado explícitamente su stroke-linejointo roundpero su stroke-widthto 0, por lo que, por supuesto , no verá esquinas redondeadas si no tiene trazo para redondear.

Aquí hay un ejemplo modificado con esquinas redondeadas hechas a través de trazos:
http://jsfiddle.net/8uxqK/1/

<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" />

De lo contrario, si necesita un relleno de forma redondeada real y no solo un trazo grueso redondeado, debe hacer lo que dice @Jlange y crear una forma redondeada real.

Phrogz
fuente
Veo esto correctamente en jsfiddle, pero al copiar a un documento HTML local, es solo un rectángulo simple.
Mads Skjern
6
Puede usar en stroke-linecaplugar de stroke-linejoin. Esto funciona para mi.
lobodart
32

También consideraría usar un viejo simple <rect>que proporcione los atributos rxyry

MDN SVG docs <- observe el segundo elemento rect dibujado

Joshua
fuente
2
Pero el OP quiere que solo se redondeen algunas de las esquinas.
Robert Longson
9
Esto responde a MI pregunta, que es lo que me trajo a esta página. ¡Así que gracias!
Steven Vachon
1
Si necesita usar esquinas redondeadas en algún grupo de elementos, y no solo en un rect, puede hacerlo usando clipPath developer.mozilla.org/pt-BR/docs/Web/SVG/Element/clipPath como puede ver aquí jsfiddle.net/thiagomata/mp28rnj6/1
Thiago Mata
Enlace muerto en OP. :(
posfan12
@ posfan12 Lo arregló para ti :)
Joshua
12

Hoy mismo me encontré con este problema y logré resolverlo escribiendo una pequeña función de JavaScript.

Por lo que puedo decir, no hay una manera fácil de dar un elemento de ruta en un SVG con esquinas redondeadas, excepto si solo necesita que los bordes estén redondeados, en cuyo caso los atributos (CSS) stroke, stroke-widthy lo más importantestroke-linejoin="round" son perfectamente suficientes.

Sin embargo, en mi caso utilicé un objeto de ruta para crear formas personalizadas con n esquinas que se rellenan con un color determinado y no tienen bordes visibles, como esto:

ingrese la descripción de la imagen aquí

Me las arreglé para escribir una función rápida que toma una matriz de coordenadas para una ruta SVG y devuelve la cadena de ruta terminada para poner en el datributo del elemento html de la ruta. La forma resultante se verá así:

ingrese la descripción de la imagen aquí

Aquí está la función:

/**
 * 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(' ');
}

Puede determinar la fuerza del redondeo configurando la variable curveRadius en la parte superior. El valor predeterminado es 3 para un sistema de coordenadas de 100x100 (ventana gráfica), pero dependiendo del tamaño de su SVG, es posible que deba ajustarlo.

Mvin
fuente
1
Esta matemática es asombrosa. Lo entendí y lo implementé en Android para hacer que los polígonos tengan esquinas redondeadas.
Adil Soomro
1
Esta es la razón por la que me encanta StackOverflow.
Bangkokian
5

Esta pregunta es el primer resultado para buscar en Google "svg ruta de esquinas redondeadas". Sugerencia de Phrogz para usarstroke tiene algunas limitaciones (es decir, que no puedo usar el trazo para otros fines y que las dimensiones deben corregirse para el ancho del trazo).

La sugerencia de Jlange de usar una curva es mejor, pero no muy concreta. Terminé usando curvas cuadráticas de Bézier para dibujar esquinas redondeadas. Considere esta imagen de una esquina marcada con un punto azul y dos puntos rojos en los bordes adyacentes:

esquina de una figura marcada en azul con dos puntos en los bordes adyacentes

Las dos líneas se pueden hacer con el Lcomando. Para convertir esta esquina afilada en una esquina redondeada, comience a dibujar una curva desde el punto rojo izquierdo (useM x,y para moverse a ese punto). Ahora, una curva de Bézier cuadrática tiene un solo punto de control que debe establecer en el punto azul. Establezca el final de la curva en el punto rojo de la derecha. Como la tangente en los dos puntos rojos está en la dirección de las líneas anteriores, verá una transición fluida, "esquinas redondeadas".

Ahora, para continuar la forma después de la esquina redondeada, se puede lograr una línea recta en una curva de Bézier estableciendo el punto de control entre en la línea entre las dos esquinas.

Para ayudarme a determinar la ruta, escribí este script de Python que acepta bordes y un radio. La matemática vectorial hace que esto sea realmente muy fácil. La imagen resultante de la salida:

forma creada a partir de la salida del script

#!/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)
Lekensteyn
fuente
3

A continuación, se muestran algunas rutas para las pestañas:

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>

Las otras respuestas explicaron la mecánica. Me gustó especialmente la respuesta de hossein-maktoobian.

Las rutas en la pluma hacen la mayor parte del trabajo, los valores se pueden modificar para adaptarse a las dimensiones deseadas.

Jackie
fuente
1

Encontré una solución, pero es un poco engañosa, por lo que puede que no siempre funcione. Descubrí que si tiene un arco (A o a) con valores realmente pequeños, lo obliga a crear una curva en un punto, formando así una esquina redondeada ...

<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>

Dennis Ranish
fuente
1

Solo para simplificar la implementación de la respuesta de @ hmak.me, aquí hay un fragmento comentado de código React para generar rectángulos redondeados.

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'),
);

Enlace jsfiddle.

polkovnikov.ph
fuente
-2
<?php
$radius = 20;
$thichness = 4;
$size = 200;

if($s == 'circle'){
  echo '<svg width="' . $size . '" height="' . $size . '">';
  echo '<circle cx="' . ($size/2) . '" cy="' . ($size/2) . '" r="' . (($size/2)-$thichness) . '" stroke="black" stroke-width="' . $thichness . '" fill="none" />';
  echo '</svg>';
}elseif($s == 'square'){
  echo '<svg width="' . $size . '" height="' . $size . '">';
  echo '<path d="M' . ($radius+$thichness) . ',' . ($thichness) . ' h' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 ' . $radius . ',' . $radius . ' v' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 -' . $radius . ',' . $radius . ' h-' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 -' . $radius . ',-' . $radius . ' v-' . ($size-($radius*2)-($thichness*2)) . ' a' . $radius . ',' . $radius . ' 0 0 1 ' . $radius . ',-' . $radius . ' z" fill="none" stroke="black" stroke-width="' . $thichness . '" />';
  echo '</svg>';
}
?>
Monsenhor enVide neFelibata
fuente
-5

Está utilizando un elemento de ruta, ¿por qué no le da una curva a la ruta? Vea aquí cómo hacer curvas usando elementos de ruta: http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands

DescansandoRobot
fuente
Gracias por tus respuestas. Son realmente útiles, pero el problema es que uso gráficos de KendoUI y las rutas se crean dinámicamente. Intenté cambiarlas con el método que ofrece Phrogz, pero obtengo el efecto border-radius = 10px, pero necesito border-top- left-radius = 10px y border-bottom-left-radius = 10px solamente. Soy realmente nuevo en SVG, así que el segundo método no es para mí. Entonces, ¿puedes escribirme las coordinadas de ruta? Gracias de antemano
Danis
Por mucho que me encantaría hacer esto por usted, simplemente no tengo tiempo para revisar la ubicación matemática / de coordenadas. No debería ser demasiado difícil si usa los comandos de arco elíptico en el enlace.
RestingRobot