动态添加SVG渐变

时间:2012-06-05 08:59:43

标签: javascript jquery svg

我有这个带路径的SVG容器。我想编辑它,所以路径的填充将是一个模式。这是我失败的尝试:

我添加渐变:

$('svg defs').prepend('<linearGradient id="MyGradient"><stop offset="5%" stop-color="#F60" /><stop offset="95%" stop-color="#FF6" /></linearGradient>');

然后更改路径的填充:

$(base + ' svg path').each(function() {
    this.setAttribute('fill','url(#MyGradient)')
}

这不起作用。我错过了什么?

4 个答案:

答案 0 :(得分:21)

你的问题(你“缺少”)是jQuery在XHTML命名空间中创建新元素,而SVG元素必须在SVG命名空间中创建。您不能在SVG元素的字符串中使用原始代码。

最简单的(无插件)方法是停止依赖jQuery这么多,只需使用简单的DOM方法来创建元素。是的,它比仅仅使用jQuery为你神奇地构建元素更加冗长......但是在这种情况下jQuery不起作用。

演示:http://jsfiddle.net/nra29/2/

createGradient($('svg')[0],'MyGradient',[
  {offset:'5%', 'stop-color':'#f60'},
  {offset:'95%','stop-color':'#ff6'}
]);
$('svg path').attr('fill','url(#MyGradient)');

// svg:   the owning <svg> element
// id:    an id="..." attribute for the gradient
// stops: an array of objects with <stop> attributes
function createGradient(svg,id,stops){
  var svgNS = svg.namespaceURI;
  var grad  = document.createElementNS(svgNS,'linearGradient');
  grad.setAttribute('id',id);
  for (var i=0;i<stops.length;i++){
    var attrs = stops[i];
    var stop = document.createElementNS(svgNS,'stop');
    for (var attr in attrs){
      if (attrs.hasOwnProperty(attr)) stop.setAttribute(attr,attrs[attr]);
    }
    grad.appendChild(stop);
  }

  var defs = svg.querySelector('defs') ||
      svg.insertBefore( document.createElementNS(svgNS,'defs'), svg.firstChild);
  return defs.appendChild(grad);
}

使用库

或者,您可以包含Keith Woods' "jQuery SVG" plugin,其中包含许多常用SVG操作的便捷方法,包括create linear gradients的功能。

答案 1 :(得分:3)

我认为你必须使用jQuery的SVG插件(找到here)。使用“普通”jQuery库添加SVG元素时,名称空间可能会混淆。

尝试以下方法:

svg.linearGradient( $('svg defs'), 
                    'MyGradient', 
                    [ ['5%', '#F60'], ['95%', '#FF6']] );

(但不完全确定。您可能需要对该代码进行一些调整。)

修改

刚刚创建了这个fiddle以测试论文(正如@Phrogz所建议的那样)。实际上,它返回http://www.w3.org/1999/xhtml作为插入的<linearGradient>的命名空间,这是错误的命名空间,从而验证了我的上述推测。

答案 2 :(得分:3)

找到解决方案。 它有点难看,但不需要使用额外的插件。

显然,首次创建SVG时,必须在标签中包含一个模式(可能只读取它)。

因此,用自己替换SVG标记的包装器内容是有效的(base是该包装器):

$(base).html($(base).html())

答案 3 :(得分:3)

我只想说,我已经找到了一个更优雅的解决方案,它允许你继续使用jQuery与SVG元素,但没有jQuery SVG库(不再更新,并且jQuery 1.8或更高版本有一些问题)。 只需使用这样的函数:

createSVGElement= function(element) {
    return $(document.createElementNS('http://www.w3.org/2000/svg', element));
}

它在SVG命名空间上创建一个SVG元素,并用jQuery封装它,一旦在正确的命名空间中创建了元素,就可以在jQuery中自由使用它:

然后您可以这种方式使用该功能:

var $myGradient= createSVGElement('linearGradient')
    .attr( {
        id:"MyGradient"
    });

//if you dont need `defs`, skip this next line
var $myDefs = createSVGElement('defs');

createSVGElement('stop')
    .attr({
        offset: "5%",
        "stop-color": "#F60"
    })
    .appendTo($myGradient);


createSVGElement('stop')
    .attr({
        offset:"95%",
        "stop-color":"#FF6"
    })
    .appendTo($myGradient);

//Use this if you already have `defs`
$('svg defs').prepend($myGradient);

//Use this if you dont have `defs`
$('svg').prepend($myDefs);
$('svg defs').prepend($myGradient);

它不像你想要的那样紧凑,因为你必须手工创建每个元素,但它比使用DOM方法操作所有元素要好得多。

小注释,jQuery .attr()函数假定所有属性都是小写的,而SVG元素则不是这种情况(例如viewBox标记中的<svg>属性)。为了解决这个问题,当使用大写字母设置属性时,请使用以下内容:

$("svg")[0].setAttribute("viewBox", "0 0 1000 1000");