In my recent project, I am interested in creating a clip-path which moves with my mousemove. My initial idea was simply to select and re-position the ellipsis with its attributes cx
and cy
using the mousemove coordinates, then selecting the rectangle and re-initializing its clip-path
attribute.
This, however, does not seem to work. The only workable solution I have found so far is to delete the rectangle and the clip-path, then re-initializing them at the new coordinates. This works fine for the simple test-case below, but in my actual experiment, the object I’ll try to clip is an externally loaded svg, and having to re-load it every mouseover tick might be prohibitively expensive.
Do you have any suggestions on how to achieve the same effect as I have shown below without re-initializing everything?
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/mathjs/lib/browser/math.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
</style>
</head>
<!-- Create a div where the graph will take place -->
<div id="my_datavisualization">
<svg id="click" xmlns="http://www.w3.org/2000/svg">
<defs>
<g id="pointer" transform="scale(0.5)">
<circle cx="0" cy="0" r="20" id="dragcircle" />
</g>
</defs>
</svg>
</div>
<body style='overflow:hidden'>
<script>
// Get the viewport height and width
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
// Fit to viewport
var height = vw*0.7;
var width = vw;
// Create the canvas. We will use only part of it for the main plot
var svg = d3.select("#click") // This selects the div
.attr("width", width) // This defines the canvas' width
.attr("height", height) // This defines the canvas' height
// define the clipPath
svg.append("clipPath") // define a clip path
.attr("id", "ellipse-clip") // give the clipPath an ID
.append("ellipse") // shape it as an ellipse
.attr("cx", 175) // position the x-centre
.attr("cy", 100) // position the y-centre
.attr("rx", 100) // set the x radius
.attr("ry", 50); // set the y radius
// draw clipped path on the screen
svg.append("rect") // attach a rectangle
.attr("id","cliprect")
.attr("x", 125) // position the left of the rectangle
.attr("y", 75) // position the top of the rectangle
.attr("clip-path", "url(#ellipse-clip)") // clip the rectangle
.style("fill", "lightgrey") // fill the clipped path with grey
.attr("height", 100) // set the height
.attr("width", 200); // set the width
// Shift the marker around on mouseover; restrict it to the contour
var movex
var movey
svg
.on("mousemove", function () {
// Get the current mouseover coordinates
movex = d3.event.x;
movey = d3.event.y;
// The only way I get this to work right now is by removing the previous clipped shape, then re-adding it
d3.select("#cliprect").remove()
d3.select("#ellipse-clip").remove()
// define the clipPath
svg.append("clipPath") // define a clip path
.attr("id", "ellipse-clip") // give the clipPath an ID
.append("ellipse") // shape it as an ellipse
.attr("cx", movex) // position the x-centre
.attr("cy", movey) // position the y-centre
.attr("rx", 100) // set the x radius
.attr("ry", 50); // set the y radius
// draw clipped path on the screen
svg.append("rect") // attach a rectangle
.attr("id","cliprect")
.attr("x", 125) // position the left of the rectangle
.attr("y", 75) // position the top of the rectangle
.attr("clip-path", "url(#ellipse-clip)") // clip the rectangle
.style("fill", "lightgrey") // fill the clipped path with grey
.attr("height", 100) // set the height
.attr("width", 200); // set the width
});
</script>
</body>
</html>