Leer una d a ojo
El método para descifrar una cadena de path minificada sin herramientas, los indicios que delatan el origen del fichero, y las APIs del navegador que te dan la geometría cuando la cadena no basta.
Un path real que llega de una herramienta de diseño es una cadena de doscientos caracteres sin espacios que a primera vista no dice nada. Pero dice bastante: el rango de los números revela la escala, la mezcla de comandos revela quién lo generó, y la estructura de subrutas revela qué piezas tiene la forma. Leerlo a ojo es una habilidad de depuración concreta, y cuando la vista no llega hay tres APIs del navegador que devuelven la geometría de verdad.
- Descomponer mentalmente una
dminificada en subrutas y comandos. - Deducir el origen probable de un fichero a partir de los comandos que usa.
- Detectar los cuatro fallos que se ven sin abrir ninguna herramienta.
- Usar
getTotalLength,getPointAtLengtheisPointInFillpara responder preguntas que la cadena no responde.
El método de lectura
Ante una d desconocida, cuatro pasadas, en este orden.
Pasada uno: cuenta las M y las Z. Cada M es una pieza. Un icono con tres M tiene tres piezas, y si tiene tres Z están las tres cerradas. Un desajuste entre el número de M y el de Z significa que hay al menos una subruta abierta, lo que en un icono relleno es casi siempre un error de exportación.
Pasada dos: mira el rango de los números. Si todos están entre 0 y 24, la forma está pensada para un viewBox de 24. Si hay valores de tres cifras y el viewBox dice 24, alguien ha pegado un icono de otra escala y falta un transform. Si hay números negativos grandes al principio, la forma sale fuera del lienzo. Esto explica el noventa por ciento de los «mi icono no se ve».
Pasada tres: identifica el alfabeto. ¿Solo M, L, H, V, Z? Geometría ortogonal, probablemente un diagrama o un icono de líneas rectas. ¿Muchas C y S? Una forma orgánica dibujada con la pluma. ¿Muchas Q y T? Casi seguro viene de una tipografía TrueType. ¿Aparece A? Hay arcos de verdad, típico de gráficos generados por código o de esquinas redondeadas escritas a mano.
Pasada cuatro: mira el primer punto y el último de cada subruta. Si coinciden y hay Z, la pieza está cerrada dos veces (redundante, no dañino). Si coinciden y no hay Z, tienes el bug del cierre falso del que hablamos en los comandos rectos.
Un ejemplo real, formateado para poder mirarlo:
<path d="M12 2 8.5 8.5 2 12l6.5 3.5L12 22l3.5-6.5L22 12l-6.5-3.5z"/>
Una sola M, una sola z. Números entre 2 y 22, así que el viewBox es de 24. Solo M, l y z, más las repeticiones implícitas del M inicial que funcionan como L. Y ocho vértices alternando cerca y lejos del centro (12, 12). Es una estrella de cuatro puntas. No hace falta abrir nada.
Firmas de origen
Cada herramienta deja huellas reconocibles en la d, y saber leerlas te dice qué esperar del resto del fichero.
| Indicio | Origen probable | Qué implica |
|---|---|---|
| Todo relativo, sin espacios, tres decimales | Pasado por un optimizador | El fichero ya está minificado; no lo vuelvas a optimizar a ciegas |
| Absoluto, con espacios, muchos decimales | Exportación directa de Figma | Habrá fill explícito en cada ruta y quizá un clipPath |
Solo C, con coordenadas de dos decimales |
Illustrator | Espera <style> con clases st0, st1 y un prólogo XML |
Predominio de Q y T |
Contorno de una tipografía | Los agujeros dependerán del sentido de giro, ojo con fill-rule |
A con radios idénticos y banderas 1 0 |
Círculo generado por código | Casi seguro se podría sustituir por <circle> |
Números con exponente (1e-5) |
Cálculo en coma flotante sin redondear | Hay ruido numérico; conviene redondear a tres decimales |
Ninguna de estas pistas es definitiva, pero juntas orientan la búsqueda. Si ves clases st0 sabes que el fichero traerá un <style> global que va a colisionar con el de los demás iconos que inlines, y eso lo tratamos en qué produce Figma e Illustrator.
Los cuatro fallos que se ven sin herramientas
Uno: fill sin declarar en una ruta abierta. Una d sin Z que en el elemento no lleva fill="none" se rellena de negro. Si el icono es una línea y sale como una mancha, es esto.
Dos: escala equivocada. Números fuera del rango del viewBox. Se arregla con el viewBox correcto, no con un transform="scale()" a ojo; el transform escala también el stroke-width y estropea el grosor.
Tres: fill-rule incorrecta. Dos subrutas concéntricas y la forma sale maciza. Prueba evenodd antes de tocar la geometría.
Cuatro: el primer comando no es M. Una d que empieza por L o por C es inválida y el elemento no se dibuja. Ocurre cuando alguien recorta una cadena por la mitad para dividir una forma.
Hay un quinto que no se ve pero se nota: decimales excesivos. Una d con seis o siete decimales por número no es más precisa a efectos prácticos y triplica el peso. En un viewBox de 24 unidades renderizado a 24 píxeles, el tercer decimal ya está por debajo de la milésima de píxel.
Cuando la vista no llega
Tres métodos del DOM responden preguntas geométricas sin que tengas que parsear nada. Viven en SVGGeometryElement, así que funcionan sobre path, rect, circle, ellipse, line, polyline y polygon.
const p = document.querySelector('#forma');
// Cuanto mide el perimetro dibujado, en unidades de usuario
const L = p.getTotalLength();
// Que punto hay al 37 por ciento del recorrido
const { x, y } = p.getPointAtLength(0.37 * L);
// Cae este punto dentro del relleno? Y sobre el trazo?
const punto = new DOMPoint(12, 12);
p.isPointInFill(punto);
p.isPointInStroke(punto);
getTotalLength() es la herramienta para el patrón de dibujo progresivo: pones stroke-dasharray y stroke-dashoffset a la longitud total y animas el desfase hasta cero. getPointAtLength() sirve para colocar objetos a lo largo de una trayectoria a velocidad constante, y también para el muestreo que permite comparar dos formas sin analizar sus comandos.
isPointInFill() e isPointInStroke() son la puerta al hit testing manual. Sirven para preguntar si el cursor está dentro de una forma sin depender de los eventos del DOM, que es justo lo que necesitas cuando dibujas la forma en un lienzo y solo mantienes el path como modelo.
Una precisión importante sobre getTotalLength(): la especificación no obliga a un método exacto. Los motores muestrean la curva y suman segmentos, así que el valor tiene un error de aproximación y puede diferir ligeramente entre navegadores. Para animación es irrelevante. Para una medida que vayas a mostrar al usuario, no uses este método: calcula la longitud tú con la precisión que necesites.
El atributo pathLength declara cuánto mide la ruta a efectos de todas las operaciones de longitud de trazo. No cambia la geometría ni el rasterizado: cambia la escala en la que se interpretan stroke-dasharray, stroke-dashoffset y los startOffset de un textPath.
Con pathLength="100", un stroke-dashoffset="30" significa exactamente el 30 por ciento, independientemente de que la ruta mida 314,159 unidades o 1.284,7. Eso resuelve tres problemas de golpe: la animación de dibujo progresivo se escribe sin JavaScript, el anillo de progreso no necesita recalcular nada al cambiar el radio, y el mismo componente sirve para cualquier forma.
El detalle que casi nadie sabe: getTotalLength() sigue devolviendo la longitud real, no la declarada. Las dos escalas coexisten y no se contaminan. Si mezclas las dos en el mismo componente (declaras pathLength y además lees getTotalLength() para calcular el desfase), obtienes un progreso que va tres veces más rápido de lo que debería y no se entiende por qué.
Coge tres iconos de tres orígenes distintos de tu proyecto y clasifícalos con la tabla de firmas sin abrir la herramienta que los generó. Después comprueba tu diagnóstico mirando el fichero completo: ¿acertaste el origen? Para el que tenga más subrutas, cuenta las M y las Z y comprueba con isPointInFill() si el interior de cada pieza está donde tú creías.