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

Elementor 3.17 Update Broke My Forms — Here’s the Fix

Everything looks fine for about twelve seconds. Then the contact forms stop submitting. No error message. No console warning. Just… ...

Read More

My WordPress Site Got Hacked (Not My Fault — wp2shell Did It)

Someone just exploited a hole in WordPress core that lets them take over your server without logging in. Here's what ...

Read More

Elementor Update Just Broke My Site — Here’s How I Sorted It

Updated Elementor and your site broke? I walk through the actual fixes that worked when it happened to a client ...

Read More

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
Showing Slide 1 of 5

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>