Mentalist
Mentalist

Reputation: 1623

CSS transition on the mask of an SVG?

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

Answers (1)

Kaiido
Kaiido

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

Related Questions