JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://d3js.org/d3.v3.min.js"></script>
<div class="container">
  <div class="background-image">
    <div class="shell"></div>
    <div class="maskmaker" data-role="maskmaker" data-displace-left="800" data-displace-top="400" data-bubble-value="150" data-label-name="Label test" data-color="#12353e" data-opacity="1" data-width="200" data-height="175"></div>
    <img src="http://i.istockimg.com/file_thumbview_approve/83190789/6/stock-photo-83190789-couple-in-love-drinking-coffee-in-coffee-shop.jpg">
  </div>

</div>

<script src="https://d3js.org/d3.v4.js"></script>

CSS

.background-image {
  position: absolute;
  bottom: 0;
  top: 0;
  left: 0;
  right: 0;
  overflow: hidden;
  z-index: 0;
}

.container {
  overflow: hidden;
  width: 1100px;
  height: 460px;
  position: relative;
}

img {
  width: 100%;
  height: auto;
}

.mask {
  position: absolute;
}

.pointer {
  stroke: #ffffff;
}

.labels text {
  fill: #ffffff;
  font-size: 25px;
}

.centrallabel text {
  fill: #ffffff;
  font-size: 60px;
}

JavaScript

$(document).ready(function() {


  function maskMaker(el) {

    var backcolor = $(el).data("color");
    var backopacity = $(el).data("opacity");

    var height = $(el).data("height");
    var width = $(el).data("width");

    var width = 1120;
    var height = 500;

    var labelName = $(el).data("label-name");
    var bubbleValue = $(el).data("bubble-value");
    var displaceLeft = $(el).data("displace-left");
    var displaceTop = $(el).data("displace-top");

    var data = [{
      "label": labelName,
      "x": displaceLeft,
      "y": displaceTop,
      "value": bubbleValue
    }];

    console.log("MASK data", data);


    // Set the main elements for the series chart
    var svgroot = d3.select($(el)[0])
      .append("svg")
      .attr("viewBox", "0 0 " + width + " " + height)
      /*
                  .attr("width", "100%")
                  .attr("height", "100%")*/
      .attr("width", width)
      .attr("height", height)
      .attr("preserveAspectRatio", "xMidYMid meet");

    // filters go in defs element
    var defs = svgroot.append("defs");

    var mask = defs.append("mask")
      .attr("id", "myMask");

    mask.append("rect")
      .attr("x", 0)
      .attr("y", 0)
      .attr("width", "100%")
      .attr("height", "100%")
      .style("fill", "white")
      .style("opacity", backopacity);


    var invisiblebubble = mask.append("circle")
      .data(data);


    //create a fixed bubble first                  
    invisiblebubble
      .attr("cx", "50%")
      .attr("cy", "50%")
      .attr("r", function(d) {
        return d.value - 20;
      });


    //now mask the fixed circle     
    var masker = defs.append(function() {
        return mask.node().cloneNode(true)
      })
      .attr("id", "myMaskForPointer")
      .select("rect")
      .style("opacity", 0.8);

    invisiblebubble
      .attr("r", 10);

    //animate this circle
    invisiblebubble
      .attr("cx", "50%")
      .attr("cy", "50%")
      .transition()
     ...