Descomposición de círculo SVG a caminos
Publicado: 2022-03-10Este artículo comienza con una confesión: me gusta codificar SVG a mano. No siempre es así, pero a menudo puede parecer peculiar para las personas que no comparten mi predilección. Hay una buena cantidad de beneficios al poder escribir SVG a mano, como optimizar los SVG de formas que una herramienta no puede (convertir una ruta en una ruta o forma más simple), o simplemente comprender cómo funcionan las bibliotecas como D3 o Greensock. .
Dicho esto, me gustaría mirar más de cerca las formas circulares en SVG y las cosas que podemos hacer con ellas cuando pasamos de un círculo básico. ¿Por qué círculos? Bueno, me encantan los círculos. Son mi forma favorita.
En primer lugar (esperemos que haya visto un círculo básico en SVG antes), aquí hay un bolígrafo que muestra uno:
Vea el círculo Pen de Bryan Rasmussen.
Se pueden hacer muchas cosas con un círculo: se puede animar y se le pueden aplicar diferentes colores. Aún así, hay dos cosas muy buenas que no puede hacer un círculo en SVG 1.1: no puede hacer que otro elemento gráfico se mueva a lo largo de la ruta del círculo (usando el elemento animateMotion
) y no puede dar forma a un texto a lo largo de la ruta de un círculo (esto solo se permitirá después del lanzamiento de SVG 2.0).
Convertir nuestro círculo en un camino
Hay una pequeña herramienta en línea que puede ayudarlo a crear caminos a partir de círculos (puede probarlo aquí), pero vamos a crear todo desde cero para que podamos descubrir qué sucede realmente detrás de escena.
Para hacer un camino circular, en realidad vamos a hacer dos arcos, es decir, semicírculos que completan el círculo en un solo camino. Como probablemente haya notado en el SVG anterior, los atributos CX
, CY
y R
definen respectivamente dónde se dibuja el círculo a lo largo de los ejes X e Y, mientras que R
define el radio del círculo. CX
y CY
crean el centro del círculo, por lo que el círculo se dibuja alrededor de ese punto.
La replicación de ese círculo podría verse así:
<path d=" M (CX - R), CY a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 " />
Tenga en cuenta que CX
es lo mismo que el atributo cx
del círculo; lo mismo ocurre con CY
y el atributo cy
del círculo, así como con R
y el atributo r
del círculo. El carácter a
pequeño se utiliza para definir un segmento de un arco elíptico. Puede usar una Z
opcional (o z
) para cerrar la ruta.
La letra minúscula a
denota el comienzo de un arco elíptico dibujado en relación con la posición actual, o en nuestro caso específico:
<path d=" M 25, 50 a 25,25 0 1,1 50,0 a 25,25 0 1,1 -50,0 " />
Puedes ver la magia sucediendo en este bolígrafo:
Vea el círculo Pen from path por Bryan Rasmussen.
Oculto debajo del camino hay un círculo con un relleno rojo. Mientras juegas con los valores de la ruta, verás ese círculo siempre que la ruta cubra totalmente el círculo (la ruta en sí es un círculo del mismo tamaño), y sabremos que estamos haciendo las cosas bien. .
Una cosa que también debe saber es que mientras dibuje arcos relativos, no necesita repetir el comando a
para cada arco que dibuje. Cuando sus primeras 7 entradas hayan terminado para su arco, las segundas 7 entradas se tomarán para el siguiente arco.
Puede probar esto con el bolígrafo de arriba eliminando la segunda a
en la ruta:
a 25,25 0 1,1 50,0 25,25 0 1,1 -50,0
Esto puede verse igual, pero prefiero dejarlo hasta que esté listo para terminar un dibujo, y esto también me ayuda a hacer un seguimiento de dónde estoy.
Cómo funciona este camino
Primero, nos movemos a una coordenada X,Y
absolutamente posicionada en la imagen. No dibuja nada allí, simplemente se mueve allí. Recuerde que para un elemento circular CX
, CY
denota el centro del círculo; pero como sucede en el arco elíptico, los verdaderos CX
y CY
del arco se calcularán a partir de las otras propiedades de ese arco.
En otras palabras, si queremos que nuestro CX
esté en 50
y nuestro radio sea 25
, entonces debemos movernos a 50 - 25
(si estamos dibujando de izquierda a derecha, por supuesto). Esto significa que nuestro primer arco se dibuja a partir de 25 X, 50 Y
lo que da como resultado que nuestro primer arco sea 25,25 0 1,0 50,0
.
Analicemos qué significa realmente el valor 25,25 0 1,0 50,0
de nuestro arco:
-
25
: El radio X relativo del arco; -
25
: El radio Y relativo del arco; -
0 1,0
: No voy a hablar de los tres valores intermedios (rotación, bandera de arco grande y propiedades de bandera de barrido) porque no son muy importantes en el contexto del ejemplo actual siempre que son iguales para ambos arcos; -
50
: La coordenada X final (relativa) del arco; -
0
: La coordenada Y final (relativa) del arco.
El segundo arco es un 25,25 0 1,0 -50,0
. Tenga en cuenta que este arco comenzará a dibujarse desde donde dejó de dibujarse el último arco. Por supuesto, los radios X e Y son los mismos ( 25
), pero la coordenada X final es -50
de donde está la actual.
Obviamente, este círculo se podría haber dibujado de muchas maneras diferentes. Este proceso de convertir un círculo en un camino se conoce como descomposición. En la especificación SVG 2, la descomposición de un círculo se realizará con 4 arcos; sin embargo, el método que recomienda aún no es posible de usar, ya que actualmente depende de una característica llamada ruta de cierre de finalización de segmento que aún no se ha especificado.
Para mostrarte que podemos dibujar el círculo de muchas maneras, he preparado un pequeño bolígrafo con varios ejemplos:
Vea el Pen all circles de Bryan Rasmussen.
Si observa más de cerca, verá nuestro círculo original junto con cinco ejemplos diferentes de cómo dibujar caminos encima de ese círculo. Cada ruta tiene un elemento desc
secundario que describe el uso de los valores CX
, CY
y R
para construir el círculo. El primer ejemplo es el que discutimos aquí, mientras que otros tres usan variaciones que deberían ser comprensibles al leer el código; los últimos ejemplos utilizan cuatro arcos semicirculares en lugar de dos, replicando un poco el proceso descrito en la especificación SVG 2 vinculada anteriormente.
Los círculos se superponen uno encima del otro utilizando la indexación z natural de SVG para colocar los elementos que aparecen más adelante en el marcado encima de los que vienen antes.
Si hace clic en las rutas circulares en el lápiz, el primer clic imprimirá cómo está estructurada la ruta en la consola y agregará una clase al elemento para que vea el color del trazo de cómo se dibuja el círculo (puede ver que el primer círculo se dibuja con una cuña inicial desde el trazo). El segundo clic eliminará el círculo para que pueda interactuar con el círculo de abajo.
Cada círculo tiene un color de relleno diferente; el elemento del círculo real es amarillo y dirá "Hiciste clic en el círculo" en la consola cada vez que se haga clic en él. Por supuesto, también puede simplemente leer el código, ya que los elementos de desc
son bastante sencillos.
Pasar de un camino a un círculo
Supongo que te habrás dado cuenta de que, si bien hay muchas formas diferentes de dibujar el círculo, los caminos utilizados aún se ven bastante similares. A menudo, especialmente en la salida de SVG de un programa de dibujo, los círculos se representarán mediante rutas. Esto probablemente se deba a la optimización del código del programa de gráficos; una vez que tenga el código para dibujar una ruta, puede dibujar cualquier cosa, así que solo use eso. Esto puede conducir a SVG algo inflados sobre los que es difícil razonar.
Lectura recomendada : " Consejos para crear y exportar mejores archivos SVG para la web" por Sara Soueidan
Tomemos el siguiente SVG de Wikipedia como ejemplo. Cuando mire el código de ese archivo, verá que tiene mucho editor cruft una vez que lo haya ejecutado a través del SVGOMG de Jake Archibald. (sobre el que puede leer más aquí), terminará con algo como el siguiente archivo que ha sido bastante optimizado pero los círculos en el documento aún se representan como rutas:
Consulte el Pen Wikipedia Screw Head Clutch Type A de Bryan Rasmussen.
Entonces, veamos si podemos averiguar cómo deberían ser esos círculos si fueran elementos de círculo reales dado lo que sabemos sobre cómo funcionan las rutas. La primera ruta en el documento obviamente no es un círculo, mientras que las dos siguientes sí lo son (mostrando solo el atributo d
):
M39 20a19 19 0 1 1-38 0 19 19 0 1 1 38 0z
M25 20a5 5 0 1 1-10 0 5 5 0 1 1 10 0z
Entonces, recordando que la segunda a
puede omitirse, reescribámosla para que tenga un poco más de sentido. (El primer camino es el círculo grande.)
M39 20 a19 19 0 1 1-38 0 a19 19 0 1 1 38 0z
Esos arcos son entonces obviamente los siguientes:
aR R 0 1 1 - (R * 2) 0 aR R 0 1 1 (R * 2) 0
Esto significa que el radio de nuestro círculo es 19
, pero ¿cuáles son nuestros valores CX
y CY
? Creo que nuestro M39
es en realidad CX + R
, lo que significa que CX
es 20
y CY
también es 20
.
Digamos que agrega un círculo después de todos los caminos como este:
<circle fill="none" stroke-width="1.99975" stroke="red" r="19" cx="20" cy="20" />
Verá que es correcto y que el círculo rojo trazado cubre exactamente el círculo grande. La ruta del segundo círculo reformulada se ve así:
M25 20 a5 5 0 1 1-10 0 5 5 0 1 1 10 0z
Obviamente, el radio es 5
, y apuesto a que nuestros valores CX
y CY
son los mismos que antes: - 20
.
Nota : si CX = 20
, entonces CX + R = 25
. El círculo está ubicado dentro del más grande en el centro, por lo que obviamente debería tener los mismos valores de CX
y CY
.
Agregue el siguiente círculo al final de las rutas:
<circle fill="yellow" r="5" cx="20" cy="20" />
Ahora puede ver que esto es correcto echando un vistazo al siguiente bolígrafo:
Consulte el Pen Wikipedia Screw Head Clutch Type A_ con círculos de ejemplo de Bryan Rasmussen.
Ahora que sabemos cuáles deberían ser los círculos, podemos eliminar esas rutas innecesarias y crear los círculos, como puede ver aquí:
Consulte la Wikipedia de la pluma Embrague de cabeza de tornillo tipo A optimizado por Bryan Rasmussen.
Usando nuestra ruta circular para envolver texto
Entonces, ahora que tenemos nuestros círculos en las rutas, podemos ajustar el texto en esas rutas. A continuación se muestra un bolígrafo con las mismas rutas que nuestro bolígrafo "Todos los círculos" anterior, pero con texto envuelto en el camino. Cada vez que haga clic en una ruta, esa ruta se eliminará y el texto se ajustará a la siguiente ruta disponible, así:

Vea el texto envuelto en todos los círculos de Bryan Rasmussen.
Mirando las diferentes rutas, verá pequeñas diferencias entre cada una (más sobre eso en un momento), pero primero hay una pequeña incompatibilidad entre navegadores, especialmente notable en la primera ruta:
![]() | Desarrollador Firefox |
![]() | Cromo |
![]() | Borde de Microsoft |
La razón por la que la "S" inicial de "Smashing" se encuentra en ese ángulo divertido en la solución de Firefox es que es donde realmente comenzamos a dibujar nuestro camino (debido al comando vR que usamos). Esto es más obvio en la versión de Chrome, donde puedes ver claramente la primera cuña en forma de pastel de nuestro círculo que dibujamos:
![]() | Chrome no sigue todas las cuñas, por lo que este es el resultado cuando cambia el texto para que sea "Smashing Magazine". |
El motivo es que Chrome tiene un error relacionado con la herencia del atributo textLength
declarado en el elemento de text
principal. Si desea que ambos tengan el mismo aspecto, coloque el atributo textLength
en el elemento textPath
, así como en el texto. ¿Por qué? Porque resulta que Firefox Developer tiene el mismo error si el atributo textLength
no se especifica en el elemento de text
(este ha sido el caso durante algunos años).
Microsoft Edge tiene un error totalmente diferente; no puede manejar espacios en blanco entre el Text
y el elemento secundario TextPath
. Una vez que haya eliminado los espacios en blanco y haya colocado el atributo textLength
en los elementos text
y textPath
, todos se verán relativamente iguales (con pequeñas variaciones debido a las diferencias en las fuentes predeterminadas, etc.). Entonces, tres errores diferentes en tres navegadores diferentes: ¡es por eso que las personas a menudo prefieren trabajar con bibliotecas!
El siguiente bolígrafo muestra cómo se pueden solucionar los problemas:
Vea el Pen all circles Wrapped Text fixed TextLength de Bryan Rasmussen.
También eliminé los diversos colores de relleno porque hace que sea más fácil ver el ajuste del texto. Eliminar los colores de relleno significa que mi pequeña función que le permite recorrer las rutas y ver cómo se ven no funcionará a menos que agregue un atributo pointer-events="all"
, por lo que también las he agregado.
Nota : puede leer más sobre las razones de esto en "Administrar la interacción SVG con la propiedad de eventos de puntero" explicada por Tiffany B. Brown.
Ya hemos discutido el envoltorio de la ruta multiarco, así que ahora veamos los demás. Dado que tenemos un camino en el que estamos ajustando, el texto siempre se moverá en la misma dirección.
Imagen | Sendero | Explicación |
---|---|---|
![]() | M CX, CY a R, R 0 1,0 -(R * 2), 0 a R, R 0 1,0 R * 2, 0 y usa la función de translate para mover +R en el eje X. | La posición inicial de nuestro textPath (ya que no lo hemos especificado de ninguna manera) está determinada por nuestro primer arco final -(R * 2) , dado el radio que tiene el arco en sí. |
![]() | M (CX + R), CY a R,R 0 1,0 -(R * 2),0 a R,R 0 1,0 (R * 2),0 | Se aplica lo mismo que el camino anterior. |
![]() | M CX CY m -R, 0 a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 | Dado que estamos terminando en (R * 2 ) en nuestro primer arco, obviamente comenzaremos en la posición opuesta. En otras palabras, este comienza donde terminaron nuestros dos caminos anteriores. |
![]() | M (CX - R), CY a R,R 0 1, 1 (R * 2),0 a R,R 0 1, 1 -(R * 2),0 | Esto comienza en la misma posición que el último debido a (R * 2) , pero se ejecuta en el sentido de las agujas del reloj porque hemos establecido la propiedad de bandera de barrido (marcada en amarillo) en 1 . |
Hemos visto cómo envolver texto en un solo camino en un círculo. Ahora echemos un vistazo a cómo podemos dividir ese camino en dos caminos y los beneficios que puede obtener de eso.
Rompiendo nuestros caminos en partes
Hay muchas cosas que puede hacer con el texto en su ruta, es decir, lograr efectos estilísticos con elementos tspan
, configurar el desplazamiento del texto o animar el texto. Básicamente, cualquier cosa que hagas estará limitada por la ruta misma. Pero al dividir nuestras rutas multiarco en rutas de un solo arco, podemos jugar con la dirección de nuestro texto, la indexación z de diferentes partes de nuestro texto y lograr animaciones más complejas.
Primero, vamos a querer usar otra imagen SVG para mostrar algunos de los efectos. Usaré el diamante del artículo sobre eventos de puntero que mencioné anteriormente. Primero, mostremos cómo se verá con un texto circular de una sola ruta colocado encima.
Supongamos que nuestro círculo es CX 295, CY 200, R 175
. Ahora, siguiendo el método de ruta circular, ahora vemos lo siguiente:
M (CX - R), CY a R,R 0 1,1 (R * 2),0 a R,R 0 1,1 -(R * 2),0
Vea el Pen SVG Amethyst de Bryan Rasmussen.
No voy a hablar de la ruta o el tamaño del texto, color de relleno o trazo. Todos deberíamos entender eso a estas alturas y ser capaces de hacer que sea lo que queramos que sea. Pero al mirar el texto, podemos ver algunas desventajas o limitaciones de inmediato:
- Todo el texto corre en una dirección;
- Sería bueno tener parte del texto detrás de la amatista, especialmente donde dice REVISTA. Para hacer que la 'M' y la 'E' se alineen en el círculo, la 'A' tiene que estar en el punto lateral inferior de la amatista, que se siente un poco desequilibrada de otra manera. (Siento que la 'A' debería estar colocada con precisión y apuntando hacia abajo en ese punto).
Si queremos solucionar estos problemas, debemos dividir nuestra ruta única en dos. En el siguiente bolígrafo, he separado la ruta en dos rutas (y las defs
en el área de definiciones del SVG para que nuestra textPath
de texto haga referencia):
Vea el Pen SVG Amethyst two paths de Bryan Rasmussen.
Nuevamente, asumiendo que nuestro CX
es 295, CY 200, R 175
, entonces las dos rutas tienen el siguiente formato (para la ruta semicircular superior):
M (CX - R), CY a R,R 0 1,1 (R * 2),0
Y lo siguiente para la parte de abajo:
M (CX + R), CY a R,R 0 1,1 -(R * 2),0
Sin embargo, todavía tenemos texto circular que se mueve todo en la misma dirección. Para arreglar eso para todo excepto Edge, todo lo que tiene que hacer es agregar el atributo side="right"
al elemento de text
que contiene el textPath
'MAGAZINE'.
Hacer que el texto vaya en otra dirección
Si queremos admitir tantos navegadores como podamos, debemos modificar la ruta y no confiar en el atributo side
que no es totalmente compatible. Lo que podemos hacer es copiar nuestra ruta de semicírculo superior, pero cambiar el barrido de 1
a 0
:
Antes:
M 120, 200 a 175,175 0 1,
M 120, 200 a 175,175 0 1,
1
350,0
350,0
Después:
M 120, 200 a 175,175 0 1,
M 120, 200 a 175,175 0 1,
0
350,0
350,0
Pero nuestro texto ahora se dibuja en el círculo interior definido por el barrido y no se verá tan bien en diferentes navegadores. Esto significa que vamos a tener que mover la posición de nuestra ruta para alinearla con la 'S' de 'Smashing', hacer que la X final de la ruta sea más grande y establecer un desplazamiento en el texto. Como puede ver, también hay una pequeña diferencia de texto entre Firefox y los demás que podemos mejorar aumentando el atributo textLength
en el elemento de text
, así como eliminando los espacios en blanco de textPath
(ya que Firefox evidentemente piensa que los espacios en blanco son significativos).
La solución:
Vea los dos caminos Pen SVG Amethyst arreglados por Bryan Rasmussen.
Cambiar el índice Z de parte de nuestro texto circular
Finalmente, queremos que nuestro texto vaya tanto por delante como por detrás de la amatista. Bueno, eso es fácil. ¿Recuerda que la indexación z de elementos de SVG se basa en el lugar en el que se encuentran en el marcado? Entonces, si tenemos dos elementos, el elemento 1
se dibujará detrás del elemento 2
. A continuación, todo lo que tenemos que hacer es mover un elemento de text
hacia arriba en nuestro marcado SVG para que se dibuje antes que la amatista.
Puede ver el resultado a continuación en el que partes de la palabra 'REVISTA' están ocultas por el punto inferior de la amatista.
Vea el Pen SVG Amethyst two paths z-index de Bryan Rasmussen.
Si observa el marcado, puede ver que el semicírculo inferior del texto se ha movido para estar antes de la ruta que dibuja la amatista.
Animando las partes de nuestro círculo
Así que ahora tenemos la capacidad de hacer un texto circular al controlar completamente la direccionalidad de las partes de nuestro texto al colocar el texto en dos semicírculos. Esto, por supuesto, también se puede aprovechar para hacer animaciones del texto. Hacer animaciones SVG entre navegadores es realmente el tema de otro artículo (o muchos más artículos). Estos ejemplos solo funcionarán en Chrome y Firefox debido al uso de la sintaxis de animaciones SMIL en lugar de fotogramas clave CSS o herramientas como Greensock. Pero da un buen indicador de los efectos que puede lograr al animar el círculo descompuesto.
Toma el siguiente bolígrafo:
Vea el Pen SVG Amethyst dos caminos animados por Bryan Rasmussen.
Presione el botón 'Reejecutar' en el codepen para ver la animación en acción. Las dos partes de nuestro texto circular comienzan a animarse al mismo tiempo, pero tienen una duración diferente, por lo que terminan en momentos diferentes. Debido a que estamos animando el atributo textLength
, hemos colocado dos directivas animate
debajo de cada texto: una para el elemento de text
(para que Firefox funcione) y otra para el elemento de textpath
de texto (para que Chrome funcione).
Conclusión
En este artículo, hemos visto cómo convertir un círculo en una ruta y viceversa, para comprender mejor cuándo necesitamos optimizar una ruta y cuándo no. Hemos visto cómo convertir el círculo en una ruta nos libera para colocar el texto en la ruta circular, pero también cómo dividir aún más la ruta circular en semicírculos y obtener un control más completo sobre la direccionalidad y la animación de las partes componentes de nuestro texto circular. .
Lectura adicional en SmashingMag:
- Repensar el SVG receptivo
- Animación de archivos SVG con SVGator
- Estilo y animación de SVG con CSS
- Gestión de la interacción SVG con la propiedad Pointer Events