¿Es posible rellenar con degradado lineal una ruta agrupada en SVG (por css o attr en el evento jQuery)?

8 minutos de lectura

Avatar de usuario de Répás
Repás

¿Cómo puedo llenar un gradiente para un <g> en una imagen SVG en lugar de llenar todos los <g>s en el seleccionado <g>?

En este caso, me gustaría mostrar África, rellena con un solo degradado de amarillo a rojo, pero debido a los subgrupos, el relleno tiene muchos degradados.

El JavaScript:

<script type="text/javascript">
function svgOver() { 
    var what = $(this).attr("id");
    $("#world #"+what, svg.root()).attr("fill", "url(#red_black)"); 
} 
function svgOut() { 
    $(this).attr("fill", "");
}

...

$("#map").svg({ 
    loadURL: 'http://teszt.privilegetours.hu/skins/privilege/svg/worldmap.svg',
        onLoad: function(svg) { 
        $("#world > g", svg.root()).bind('mouseover', svgOver).bind('mouseout', svgOut).bind('click', svgZoom);
        },
    settings: {}
});

El SVG:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" mlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="570px" height="300px" viewBox="146.605 71.42 570 300" enable-background="new 146.605 71.42 570 300" xml:space="preserve">

<defs>
    <linearGradient id="red_black" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1"/>
        <stop offset="100%" style="stop-color:rgb(255,255,0);stop-opacity:1"/>
    </linearGradient>
</defs>

<g id="world" transform="scale(1)" fill="#AAAAAA" stroke="#FFFFFF" stroke-width="0.1">
    <g id="africa" name="africa"> // < i want to fill this
        <g id="er" transform="translate(-29.9017, -45.0745)"> // < instead of theese
            <path d="..."/>
        </g>
        <g id="yt"> // < instead of theese
            <path d="..."/>
        </g> 
        ...

esto es áfrica

¿Como puedo solucionar este problema?
¿Cómo puedo solucionar este problema sin agregar otro? <g> etiqueta a la imagen original?

  • ¿Puedes vincular a un ejemplo en línea? Creo que el problema puede estar en su CSS en lugar de su SVG, solo quiero ver todo.

    – roberto

    29 de marzo de 2011 a las 14:09

  • Entonces, ¿no puede vincular el ejemplo en línea? ¿Dónde está el código al que está aplicando el estilo? #africa?

    – roberto

    30 de marzo de 2011 a las 10:04

  • $("#world #"+what, svg.root()).attr("fill", "url(#red_black)"); 1ra caja línea 4

    – Repás

    30 de marzo de 2011 a las 11:33

  • Pero solo estás haciendo eso al pasar el mouse: $("#world > g", svg.root()).bind('mouseover', svgOver). ¿Realmente no puedes enlazar todo en línea?

    – roberto

    30 de marzo de 2011 a las 13:37

  • Bueno, como puedes ver, cuando $("#world > g", svg.root()).bind('mouseover', svgOver) une el mouseover evento pasa el id del elemento al svgOver() función. Entonces el svgOver será igual a $("#world #africa", svg.root()).attr("fill", "url(#red_black)");. El url(#red_black) será elegido del svg, (<defs>). En realidad no tengo permiso para compartir todo el sitio. Haré una copia de esto más tarde y lo compartiré.

    – Repás

    30 de marzo de 2011 a las 13:46


avatar de usuario de colapsar
colapsar

su problema se puede resolver configurando el sistema de coordenadas de gradientes en el espacio del usuario (en lugar del cuadro delimitador de objetos predeterminado).

podrías intentar

<defs>
    <linearGradient id="red_black" x1="0%" y1="0%" x2="0%" y2="100%" gradientUnits="userSpaceOnUse">
        <stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1"/>
        <stop offset="100%" style="stop-color:rgb(255,255,0);stop-opacity:1"/>
    </linearGradient>
</defs>

la solución no desafía el comentario de e.nelson: lo que sucede aquí es que cada subgrupo que representa a las naciones todavía tiene su instancia de gradiente individual aplicada, mientras que todas estas instancias comparten el mismo origen de coordenadas y las mismas transformaciones en el espacio del usuario, por lo que en cualquier punto en el renderizado final, no importa qué instancia de degradado sea visible.

se requieren dos ajustes:

  1. [ minor]

    tiene que ajustar las compensaciones y1/y2 (o las compensaciones de las paradas) de la definición del degradado; dado que se refieren al espacio de coordenadas del usuario de todo el mapa, África solo cubre una parte del degradado entre las paradas definidas. intentar y1="50%" y y2="100%".

  2. [medium]

    si echa un vistazo a los elementos svg g que definen las formas de los países, notará que algunos de ellos están sujetos a una traducción adicional. cambian efectivamente el sistema de coordenadas del usuario y, por lo tanto, también se aplican al gradiente que hace que las formas de los países afectados aparezcan como manchas en el mapa. esta transformación de espurios es probablemente un artefacto de las acciones en el generador utilizado para crear el mapa. se puede remediar agregando las compensaciones de traducción a cada absoluto coordenada en los elementos de ruta dentro de los respectivos elementos g. como estas rutas se definen usando coordenadas relativas para las piezas unidas, esto se reduce a alterar las coordenadas de los comandos ‘M’ inicial y ‘C’ final en el atributo d de la ruta.

he preparado un script perl ad hoc para normalizar la estructura del código svg que representa las fronteras del país que implementa las modificaciones mencionadas anteriormente. tenga en cuenta que estas modificaciones también se pueden hacer de manera conveniente en js. Este es el resultado.

espero que ayude y envíeme una nota si necesita información adicional sobre cómo realizar los ajustes mencionados.

PD: acabo de darme cuenta de que todavía falta mozambique en la salida generada; para la forma de ese único país, se ha especificado otra traducción. este detalle menor es algo que se añadirá más tarde hoy, sin embargo…

resultado

  • Bueno, estoy totalmente asombrado. trabajando en ello. ¿Tiene algún programa que pueda usar para hacer una versión de mi mapa sin M y sin traducir? Gracias, Repás

    – Repás

    4 de abril de 2011 a las 8:22

  • tengo un script perl. pd: hoy tengo una agenda apretada, así que probablemente no estaré en stackoverflow hasta mañana. cu, carsten

    – colapsar

    4 de abril de 2011 a las 10:02


  • Felicitaciones y +1, gran solución.

    – Elliot Nelson

    6 de abril de 2011 a las 1:01

  • @repas: perdón por la latencia prolongada. Estuve mucho más ocupado la semana pasada de lo que esperaba… la URL del script es collapsar.co.ohost.de/data/astcko.03.cleanse.pl; tendrá que ajustar las rutas a su sistema de archivos local en las líneas 64ff. $fclone es el original, $fres el nombre del archivo de destino, $workdir … bueno … 😉 (nombres de identificación pintorescos por razones históricas …). Saludos, Carsten

    – colapsar

    12 de abril de 2011 a las 22:16

  • @collapsar ohai. los enlaces están muertos.

    – GottZ

    8 de diciembre de 2015 a las 13:21

“La pintura, sin embargo, siempre se realiza en cada elemento gráfico individualmente, nunca en el nivel del elemento contenedor (por ejemplo, una ‘g’). Por lo tanto, para el siguiente SVG, aunque el relleno degradado se especifica en la ‘g’, el el degradado simplemente se hereda a través del elemento ‘g’ hacia abajo en cada rectángulo, cada uno de los cuales se representa de tal manera que su interior se pinta con el degradado”.

http://www.w3.org/TR/SVGTiny12/painting.html#InheritanceOfPaintingProperties

Lo que estás pidiendo no es posible, según la especificación. Si es un requisito, puede explorar uno de los siguientes: hacer que el creador de SVG agregue el mouse sobre las rutas para usted; combine las rutas en código en el servidor (potencialmente complicado); elige un color sólido en lugar de un degradado para que el problema no sea tan obvio.

Si desea llenar toda África con un gradiente, entonces desea unir los caminos para ese relleno. ¿Tal vez deberías usar un mapa diferente? ¿Uno con sólo los continentes?

De todos modos, una forma de solucionarlo sería:

  1. abrirlo en Inkscape
  2. seleccione todos los caminos que desea llenar
  3. elija “Unión” en el menú “Ruta”
  4. guarde el archivo (o copie y pegue la ruta unida)

De otra manera:

  1. Busque otro mapa, vea http://d-maps.com/ o http://commons.wikimedia.org. Aquí hay uno con el solo continentes, áfrica marcada.

Después de hacer eso, puede aplicar el degradado a esa nueva ruta.

También podría hacerlo de otras formas, pero probablemente no sean tan buenas por motivos de rendimiento. Una de esas formas (no recomendadas) sería llenar un rectángulo con el degradado en el que ha creado un trazado de recorte que consta de los trazados del grupo. Algo a lo largo de estas líneas:

<clipPath id="clip">
  <use xlink:href="#africa"/>
</clipPath>
<rect width="100" height="100" fill="url(#grad)" clip-path="url(#clip)"/>
<g id="africa">...</g>

  • Este mapa tiene otros javascripts… 😀 Mover, seleccionar, etc. Así que no puedo elegir de otra manera, primero tampoco, porque. Ahora tenía una idea con un camino de unión invisible. 🙂 Me verás más tarde.

    – Repás

    30 de marzo de 2011 a las 20:02

  • Tenga en cuenta que los grupos svg en clip-paths no tienen ningún efecto. clip-paths sólo puede contener formas básicas, text o path elementos o use-referencias que apuntan directamente a tales.

    – Simón

    5 de julio de 2021 a las 14:11

creo que tu problema puede ser ese fill se hereda de acuerdo con la reglas estándar de CSS en SVG. Así que necesitaría establecer un explícito fill de transparente en el niño g elementos. Si no es así, volveré y echaré otro vistazo después de que tengas un ejemplo en línea.

¿Ha sido útil esta solución?