Ian
Ian

Reputation: 34489

Moving an SVG mask

I've got some SVG that I've put together, but one of the things I'm stuggling with is to move a mask that I've created. It's easiest to describe with an example:

function createFog(imageUrl) {
  const mapImage = new Image();

  mapImage.onload = function() {
    // Grab the dimensions of the map
    const width = this.width;
    const height = this.height;

    d3.selectAll(".full-size").attr({
      height,
      width
    });
    d3.select("#map").attr("xlink:href", imageUrl);
  };

  mapImage.src = imageUrl;
}
createFog("https://oneinchsquare.files.wordpress.com/2015/01/greenest_transparent_grid.jpg");
img, svg, canvas {
    position: absolute;
    top: 0;
    left: 0;
}

html, body {
    overflow: hidden;
    background: black;
}

#interaction {
    fill: none;
    pointer-events: all;
}

#light {
    opacity: 0.9;
}

#fog {
    opacity: 0;
    fill: black;
}

.explore-outline {
    fill: none;
    stroke: #aaa;
}

.torch-outline {
    fill: none;
    stroke: #ff9800;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<svg class="full-size">
        <g id="view" transform="scale(0.25)">
            <defs>
              <radialGradient id="radGrad" class="full-size" r="0.31">
                <stop offset="0%" stop-color="black" />
                <stop offset="50%" stop-color="#aaa" />
                <stop offset="60%" stop-color="#bbb" />
                <stop offset="70%" stop-color="#ccc" />
                <stop offset="80%" stop-color="#ddd" />
                <stop offset="90%" stop-color="#eee" />
                <stop offset="100%" stop-color="white" />
              </radialGradient>
              <mask class="full-size" id="explored">
                    <rect class="full-size" id="unexplored" x="0" y="0" width="500" height="500" fill="white"/>
                    <g id="explored"></g>
              </mask>
              <mask id="hole">
                <rect id="mask" class="full-size" x="0" y="0" width="500" height="500" fill="url(#radGrad)"></rect>
              </mask>
            </defs>
            <image class="full-size" id="map" x="0" y="0"/>
            <rect class="full-size" id="light" x="0" y="0" width="500" height="500" mask="url(#hole)"></rect>
            <rect class="full-size" id="fog" x="0" y="0" width="500" height="500" mask="url(#explored)"></rect>
        </g>
        <rect id="interaction" class="full-size" x="0" y="0"></rect>
      </svg>

What I wish to be able to do is to move the "light" effect when someone clicks on the background, moving the lighting to that point. This light effect is actually created by cutting a hole in the middle of a dark layer. I don't need help with the code from that point of view, it's more the things I've tried to do in SVG don't seem to produce the effect I want.

The problem I've got is I don't seem to be able to move the radialGradient, I assumed I could set the cx or cy but they don't seem to do anything.

This leaves me with another option that I've tried - translating the #mask or #light elements, however doing so then leaves a portion of the map uncovered. I thought I'd be able to just oversize these layers to compensate - but then unfortunately that scales the hole to larger which I don't want to do.

Is there anyway I can move this "hole" effectively?

Upvotes: 1

Views: 669

Answers (1)

Paul LeBeau
Paul LeBeau

Reputation: 101820

I assumed I could set the cx or cy but they don't seem to do anything.

It seems to work fine when the cx and cy are changed.

function createFog(imageUrl) {
  const mapImage = new Image();

  mapImage.onload = function() {
    // Grab the dimensions of the map
    const width = this.width;
    const height = this.height;

    d3.selectAll(".full-size").attr({
      height,
      width
    });
    d3.select("#map").attr("xlink:href", imageUrl);
  };

  mapImage.src = imageUrl;
}
createFog("https://oneinchsquare.files.wordpress.com/2015/01/greenest_transparent_grid.jpg");
img, svg, canvas {
    position: absolute;
    top: 0;
    left: 0;
}

html, body {
    overflow: hidden;
    background: black;
}

#interaction {
    fill: none;
    pointer-events: all;
}

#light {
    opacity: 0.9;
}

#fog {
    opacity: 0;
    fill: black;
}

.explore-outline {
    fill: none;
    stroke: #aaa;
}

.torch-outline {
    fill: none;
    stroke: #ff9800;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<svg class="full-size">
        <g id="view" transform="scale(0.25)">
            <defs>
              <radialGradient id="radGrad" class="full-size" r="0.31"
                              cx="0.3" cy="0.7">
                <stop offset="0%" stop-color="black" />
                <stop offset="50%" stop-color="#aaa" />
                <stop offset="60%" stop-color="#bbb" />
                <stop offset="70%" stop-color="#ccc" />
                <stop offset="80%" stop-color="#ddd" />
                <stop offset="90%" stop-color="#eee" />
                <stop offset="100%" stop-color="white" />
              </radialGradient>
              <mask class="full-size" id="explored">
                    <rect class="full-size" id="unexplored" x="0" y="0" width="500" height="500" fill="white"/>
                    <g id="explored"></g>
              </mask>
              <mask id="hole">
                <rect id="mask" class="full-size" x="0" y="0" width="500" height="500" fill="url(#radGrad)"></rect>
              </mask>
            </defs>
            <image class="full-size" id="map" x="0" y="0"/>
            <rect class="full-size" id="light" x="0" y="0" width="500" height="500" mask="url(#hole)"></rect>
            <rect class="full-size" id="fog" x="0" y="0" width="500" height="500" mask="url(#explored)"></rect>
        </g>
        <rect id="interaction" class="full-size" x="0" y="0"></rect>
      </svg>

Upvotes: 1

Related Questions