简体   繁体   English

动态添加SVG渐变

[英]Dynamically adding a SVG gradient

I have this SVG container with paths. 我有这个带路径的SVG容器。 I want to edit it, so the paths' fill will be a pattern. 我想编辑它,所以路径的填充将是一个模式。 This is my failed attempt: 这是我失败的尝试:

I add a gradient: 我添加了一个渐变:

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

And then change the paths' fill: 然后更改路径的填充:

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

This doesn't work. 这不起作用。 What am I missing? 我错过了什么?

Your problem (what you are "missing") is that jQuery creates new elements in the XHTML namespace, while SVG elements must be created in the SVG namespace. 你的问题(你“缺少”)是jQuery在XHTML命名空间中创建新元素,而SVG元素必须在SVG命名空间中创建。 You cannot use raw code in a string for SVG elements. 您不能在SVG元素的字符串中使用原始代码。

The simplest (no-plugins) method is to stop leaning on jQuery so much and just use simple DOM methods to create the elements. 最简单的(无插件)方法是停止依赖jQuery这么多,只需使用简单的DOM方法来创建元素。 Yes, it's more verbose than just using jQuery to magically construct your elements for you...but jQuery does not work in this case. 是的,它比仅使用jQuery为你神奇地构建元素更加冗长......但jQuery在这种情况下不起作用。

Demo: http://jsfiddle.net/nra29/2/ 演示: 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);
}

Using a Library 使用图书馆

Alternatively, you can include Keith Woods' "jQuery SVG" plugin that has a lot of convenience methods for common SVG operations, including the ability to create linear gradients . 或者,您可以包含Keith Woods的“jQuery SVG”插件 ,该插件具有许多常用SVG操作的便捷方法,包括创建线性渐变的功能

I think you'll have to use the SVG plugin for jQuery (found here ). 我想你必须使用jQuery的SVG插件(在这里找到)。 When adding SVG elements using the "normal" jQuery library, probably the namespaces get mixed up. 使用“普通”jQuery库添加SVG元素时,名称空间可能会混淆。

Try the following: 请尝试以下方法:

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

(Not exactly sure, however. You might need to fiddle around a bit with that code.) (但不完全确定。你可能需要用这个代码来捣乱。)

EDIT 编辑

Just created this fiddle in order to test the thesis (as suggested by @Phrogz). 刚刚创建了这个小提琴来测试论文(正如@Phrogz所建议的那样)。 Indeed it returns http://www.w3.org/1999/xhtml as the namespace for the inserted <linearGradient> , which is the wrong namespace and thus validates my above speculation. 实际上,它返回http://www.w3.org/1999/xhtml作为插入的<linearGradient>的命名空间,这是错误的命名空间,因此验证了我的上述推测。

Found a solution. 找到了解决方案。 It's a bit ugly, but doesn't require the use of additional plugins. 它有点难看,但不需要使用额外的插件。

Apparently, a pattern has to be included in the tag when the SVG is first created (it's probably only read then). 显然,首次创建SVG时必须在标记中包含一个模式(它可能只读取它)。

Thus, replacing the SVG tag's wrapper's contents with themselves works ( base being that wrapper): 因此,用自己替换SVG标签的包装器内容是有效的( base是包装器):

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

I just want to drop by and say I have found a more elegant solution that allows you to keep using jQuery with SVG elements but without the jQuery SVG library (which is no longer being updated and has some problems with jQuery 1.8 or higher). 我只想说,我已经找到了一个更优雅的解决方案,允许你继续使用jQuery与SVG元素,但没有jQuery SVG库(不再更新,并有jQuery 1.8或更高版本的一些问题)。 Simply use a function like this: 只需使用这样的功能:

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

it creates a SVG element on the SVG namespace and encapsulates it with jQuery, once the element is created in the right namespace you can use it freely with jQuery: 它在SVG命名空间上创建一个SVG元素并用jQuery封装它,一旦在正确的命名空间中创建了元素,你就可以在jQuery中自由使用它:

You can then use the function in this manner: 然后,您可以以这种方式使用该功能:

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);

It's not as compact as you might want it to be since you have to create each element by hand, but its a lot better than manipulating everything with DOM methods. 它不像你想要的那样紧凑,因为你必须手工创建每个元素,但它比使用DOM方法操作所有元素要好得多。

A small note, jQuery .attr() function assumes all attributes are lowercased, which is not the case for SVG elements (for example the viewBox attribute in <svg> tags). 一个小小的注释,jQuery .attr()函数假设所有属性都是小写的,而SVG元素则不是这种情况(例如<svg>标签中的viewBox属性)。 To get around that, when setting attributes with uppercased letters use something like this: 为了解决这个问题,当使用大写字母设置属性时,请使用以下内容:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM