如何使拉斐尔填充图案与元素一起移动? (相对于元素的背景图像位置)

时间:2013-07-22 15:26:23

标签: svg raphael background-image

如何为Raphael元素提供随元素移动而移动的填充,例如background-image HTML元素的CSS position: absolute;如何在移动时保持相对于其位置的相同位置?

这是一个示例演示:如何将Raphael元素(三角形路径)的背景图像模式拖动为HTML元素(方形div)时的行为相同?

http://jsbin.com/oxuyeq/8/edit


这个问题基本上与偏振眼镜模拟器的 How to make a pattern “fixed” in Raphael.js / IE? 相反 - 我想在所有浏览器(包括所有浏览器)中尝试避免一致的IE特定行为IE8)。

正如其他问题所详述的那样,仅在IE8(VML)中,Raphael元素就像我想要的那样;但即使这是不稳定的:诸如在setSize元素上调用paper或重新定义填充(基本上是强制重绘的任何东西)等各种事情会导致它切换到其他行为。


有一个question similar to this for pure SVG,但在撰写本文时没有任何好的答案,当然也没有一个适用于拉斐尔。


编辑2 :观察SVG模式中发生的情况,似乎每个Raphael变换也会自动对svg <pattern>元素进行相同的矩阵变换。我认为这是导致我试图避免的行为的原因 - 我认为patternContentUnitspatternUnits是无关的。这里似乎有一个未解决的问题(在行this.pattern && updatePosition(this);旁边突出显示与clip-rect相同的问题) - https://github.com/DmitryBaranovskiy/raphael/issues/638

因此,一种可能是to define a custom attribute可以将变换应用于元素而不将其应用于模式。听起来很难 - 可能需要黑客拉斐尔或复制大量拉斐尔变换代码。希望还有其他一些方法。并且god help us making this work in VML ...


编辑3:一些可能相关的信息,不仅仅是有这个问题的路径填充。使用image创建的Raphael paper.image()元素在SVG模式下没有这个问题,但是在IE8 VML模式下有一个非常类似的问题。 Here's a demo 有几种方法可以让图像元素移动,这里有一个并排比较,显示它们在非IE中完美运行并且在IE中都失败了:

enter image description here

2 个答案:

答案 0 :(得分:3)

我最近偶然发现了类似的问题。我无法在任何地方找到合适的解决方案,所以我想出了自己的解决方案。我使用@ user568458的答案作为起点。首先,我更改了updatePosition函数:

 updatePosition = function (o) {
    if(!o.data("relativeFill")) { //data is a custom store for user's properties
        var bbox = o.getBBox(1);
        $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
    }
},

我还改变了fill setFillAndStroke函数的情况,如下所示:

var relativeFill = o.data("relativeFill"),
isURL = Str(value).match(R._ISURL);
if (isURL) {
    el = $("pattern");
    var ig = $("image");
    el.id = R.createUUID();
    $(el, {x: 0, y: 0, patternUnits: relativeFill ? "objectBoundingBox" : "userSpaceOnUse", height: 1, width: 1});
    $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
    el.appendChild(ig);

    (function (el) {
        R._preload(isURL[1], function () {
            var w = this.offsetWidth,
                h = this.offsetHeight,
                bbox = o.getBBox();
            $(el, {width: 1, height: 1});
            $(ig, {width: relativeFill ? bbox.width : w, height: relativeFill ? bbox.height : h});
            o.paper.safari();
        });
    })(el);
    o.paper.defs.appendChild(el);
    $(node, {fill: "url(#" + el.id + ")"});
    o.pattern = el;
    o.pattern && updatePosition(o);
    break;
}

然后,只要您想使用它,就必须设置yourElement.data({ relativeFill: true })

以下是相对和重复填充的工作示例:https://codepen.io/mmazur/pen/Gmebrj?editors=0010

我同意@ user568458,这个解决方案非常难看,可能会停止使用未来的Raphael更新。然而,拉斐尔最近没有改变太多,所以我很好地承担了这个风险。

编辑:修正了此处描述的错误:Image blurry when using url fill pattern for svg circle

答案 1 :(得分:1)

修改:

有一个粗略的解决方法,不需要黑客Raphael核心代码,但确实依赖于一个不完全稳定的IE错误,描述https://github.com/DmitryBaranovskiy/raphael/issues/177

它通过在Raphael对象中重命名来隐藏Raphael中的SVG模式对象,导致updatePosition()不被调用。这会阻止Raphael在SVG模式下移动背景图像。在VML模式下,IE8有一个错误,即使代码移动<fill>元素,在重新强制重绘之前,屏幕上不会更新。

path.patternTmp = path.pattern;
delete path.pattern;

这与下面的建议有相同的另一个缺点,即在VML模式下它依赖于IE错误。但它适用于简单的情况。我没有完全测试,看看这个修复是否有其他副作用,但不应该 - 看看Raphael的源代码,看起来updatePosition()是唯一使用{{1}的Raphael函数}}

http://jsbin.com/oxuyeq/10/edit

请注意,每次应用图片填充时都必须重命名element.pattern - 因此,如果您因任何原因重新应用填充,则需要重新执行此修复 - 并重新应用填充将“修复”我们依赖的IE错误导致图像与对象失去同步。


原始回答:这是一种修复方法。它在两个方面很难看:

  • 对于SVG,它依赖于黑客Raphael核心代码(但只是一个小黑客)
  • 对于VML,它依赖于Internet Explorer重绘错误,如果您的代码导致重绘,则会失败

但是你可以获得相对位置,只要你的代码不会触发IE重绘(例如通过重新设置元素的填充)。

理想情况下,有一种方法可以在IE VML中稳健地运行,而不是依赖于错误。


你可以通过以某种方式关闭Raphael函数path.pattern 来解决这个问题。上述行为不是SVG默认值,它是updatePosition()函数中定义的Raphael特性,似乎旨在使Raphael SVG元素的行为与VML元素 行为的行为相匹配Internet Explorer不是一堆bug和错误。请注意,updatePosition()函数仅在 中以SVG模式调用 - 默认情况下,假定 以此方式运行(但由于重绘而无法可靠地执行此操作)上述链接问题中描述的错误......)。

我(丑陋)这样做的方法是将updatePosition()功能代码修改为:

updatePosition()

...然后,对于要进行相对填充的每个元素,设置updatePosition = function (o) { if(!o.relativeFill) { // <<< added this condition var bbox = o.getBBox(1); $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"}); } },


请注意这不是一种好的做法 - 例如未来的Raphael更新可能会将relativeFill命名空间用于其他内容。这只是一个粘性石膏修复的例子(几乎可以)。


<强>重。 VML模式:理论上,您可以通过在VML模式Raphael函数someElement.relativeFill = true;和/或{{1}中向该位置添加类似条件,使此修复变得健壮且不依赖于bug将setFillAndStroke设置为动态计算。这些似乎是setCoords的VML等价物。理论上,VML应该默认设置相对于形状的填充,只要alignshape = true for the fill element, which in theory should be true by default

但是,它似乎在实践中并没有像那样工作。从我的测试来看,依赖于上面的IE错误实际上似乎更多强大,而不是试图让IE VML按照记录的方式工作。在我的测试中,让IE8重绘填充是非常困难的:只重置填充似乎就可以了。