ElwoodP
ElwoodP

Reputation: 428

Cross browser gradient mask

I have a CSS3 animation where a background is vertically scrolling. I want the top of the background to gradually fade out. I have achieved this in Chrome using:

-webkit-mask-box-image: -webkit-linear-gradient(transparent, black);

Is there a cross browser way of achieving this? Maybe using an SVG mask? Or even a JavaScript solution to setting up this animation?

Please see example code below:

div {
  background-image: linear-gradient(rgba(24, 49, 67, .95), rgba(24, 49, 67, .88)), url(https://static.pexels.com/photos/96380/pexels-photo-96380.jpeg);
  background-size: cover;
  position: absolute;
  width: 500px;
  height: 500px;
}
div:before {
  background: url(https://dl.dropboxusercontent.com/u/3454522/shapes1.svg);
  content: '';
  display: block;
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0;
  background-size: 100%;
  height: 500px;
  animation: bgscroll 100s infinite linear;
  -webkit-mask-box-image: linear-gradient(transparent, black);
}
@keyframes bgscroll {
  100% {
    background-position: 0px -3000px;
  }
}
<div>
</div>

Upvotes: 1

Views: 1475

Answers (1)

Harry
Harry

Reputation: 89780

Cross-browser Mask:

Yes, we can achieve cross-browser masking using SVG masks. For this, we would need to put both the background image (your shapes SVG) and the mask on the same SVG element like in the below snippet. This part of the answer works in Chrome, Firefox, Opera (all latest versions), IE11 and Edge. It should also work in IE9 and IE10 because they have support for basic SVG, masking and clipping.

div {
  background-image: linear-gradient(rgba(24, 49, 67, .95), rgba(24, 49, 67, .88)), url(https://static.pexels.com/photos/96380/pexels-photo-96380.jpeg);
  background-size: cover;
  position: absolute;
  width: 500px;
  height: 500px;
}
svg {
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0;
  height: 500px;
  width: 500px;
}
<div>
  <svg viewBox='0 0 500 500' id='svg1'>
    <defs>
      <pattern id='bg-img' patternUnits='userSpaceOnUse' width='500' height='500' patternTransform='translate(0 3000)'>
        <image xlink:href='https://dl.dropboxusercontent.com/u/3454522/shapes1.svg' x='0' y='0' width='500' height='500' />
      </pattern>
      <linearGradient id='grad' gradientTransform='rotate(90 250 250)'>
        <stop offset='0%' stop-color='black' />
        <stop offset='100%' stop-color='white' />
      </linearGradient>
      <mask id='msk'>
        <polygon points='0,0 0,500 500,500 500,0' fill='url(#grad)' />
      </mask>
    </defs>
    <polygon points='0,0 0,500 500,500 500,0' fill='url(#bg-img)' mask='url(#msk)' />
  </svg>
</div>


Cross-browser Mask Animation:

In theory, the animation can also be achieved using SVG's patternTransform attribute. We can add a translate transform to the pattern element and make it mimic background-position animation.

var y = 3000;
var svg1 = document.getElementById('bg-img');
setInterval(function() {
  y = (y <= 0) ? 3000 : y - 10;
  svg1.setAttribute('patternTransform', 'translate(0 ' + y + ')');
}, 100);
div {
  background-image: linear-gradient(rgba(24, 49, 67, .95), rgba(24, 49, 67, .88)), url(https://static.pexels.com/photos/96380/pexels-photo-96380.jpeg);
  background-size: cover;
  position: absolute;
  width: 500px;
  height: 500px;
}
svg {
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0;
  height: 500px;
  width: 500px;
}
<div>
  <svg viewBox='0 0 500 500' id='svg1'>
    <defs>
      <pattern id='bg-img' patternUnits='userSpaceOnUse' width='500' height='500' patternTransform='translate(0 3000)'>
        <image xlink:href='https://dl.dropboxusercontent.com/u/3454522/shapes1.svg' x='0' y='0' width='500' height='500' />
      </pattern>
      <linearGradient id='grad' gradientTransform='rotate(90 250 250)'>
        <stop offset='0%' stop-color='black' />
        <stop offset='100%' stop-color='white' />
      </linearGradient>
      <mask id='msk'>
        <polygon points='0,0 0,500 500,500 500,0' fill='url(#grad)' />
      </mask>
    </defs>
    <polygon points='0,0 0,500 500,500 500,0' fill='url(#bg-img)' mask='url(#msk)' />
  </svg>
</div>

Unfortunately, this seems to work only in Chrome, Firefox and Opera. As usual, IE behaves differently and doesn't change the pattern position even though the patterTransform is getting applied correctly.


It works in IE also if we animate the y attribute of the pattern using snap.svg library.

var s = Snap("#svg1");

var polygon = s.polygon([0, 0, 0, 500, 500, 500, 500, 0]);
var mask = s.polygon([0, 0, 0, 500, 500, 500, 500, 0]);
var grad = s.gradient('L(0,0,500,500)#000-#fff').attr({
  gradientTransform: 'rotate(45,250,250)'
});
var pattern = s.image('https://dl.dropboxusercontent.com/u/3454522/shapes1.svg', 0, 0, 500, 500)
  .pattern(0, 0, 500, 500);
mask.attr({
  fill: grad
});
polygon.attr({
  fill: pattern,
  mask: mask
});

function animIn() {
  this.animate({
    y: '0'
  }, 10000, mina.linear, animOut);
}

function animOut() {
  this.animate({
    y: '3000'
  }, 0, mina.linear, animIn);
};
pattern.animate({
  y: '3000'
}, 0, mina.linear, animIn);
div {
  background-image: linear-gradient(rgba(24, 49, 67, .95), rgba(24, 49, 67, .88)), url(https://static.pexels.com/photos/96380/pexels-photo-96380.jpeg);
  background-size: cover;
  position: absolute;
  width: 500px;
  height: 500px;
}

svg {
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0;
  height: 500px;
  width: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>
<div>
  <svg viewBox='0 0 500 500' id='svg1'>
  </svg>
</div>

(A big thanks to web-tiki for helping me figure out the option using snap.svg.)

Upvotes: 3

Related Questions