Reputation: 5954
Here is what I've built. You can drag the image around to explore the whole image.
<?xml version='1.0' standalone='no'?>
<svg version='1.1'>
<image xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='background-image' />
<clipPath>
<rect />
</clipPath>
<image xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='main-image'/>
</svg>
Instead of the clipped rectangle having solid edges, what I would like to do is something like this, except for SVG. The caveat is that it must be responsive since the clipped rectangle is responsive.
Is it possible to do something similar in SVG?
One idea that comes to mind is something similar to either of the following images, whereby multiple gradients would be used, but it seems like a whole lot of work for something that can be done so easily in canvas.
Upvotes: 0
Views: 378
Reputation: 137006
What you want is a <mask>
.
In this mask, you will append your small rounded <rect>
filled in black, with an feGaussianBlur applied on it.
const
bdy = document.body,
svg = document.getElementById('svg'),
crc = document.getElementById('circle'),
rec = document.getElementById('rectangle')
let
mousednX = 0,
mousednY = 0
window.addEventListener('load', position)
bdy.addEventListener('mousedown', mousedown)
window.addEventListener('mouseup', mouseup)
bdy.addEventListener('mousemove', moveEye)
function position(){
const
box = svg.getBoundingClientRect()
svg.style.left = -(box.width - innerWidth) / 2 + 'px'
svg.style.top = -(box.height - innerHeight) / 2 + 'px'
}
function mousedown(e){
e.preventDefault()
mousednX = e.clientX
mousednY = e.clientY
bdy.addEventListener('mousemove', mousemove)
}
function mouseup(){
bdy.removeEventListener('mousemove', mousemove)
}
function mousemove(e){
adjustX = e.clientX - mousednX
adjustY = e.clientY - mousednY
if (svg.getBoundingClientRect().left + adjustX < 0 && svg.getBoundingClientRect().right + adjustX > innerWidth){
svg.style.left = svg.getBoundingClientRect().left + adjustX + 'px'
} else if (svg.getBoundingClientRect().left + adjustX >= 0){
svg.style.left = 0 + 'px'
} else {
svg.style.left = -(svg.getBoundingClientRect().width - innerWidth)
}
if (svg.getBoundingClientRect().top + adjustY < 0 && svg.getBoundingClientRect().bottom + adjustY > innerHeight){
svg.style.top = svg.getBoundingClientRect().top + adjustY + 'px'
} else if (svg.getBoundingClientRect().top + adjustY >= 0){
svg.style.top = 0 + 'px'
} else {
svg.style.top = -(svg.getBoundingClientRect().height - innerHeight)
}
mousednX = e.clientX
mousednY = e.clientY
}
function moveEye(e){
rec.setAttribute('x', -(svg.getBoundingClientRect().left) + e.clientX - rec.getBoundingClientRect().width / 2)
rec.setAttribute('y', -(svg.getBoundingClientRect().top) + e.clientY - rec.getBoundingClientRect().height / 2)
}
body {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
}
#svg {
width: 6144px;
height: 4608px;
position: absolute;
left: -3072px; /* set with JS */
top: -2304px; /* set with JS */
}
#background-image {
width: 6144px;
height: 4608px;
opacity: 0.25;
}
#rectangle {
width: 35vw;
height: 75vh;
}
#main-image {
width: 6144px;
height: 4608px;
mask: url(#myMask);
}
#myMask .bg {
width: 100%;
height: 100%;
}
<svg id='svg' viewBox='0 0 6144 4608' version='1.1'>
<defs>
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
</filter>
<mask id="myMask">
<rect class='bg'/>
<rect id='rectangle' x='3172' y='2404' rx='10' ry='10' fill="white" filter="url(#blurMe)"/>
</mask>
</defs>
<image x='0' y='0' preserveAspectRatio='none'
xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='background-image' />
<image x='0' y='0' preserveAspectRatio='none'
xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='main-image'/>
</svg>
But note that setting the dimensions of your svg elements through CSS is a new feature of SVG2 and that all browsers still didn't implemented it (e.g Firefox).
So here is an SVG1 compliant version, but there the vw
/vh
units won't work.
<svg width="500" height="500" viewBox="0 0 500 500">
<defs>
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
</filter>
<mask id="myMask">
<rect width="500" height="500" fill="black"/>
<rect y="100" fill="white" width="50" height="50" x="35" y="35" rx="5" ry="5" filter="url(#blurMe)"/>
</mask>
</defs>
<image xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='background-image' width="500" height="500" style="opacity:0.3"/>
<image xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='main-image' width="500" height="500" mask="url(#myMask)"/>
</svg>
And you could even make this all with a single image, by setting the background's fill color to some shade of gray:
<svg width="500" height="500" viewBox="0 0 500 500">
<defs>
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
</filter>
<mask id="myMask">
<rect width="500" height="500" fill="#333"/>
<rect y="100" fill="white" width="50" height="50" x="35" y="35" rx="5" ry="5" filter="url(#blurMe)"/>
</mask>
</defs>
<image xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg'
id='main-image' width="500" height="500" mask="url(#myMask)"/>
</svg>
Here is the interactive version with a single image.
Upvotes: 1