JSFiddle - React, Tailwind, and code Playground
HTML
<div id="svg_wrapper">
<svg enable-background="new 0 0 2850 1800" version="1.1" viewBox="0 0 2850 1800" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="SVGID_1_" x1="1425" x2="1425" y1="4.6407" y2="1804.6" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C4A74" offset="0"/>
<stop stop-color="#FA798D" offset="1"/>
</linearGradient>
</defs>
<g id="g0.0"><rect width="2850" height="1800" fill="url(#SVGID_1_)"/><circle cx="1409.8" cy="990.67" r="597.05" fill="#e5edd4" opacity=".08"/><circle cx="1409.8" cy="990.67" r="429.67" fill="#e5edd4" opacity=".08"/><circle cx="1409.8" cy="990.67" r="316.81" fill="#fff"/><path d="m1707.5 264.5c.588-1.213.61-2.531-.232-3.621-.65-.842-1.652-1.314-2.719-1.312-.903.002-1.843.357-2.477 1.008-1.051 1.079-1.134 2.629-.467 3.931.733 1.433 2.861 1.965 4.201 1.101 1.462-.943 1.885-2.668 1.101-4.201-.011-.021-.021-.042-.032-.063.103.244.206.489.309.733 0-.019-.01-.037-.011-.056v1.632c0-.016 0-.033.01-.049-.103.244-.206.489-.309.733.01-.014.016-.028.023-.042l-.48.621c.012-.012.025-.024.037-.036l-.621.48c.016-.01.032-.019.048-.029-.244.103-.489.206-.733.31.038-.018.078-.03.12-.036-.272.037-.544.073-.816.11.071-.008.131-.009.202-.002-.272-.037-.544-.073-.816-.11.042.006.082.017.12.034-.244-.103-.489-.206-.733-.31.016.009.032.018.049.028l-.621-.48c.013.012.025.023.038.035l-.48-.621c.01.014.016.028.024.042-.103-.244-.206-.489-.309-.733 0 .016.01.033.01.049v-1.632c0 .019-.01.038-.01.056.103-.244.206-.489.309-.733-.01.021-.021.042-.031.064-.365.752-.538 1.536-.31 2.366.196.712.747 1.484 1.411 1.835 1.376.726 3.453.441 4.2-1.102z" fill="#fff"/><path d="m1717.6 179.12c-.275.275-.551.55-.826.826-1.137 1.137-1.222 3.22 0 4.342 1.226 1.125 3.128 1.214 4.342 0l.826-.826c1.137-1.137 1.222-3.22 0-4.342-1.226-1.126-3.129-1.214-4.342 0z" fill="#fff"/><path d="m1757.5 137-.826-.826c-1.137-1.137-3.22-1.222-4.342 0-1.125 1.226-1.214 3.128 0 4.342l.826.826c1.137 1.137 3.22 1.222 4.342 0 1.126-1.225 1.214-3.128...
CSS
#svg_wrapper {
padding: 25em 0;
}
JavaScript
let svg = document.querySelector("svg");
// Groups filtered by g prefix on id attribute.
let groups = document.querySelectorAll("svg g[id^='g']");
// Regex to match and extract parallax factor(s)
let groupRegex = /^\w+(\d*(?:\.\d+)?)(?:-(\d*(?:\.\d+)?))?$/;
let lastKnownScrollPosition = 0;
let ticking = false;
document.addEventListener('scroll', function(e) {
lastKnownScrollPosition = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
parallax(lastKnownScrollPosition);
ticking = false;
});
ticking = true;
}
});
// Do parallax scrolling on groups.
function parallax(scrollPos) {
let svgHeight = svg.getBBox().height;
// This variable controls the vertical coordinate of the document in which
// the image appears as visible in the editors (all groups have no transformation).
let delta = svg.clientTop + svg.clientHeight * 0.5 - scrollPos;
for (let i = 0; i < groups.length; ++i) {
let id = groups[i].getAttribute("id");
let match = id.match(groupRegex);
if (match === null)
continue;
let factor = parseFloat(match[1]) * delta;
let transform = "translate(0 " + factor + ")";
// If a second float is specified i.e.: 60.65-1.78 the group is perpendicular to
// the viewing plane and need additional computation.
if (match[2] != undefined) {
let boundingBox = groups[i].getBBox();
// Get parallax factor for bottom edge of group bounding box.
let factorFront = parseFloat(match[2]) * delta + boundingBox.height;
// Compute the scale for the group.
let scale = (factorFront - factor) / boundingBox.height;
// Compute the translation.
let y = boundingBox.y + factor;
// Tranform the group aligning first on zero y coordinate, then scale,
// then moving it back to correct position.
transform = "translate(0, " + y + ") scale(1 " + scale + ") translate(0 " + -boundingBox.y + ")";
}
groups[i].setAttribute("transform",...