Location>code7788 >text

CSS mask-image for edge fading transitions

Popularity:526 ℃/2024-08-02 12:05:26

Usage Scenarios

In a production environment, we encountered a requirement to embed Google Maps in a large, dark-colored page, and in order to reduce the sense of contradiction, we wanted the map to have a fade-out transition on all four sides.

The key here is to "fade out the transition", not to reduce transparency.

In the dark color example based on Google Maps, the above requirement is attached and the result is as follows:

image

Simply put, it's a map in the center, surrounded by captions and other display board content.

image

CSS mask-image + SVG

Simplify it by replacing the map with an image and implementing an example.

In the example, comment out the contents of the "mask" tag and restore the contents of the "svg test" tag, you can see svg.

Preparation, defining a "container" and "target" layer:

<div >
  <img  src="/photo/2024/07/28/09/04/mountain-8927018_1280.jpg">
  
  <!-- svg test -->
  <!-- <div  style="width:1920px;height:1080px;"></div> -->
</div>

Base Style:

body {
  margin: 0;
  background-color: black;
}

#container {
  position: absolute;
  width: 100%;
  height: 100%;
  background-repeat: repeat;
  display: flex;
  align-items: center;
  justify-content: center;
}

#target {
  max-width: 80%;
  max-height: 80%;
  
  /* mask */
  -webkit-mask-mode: alpha;
  mask-mode: alpha;
  mask-repeat: no-repeat;
  mask-size: 100% 100%;
  
  /* svg test */
  /* background-repeat: no-repeat;
  background-size: 100% 100%; */
}

Add a polka dot background to the "container", in order to verify that the fade transition area can see through the background, here directly with svg implementation:

(function() {
  const container = ('#container');
  const containerBg = `<svg xmlns="http:///2000/svg" width="30" height="30"><circle fill="rgba(255,255,255,0.1)" cx="15" cy="15" r="10" /></svg>`;
   = `url('data:image/svg+xml;utf8,${encodeURIComponent(containerBg)}')`;
  // summarize
})();

Then a handler is prepared for the "target", if the target is an image, it will be executed in the onload of the image in order to get the size of the image:

(function() {
  // summarize
  const target = ('#target');

  function setTargetBg() {
    // summarize
  }

   = setTargetBg
  
  setTargetBg()
})();

To realize the fade transition effect, you need to prepare an svg:

It is divided into 4+1 blocks, 4 trapezoidal paths at the top, bottom, left and right, and 1 rectangular rectangle in the center.
Each of the 4 trapezoids has a linearGradient gradient in 4 directions.

image

Here's the code to draw the svg above:

The width and height of the svg is based on the width and height of the "target", and the size of the fade transition area padding is based on 20% of the short side of the "target".
In particular, the addition and subtraction of "1" in patch and rect is intended to eliminate gaps between paths.

  function setTargetBg() {
    const svgWidth = ,
      svgHeight = ,
      padding = ((, ) * 0.2),
      fill = 'white',
      patch = 0.2;

    const targetMask = `
  <svg xmlns="http:///2000/svg"
    xmlns:xlink="http:///1999/xlink" version="1.1"
    width="${svgWidth}"
    height="${svgHeight}" viewBox="0 0 ${svgWidth} ${svgHeight}">
    <defs>
      <linearGradient  x1="0" x2="0" y1="0" y2="1">
        <stop offset="0%" stop-color="transparent" />
        <stop offset="100%" stop-color="${fill}" />
      </linearGradient>
      <linearGradient  x1="0" x2="0" y1="0" y2="1">
        <stop offset="0%" stop-color="${fill}" />
        <stop offset="100%" stop-color="transparent" />
      </linearGradient>
      <linearGradient  x1="0" x2="1" y1="0" y2="0">
        <stop offset="0%" stop-color="transparent" />
        <stop offset="100%" stop-color="${fill}" />
      </linearGradient>
      <linearGradient  x1="0" x2="1" y1="0" y2="0">
        <stop offset="0%" stop-color="${fill}" />
        <stop offset="100%" stop-color="transparent" />
      </linearGradient>
    </defs>
    <path fill="url(#mask-bottom-to-top)" d="M0,0 L${svgWidth},0 L${svgWidth - padding + patch},${padding + patch} L${padding - patch},${padding + patch} Z"></path>
    <path fill="url(#mask-top-to-bottom)" d="M0,${svgHeight} L${padding - patch},${svgHeight - padding - patch} L${svgWidth - padding + patch},${svgHeight - padding - patch} L${svgWidth},${svgHeight} Z"></path>
    <path fill="url(#mask-rigth-to-left)" d="M0,0 L${padding + patch},${padding} L${padding + patch},${svgHeight - padding} L0,${svgHeight} Z"></path>
    <path fill="url(#mask-left-to-right)" d="M${svgWidth},0 L${svgWidth - padding - patch},${padding} L${svgWidth - padding - patch},${svgHeight - padding} L${svgWidth},${svgHeight} Z"></path>
    <rect x="${padding - 1}" y="${padding - 1}" width="${svgWidth - padding * 2 + 1 * 2}" height="${svgHeight - padding * 2 + 1 * 2}" fill="${fill}"></rect>
  </svg>
`;

	// mask
     = `url('data:image/svg+xml;utf8,${encodeURIComponent((/\n/g, ''))}')`;
    
    // svg test
    //  = `url('data:image/svg+xml;utf8,${encodeURIComponent((/\n/g, ''))}')`;
  }

Final effect:

image

Online Demo