¿Cómo normalizar los datos de ruta SVG (browser cruzado)?

He intentado encontrar una forma de implementar el normalizador de ruta cruzada. Hay una forma nativa que se describe aquí y el ejemplo funcional está aquí , pero funciona solo en la nueva Opera (pero no en IE, FF, Safari, Chrome).

La forma nativa usa pathElm.normalizedPathSegList y convierte todas las coorderadas relativas en absolutas y representa todos los types de segmentos de ruta como un subset de types siguientes: M, L, C, z.

Solo he encontrado un código de JavaScript y un ejemplo funcional de jsfiddled , pero solo funciona en IE y FF. Chrome muestra "Error no detectado: INDEX_SIZE_ERR: DOM Exception 1". ¿Cómo se puede arreglar esto para que funcione también en Opera, Safari y Chrome o existe alguna otra forma de normalizar las routes SVG?

EDITAR: ¡No use esto! Lo probé más y me di count de que la conversión A-> C no es confiable en todos los casos y también fallan algunas otras combinaciones de commands de ruta. Por favor use esto en su lugar!

Finalmente lo hizo funcionar en Safari, Opera, IE9, Firefox y Chrome: http://jsfiddle.net/timo2012/M6Bhh/41/

La function normaliza los datos de la ruta SVG, de modo que todos los segmentos de ruta se convierten en M, C, L yz (= coorderadas absolutas, lo que significa que todas las coorderadas relativas se convierten en absolutas). Todos los otros segmentos son triviales y 100% precisos, pero el arco (A) es un caso especial y puede seleccionar si los arcos se convierten a líneas (L), curvas cuadráticas (Q) o curvas cúbicas (C). Las líneas más precisas son las líneas, pero luego perdemos independencia de resolución. Por alguna razón, los cuadráticos fallan en ciertos arcos, pero los cúbicos son más precisos.

Si tenemos la siguiente ruta:

 <svg width="400" height="400"> <path stroke="networking" stroke-width="3" d="M30 30 S40 23 23 42 L23,42 C113.333,113.333 136.667,113.333 150,80 t40,50 T230,240 q20 20 54 20 s40 23 23 42 t20,30 a20,30 0,0,1 -50,-50"/> </svg> 

y normalizarlo usando:

 var path = document.querySelector('path'); path.normalizePath(3, 0.1); // 3 = C = cubic curves. Best alternative, rather good accuracy and path data remains reasonable sized 

la versión normalizada es esta:

 <svg width="400" height="400"> <path stroke="networking" stroke-width="3" d="M 30 30 C 30 30 40 23 23 42 L 23 42 C 113.333 113.333 136.667 113.333 150 80 C 150 80 163.333 96.6667 190 130 C 216.667 163.333 230 200 230 240 C 243.333 253.333 261.333 260 284 260 C 284 260 324 283 307 302 C 307 302 313.667 312 327 332 C 324.811 336.924 321.997 341.154 318.719 344.448 C 315.441 347.741 311.762 350.033 307.893 351.194 C 304.024 352.355 300.04 352.361 296.169 351.213 C 292.298 350.064 288.616 347.783 285.333 344.5 C 282.05 341.217 279.23 336.996 277.035 332.078 C 274.839 327.161 273.311 321.642 272.537 315.839 C 271.763 310.035 271.759 304.06 272.525 298.254 C 273.291 292.448 274.811 286.924 277 282"/> </svg> 

Si distribuimos ambos uno encima del otro, el resultado es el siguiente (el rojo está normalizado y el negro es original):

Camino normalizado y original

Otras posibilidades son estas:

 path.normalizePath(1,0.5); // A->L, Many lines, high accuracy. Very good accuracy, but not so resolution independent, because when scaled, the corners become visible path.normalizePath(1,40); // A->L, Few lines, less accuracy path.normalizePath(2,0.5); // A->Q, quadratic curves. I tested this, but not good. Fails in some cases. 

¿Y cuáles son los beneficios de esto?

La forma nativa de normalizar los datos de ruta aún no está implementada en todos los browseres, por lo que estamos solos hasta ahora. Y cuando se implementa la forma nativa, no estamos seguros de que todos los browseres lo hagan de la misma manera. La documentation de SVG habla de convertir arcos en líneas, pero esta no es una buena manera, ya que se perderá la principal ventaja de SVG: independencia de resolución. Deberíamos tener un control total sobre cómo se realiza la normalización de los arcos, y esta secuencia de commands proporciona una forma de hacerlo.

Cuando los datos se normalizan, se pueden modificar exactamente de la misma manera que las coorderadas en imágenes de bitmap. Si queremos deformar (arco, arco, protuberancia, caparazón, bandera, onda, pez, upload, ojo de pez, inflar, estrujar, torcer) de manera ilustrativa o distorsionar paths para lograr ilusión de perspectiva, los datos de ruta normalizados pueden modificarse seguramente.

El código está basado en el guión de YannickBochatay y lo hice más cross browser.

Creo que encontré el motivo de las excepciones DOM en Opera, Safari y Chrome.

El documento SVG dice que getItem() devuelve el elemento especificado de la list. El artículo devuelto es el artículo en sí y no una copy. Cualquier cambio realizado en el elemento se refleja inmediatamente en la list.

Y de manera similar, appendItem() un nuevo elemento al final de la list. Si newItem ya está en una list, se elimina de su list anterior antes de que se inserte en esta list. El ítem insertado es el ítem mismo y no una copy.

Así que esto se refiere al artículo original:

 seg = path1.pathSegList.getItem(i); 

Y cuando este elemento se agrega a la list de segmentos de otra ruta usando

 newpath.pathSegList.appendItem(seg); 

cada browser tiene su propia opinión de qué hacer a seg original. IE9 y FF dejan la list de segmentos de path1 original intacta (o al less conserva índices (i en el ejemplo anterior)) y Safari, Chrome y Opera eliminan seg de la list de segmentos de path1. La documentation de SVG explica claramente que el elemento ( se elimina de su list anterior ), por lo que IE9 y FF parecen tener una implementación incorrecta. Por lo tanto, no estoy (todavía) muy seguro de si el elemento se eliminó de la list anterior o si solo se conserva la indexing (lo verificaré un poco más adelante).

EDITAR: verifique esto y confirme que IE9 y FF dejan la list de segmentos de la ruta original (ruta1) intacta, cuando sus segmentos se anexan a la list de segmentos de otra ruta, por lo que también se conservan los índices. El comportamiento en Safari, Chrome y Opera es diferente: el elemento se elimina de la list original cuando se agrega a otra list, y hasta ahora, por supuesto, la indexing también se actualiza (los índices antiguos ya no son válidos después de agregarlos). Hecho jsfiddle que confirma la diferencia. IE9 y FF devuelven longitudes de list de segmentos 1,1,10,10. Opera, Safari, Chrome devuelve 1,0,10,8.

Y una vez más tenemos que tener en count las diferencias del browser de alguna manera, por ejemplo. agregando seg simulado antes del primer elemento o consultando path1.numberOfItems () para determinar si la ruta original está modificada o no.

EDITAR: He reparado el error de Raphaël y realicé testings exhaustivas con routes complejas animadas y no animadas, así que creo que es aconsejable usar Raphaël para la normalización de routes. La explicación del error y su solución está aquí: https://stackoverflow.com/a/13079377/1691517 . La function path2curve de Raphaël puede convertir fácilmente todos los commands de ruta (también A ie Arc) a la forma normalizada (es decir, curvas cúbicas). ¡Es bueno que los Cubics puedan representar todos los commands de ruta!


La otra forma es usar el nuevo Raphaël, donde hay una function interesante Raphael.path2curve() , que convierte todos los commands de ruta en curvas cúbicas, pero tiene algún error. La siguiente image visualiza el error:

enter image description here

El ejemplo funcional está aquí y el código es el siguiente:

 <style>path {fill:none}</style> <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script> <div id="res" style="width:800px"></div> <div id="raphael"></div> <script> window.onload = function () { var paper = Raphael("raphael", 400, 400); var original_path = "M30 30 S40 23 23 42 L23,42 C113.333,113.333 136.667,113.333 150,80 t40,50 T230,240 q20 20 54 20 s40 23 23 42 t20,30 a20,30 0,0,1 -50,-50"; var arr=Raphael.path2curve(original_path); var normalized_path = arr.toString(); var path1 = paper.path(normalized_path).attr({stroke: "networking", "stroke-width":6}); var path2 = paper.path(original_path).attr({stroke: "black", "stroke-width":2}); document.getElementById("res").innerHTML="ORIGINAL PATH (black):<br>"+original_path+"<br><br>NORMALIZED PATH (networking):<br>"+normalized_path; } </script>​ 

Sería muy bueno poder hacer una normalización de ruta en Raphaël, porque admite una gran cantidad de browseres y utiliza matrices en lugar de segmentos de ruta DOM (= velocidad y compatibilidad con versiones anteriores). Hice un informe de error . Espero que se solucione en alguna versión futura.