8

I have some SVG objects with a tiled image background using fill="url(#background), where I'm defining #background as a pattern containing just an image.

However, when the object is relocated by settings its x/y attributes (or cx/cy, or whatever else defines the offset), the background does not move with it.

How do I make the background move as well? From How would I move an SVG pattern with an element I tried to set the patternContentUnits='objectBoundingBox' but that just turns the image into a solid color blob (oddly, though, colored according to the image, as if it were just the first pixel or some such).

Here is a jsfiddle illustrating all of this - the top rect has been turned into a solid color, and then bottom rect has the background image but it doesn't move with the rect:

http://jsfiddle.net/x8nkz/17/

I know that if you were using transform="translate(...)" instead of setting the x/y attributes, the background does get translated as well, but the existing codebase I'm working within doesn't use translate for positioning (and it would have other unintended consequences on the rest of the application).

Thanks in advance for any help.

Community
  • 1
  • 1
Yang
  • 16,037
  • 15
  • 100
  • 142

4 Answers4

4

I actually encountered the same problem and found a solution. I've forked your fiddle to show the changes. Basically, you remove the patternContentUnits attribute as it doesn't help here and update the x attribute of the image to match that of the rect object after each update.

http://jsfiddle.net/RteBM/2/

Updated HTML:

<svg xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
<title>basic pattern</title>
<defs>
    <pattern id="pat1" width="179" height="132" patternUnits="userSpaceOnUse">
         <image x="0" y="0" width="179" height="132" xlink:href="http://subtlepatterns.subtlepatterns.netdna-cdn.com/patterns/honey_im_subtle.png"></image>
    </pattern>
    <pattern id="pat2" width="179" height="132" patternUnits="userSpaceOnUse">
        <image x="0" y="0" width="179" height="132" xlink:href="http://subtlepatterns.subtlepatterns.netdna-cdn.com/patterns/honey_im_subtle.png"></image>
    </pattern>
</defs>
<rect id="rect1" x="0" y="0" width="179" height="132" fill="url(#pat1)"/>
<rect id="rect2" x="0" y="150" width="179" height="132" fill="url(#pat2)"/>
</svg>

Updated JS:

var i = 0;
var newX;
setInterval(
    function(){
        $('rect').attr('x', Math.sin(i+=.01)*100+100);      
        $('#pat1').attr('x', $("#rect1").attr('x'));
    }, 100);
1

If you use objectBoundingBox units then a width of 1 (or 100%) is the width of the object so 178 would be 178 times that. You likely want

<pattern id="pat1" width="179" height="132" patternUnits="userSpaceOnUse" patternContentUnits="objectBoundingBox">
    <image width="1" height="1" xlink:href="http://subtlepatterns.subtlepatterns.netdna-cdn.com/patterns/honey_im_subtle.png"></image>
</pattern>

That won't make the pattern move though. You are going to need a transform of some sort to do that. If you can't put it on the <rect> maybe you can make the rect a child of a <g> element and transform the <g> element instead.

Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • I just tried this out on the jsfiddle. It doesn't work - the background still does not shift with the rect. – Yang Jan 19 '13 at 01:16
0

Try to set value of patternUnits to "objectBoundingBox".

defghi1977
  • 5,081
  • 1
  • 30
  • 29
-1

use a "nested SVG document" to group the draggable elements

pattern position is relative to the first parent SVG document

fiddle here

note: this may have worse performance than other solutions (assuming that nesting svg documents is expensive)

<html>

<!-- svg.js + svg.draggable.js -->
<script type="text/javascript"
src="https://cdn.jsdelivr.net/npm/@svgdotjs/svg.js@latest/dist/svg.min.js"></script>
<script type="text/javascript"
src="https://cdn.jsdelivr.net/npm/@svgdotjs/svg.draggable.js@latest/dist/svg.draggable.min.js"></script>

<div id="canvas"></div>

<script type="text/javascript">

// create canvas
var draw = SVG().size(200, 200).addTo('#canvas')

// pattern of checkerboard
var patt = draw.pattern(20, 20)
patt.rect(20, 20).fill({color: '#fff'})
patt.rect(10, 10)
patt.rect(10, 10).move(10, 10)

// nested svg document
var nested = draw.nested()

// rect + pattern
nested.rect(80, 80).attr({
    fill: patt
})

// drag on
nested.draggable()



/*
// this works too, but its slow:

// group + draggable
var group = draw.group().draggable()

// move handler
group.on('dragmove', (e) => {
  const {x, y} = e.detail.box
  patt.move(x, y)
})

// rect with pattern
var r = group.rect(80, 80).attr({
    fill: patt
})
*/

</script>
</html>
milahu
  • 2,447
  • 1
  • 18
  • 25