Reputation: 1623
I have an image inside an inline SVG that switches between two masks when it is hovered over.
However, the CSS transition is not working with transition-property: mask;
Is there a different method that can be used that works with a CSS transition?
I also tried styling <mask>
but it seems that definition elements cannot be styled (?).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div style="width: 604px; height: 302px; margin: 20px auto;"> <!-- IE needs width AND height specified to scale the SVG inside correctly. -->
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1208 604">
<style type="text/css">
/* <![CDATA[ */
.mask_hover a:link,
.mask_hover a:visited {
mask: url(#mask_right);
}
.mask_hover a:hover,
.mask_hover a:active {
transition-property: mask;
transition-duration: 0.4s;
transition-timing-function: ease-in-out;
mask: url(#mask_left);
}
/* ]]> */
</style>
<defs>
<mask id="mask_left">
<circle id="circle_l" cx="416" cy="216" r="202" fill="#fff"/>
</mask>
<mask id="mask_right">
<circle id="circle_r" cx="809" cy="337" r="202" fill="#fff"/>
</mask>
</defs>
<g class="mask_hover">
<a xlink:href="#">
<image id="img" width="1208" height="604" xlink:href="https://i.sstatic.net/LCpGU.jpg"/>
</a>
</g>
</svg>
</div>
</body>
</html>
Image credit: Pixabay
Upvotes: 1
Views: 2255
Reputation: 136618
It is not really the mask
property that can't be animated, but the url()
<func>.
To create a transition, you need to have a state 1 that goes to a state 2 with the possibility to create an interpolation between both states.
When you use url(#1)
and wish to go to url(#2)
, there is no way to create any interpolation between these two states, because url(#1.5)
will not be the in-between state.
What can be animated though is the content of your mask.
In SVG2, you can set directly the properties that did change (i.e cx
and cy
) from CSS, but this is still only supported by Blink&Safari browsers:
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1208 604">
<style type="text/css">
/* <![CDATA[ */
.mask_hover a:link,
.mask_hover a:visited {
mask: url(#mask);
}
#mask > circle {
transition-property: cx, cy;
transition-duration: 0.4s;
transition-timing-function: ease-in-out;
}
.mask_hover a:hover + defs #mask > circle,
.mask_hover a:active + defs #mask > circle {
cx: 416;
cy: 216;
}
/* ]]> */
</style>
<g class="mask_hover">
<a xlink:href="#">
<image id="img" width="1208" height="604" xlink:href="https://i.sstatic.net/LCpGU.jpg"/>
</a>
<!-- we need to move it here so we can target it with our CSS rules -->
<defs>
<mask id="mask">
<circle id="circle_l" cx="809" cy="337" r="202" fill="#fff"/>
</mask>
</defs>
</g>
</svg>
For other browsers that don't support this part of SVG2, you should be able to do it with the transform property, but somehow, Firefox doesn't accept it...
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1208 604">
<style type="text/css">
/* <![CDATA[ */
.mask_hover a:link,
.mask_hover a:visited {
mask: url(#mask);
}
#mask > circle {
transition-property: transform;
transition-duration: 0.4s;
transition-timing-function: ease-in-out;
}
.mask_hover a:hover + defs #mask > circle,
.mask_hover a:active + defs #mask > circle {
transform: translate(-393px, -121px);
}
/* ]]> */
</style>
<g class="mask_hover">
<a xlink:href="#">
<image id="img" width="1208" height="604" xlink:href="https://i.sstatic.net/LCpGU.jpg"/>
</a>
<!-- we need to move it here so we can target it with our CSS rules -->
<defs>
<mask id="mask">
<circle id="circle_l" cx="809" cy="337" r="202" fill="#fff"/>
</mask>
</defs>
</g>
</svg>
So you may also want to try with SMIL, but there you won't be able to have the :active
rule, and Safari is buggy in implementing an hover state...
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1208 604">
<style type="text/css">
/* <![CDATA[ */
.mask_hover a:link,
.mask_hover a:visited {
mask: url(#mask);
}
/* ]]> */
</style>
<defs>
<mask id="mask">
<circle id="circle_r" cx="809" cy="337" r="202" fill="#fff">
<animateTransform attributeName="transform"
attributeType="XML"
type="translate"
to="-393 -121"
dur="0.4s"
fill="freeze"
begin="anchor.mouseover"/>
<animateTransform attributeName="transform"
attributeType="XML"
type="translate"
to="0 0"
dur="0.4s"
fill="freeze"
begin="anchor.mouseout"/>
</circle>
</mask>
</defs>
<g class="mask_hover">
<a xlink:href="#" id="anchor">
<image id="img" width="1208" height="604" xlink:href="https://i.sstatic.net/LCpGU.jpg"/>
</a>
</g>
</svg>
So a final way might be to implement the whole thing using only clip-path
from CSS:
<svg id="artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1208 604">
<style type="text/css">
/* <![CDATA[ */
.mask_hover a image {
transition: clip-path .4s;
}
.mask_hover a {
pointer-events: all;
}
.mask_hover a:link image,
.mask_hover a:visited image{
clip-path: circle(202px at 809px 337px);
}
.mask_hover a:hover image,
.mask_hover a:active image{
clip-path: circle(202px at 416px 216px);
}
/* ]]> */
</style>
<g class="mask_hover">
<a xlink:href="#">
<image id="img" width="1208" height="604" xlink:href="https://i.sstatic.net/LCpGU.jpg"/>
<!-- so we can hover everywhre -->
<rect fill="none" width="1208" height="604"/>
</a>
</g>
</svg>
Upvotes: 5