Never Seen Before – Animated SVG mask entrance effect for images in elementor

How To Make A Vertical 3D Gallery Slider

So if you follow along in the video, I refer to this blog post for you to enter code at ...

Read More

How To Make A Horizontal 3D Gallery Slider

So if you follow along in the video, I refer to this blog post for you to enter code at ...

Read More

How To Make A Auto Rotating 3D Carousel

So if you follow along in the video, I refer to this blog post for you to enter code at ...

Read More

How To Make A 3D Carousel For Products

So if you follow along in the video, I refer to this blog post for you to enter code at ...

Read More

So if you follow along in the video, I refer to this blog post for you to enter code at certain points, so below is everything you will need!

Mask Example I Used In The Tutorial, these are svg so remember to either enable the file type in WordPress or upload through Elementor

The Following are the css class names I used in the tutorial:

				
					mig-image-mask
				
			

The Following code goes into the Slider Container (reboot-slider):

				
					<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
  const images = document.querySelectorAll(".mig-image-mask img");
  const maskUrls = [
    'MASK 1',
    'MASK 2',
    'MASK 3',
    'MASK 4']

  images.forEach((image, imageIndex) => {
    const container = image.parentElement;

    // Create SVG container
    const svgContainer = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgContainer.style.position = "absolute";
    svgContainer.style.top = "0";
    svgContainer.style.left = "0";
    svgContainer.style.width = "100%";
    svgContainer.style.height = "100%";
    svgContainer.style.zIndex = "2";
    svgContainer.style.pointerEvents = "none";
    container.appendChild(svgContainer);

    // Create a mask element
    const mask = document.createElementNS("http://www.w3.org/2000/svg", "mask");
    mask.setAttribute("id", `mask-${imageIndex}`);
    svgContainer.appendChild(mask);

    // Set initial dimensions
    const imgWidth = image.clientWidth;
    const imgHeight = image.clientHeight;
    svgContainer.setAttribute("viewBox", `0 0 ${imgWidth} ${imgHeight}`);

    // Create a Snap.svg instance for animation
    const snapSvg = Snap(svgContainer);
    const path = snapSvg.paper.path();
    mask.appendChild(path.node);

    path.attr({
      fill: "white",
      d: "" // Placeholder for initial path
    });

    // Apply mask to the image
    image.style.mask = `url(#mask-${imageIndex})`;
    image.style.webkitMask = `url(#mask-${imageIndex})`;

    // Load and animate paths
    Promise.all(maskUrls.map(url =>
      fetch(url)
  .then(res => res.text())
  .then(svgContent => {
    const tempDiv = document.createElement("div");
    tempDiv.innerHTML = svgContent;
    const svgElement = tempDiv.querySelector("svg");

    const originalViewBox = svgElement.getAttribute("viewBox").split(" ");
    const scaleX = imgWidth / originalViewBox[2];
    const scaleY = imgHeight / originalViewBox[3];

    // Collect all paths and shapes
    const elements = Array.from(svgElement.querySelectorAll("path, rect, circle, polygon, polyline"));
    const paths = elements.map(el => el.tagName === "path" ? el.getAttribute("d") : convertToPathData(el));

    // Combine and scale paths
    const combinedPath = paths.filter(Boolean).join(" ");
    return Snap.path.map(combinedPath, Snap.matrix().scale(scaleX, scaleY));
  })

      
    )).then(paths => {
      if (paths.length > 0) {
        let currentIndex = 0;

        function animateToNextPath() {
            
            //------------------------------------------------------------------------------
            
            // This next line controls the animation type and speed
            //------------------------------------------------------------------------------
          const nextIndex = (currentIndex + 1) % paths.length;
          
          path.animate({ d: paths[nextIndex] }, 3000, mina.linear, () => {
            currentIndex = nextIndex;
            animateToNextPath(); // Start the next animation immediately
            //setTimeout(animateToNextPath, 1000);
          });
        }

        // Initialize path and start animation
        path.attr({ d: paths[0] });
        animateToNextPath();
      } else {
        console.error("No valid paths loaded for animation.");
      }
    }).catch(err => console.error("Error loading mask paths:", err));
  });

  // Resize handler for dynamic adjustments
  window.addEventListener("resize", () => {
    images.forEach(image => {
        console.log('Resizing image:', image);  // Log to see which images are affected
      const svgContainer = image.parentElement.querySelector("svg");
      if (svgContainer) {
        const imgWidth = image.clientWidth;
        const imgHeight = image.clientHeight;
        svgContainer.setAttribute("width", imgWidth);
        svgContainer.setAttribute("height", imgHeight);
        svgContainer.setAttribute("viewBox", `0 0 ${imgWidth} ${imgHeight}`);
      }
    });
  });
});

function convertToPathData(element) {
  if (element.tagName === "rect") {
    const x = parseFloat(element.getAttribute("x") || 0);
    const y = parseFloat(element.getAttribute("y") || 0);
    const width = parseFloat(element.getAttribute("width") || 0);
    const height = parseFloat(element.getAttribute("height") || 0);
    return `M${x},${y} h${width} v${height} h-${width} Z`; // Rect as a path
  } else if (element.tagName === "circle") {
    const cx = parseFloat(element.getAttribute("cx") || 0);
    const cy = parseFloat(element.getAttribute("cy") || 0);
    const r = parseFloat(element.getAttribute("r") || 0);
    return `M${cx - r},${cy} a${r},${r} 0 1,0 ${2 * r},0 a${r},${r} 0 1,0 -${2 * r},0 Z`; // Circle as a path
  } else if (element.tagName === "polygon" || element.tagName === "polyline") {
    const points = element.getAttribute("points").trim().split(/\s+|,/).map(parseFloat);
    const commands = points.map((p, i) => (i % 2 === 0 ? (i === 0 ? "M" : "L") : "") + p).join(" ");
    return commands + (element.tagName === "polygon" ? " Z" : ""); // Close path for polygons
  }
  return ""; // Ignore unsupported elements
}

</script>

				
			
Hello! How can I help you today?