Как сделать анимацию прозрачных кругов сначала они маленькие потом большие и исчезают
Как можно сделать сначала виден весь сайт потом запускается анимация по кнопке (это без разницы) на весь экран спускается фон быстро, и постепенно в рандомном порядке и месте появляются круги без фона за ними виден сайт и увеличиваются в размере, после чего пропадают ну или так скажем соединяются в большой один исчезает, и все сайт виден. Есть зарисовка идеи, черный фон это как бы сайт

Ответы (2 шт):
Маска реализована через clipPath - внешний контур - прямоугольник, внутренние - круги. Так сделан опускающийся занавес и появления кругов на нём. Группа g отвечает за изменение масштаба в конце.
const createElement = (parent_, tag) => {
const element = document.createElementNS(
'http://www.w3.org/2000/svg',
tag
);
parent_.appendChild(element);
return element;
}
const svg = createElement(document.body, 'svg');
svg.style.position = 'fixed';
svg.style.left = 0;
svg.style.top = 0;
svg.style.width = '100%';
svg.style.height = '100%';
const g = createElement(svg, 'g');
const clipPath = createElement(g, 'clipPath');
clipPath.setAttribute('id', 'clipPath');
const path = createElement(clipPath, 'path');
path.setAttribute('clip-rule', 'evenodd');
const setPath = (w, h, r, holes) => {
const shapes = [`M 0 0 H ${w} V ${h} H -${w} z`];
for (const [x, y] of holes) {
const hole = `
M ${x - r} ${y}
a ${r} ${r} 0 1 0 ${2 * r} 0
a ${r} ${r} 0 1 0 ${-2 * r} 0
z
`;
shapes.push(hole);
}
path.setAttribute('d', shapes.join(' '));
};
setPath(200, 300, 20, []);
const rect = createElement(g, 'rect');
rect.setAttribute('width', '100%');
rect.setAttribute('height', '100%');
rect.setAttribute('clip-path', 'url(#clipPath)');
const shuffle = a => {
for (let i = 1; i < a.length; ++i) {
const j = Math.floor(Math.random() * (i + 1)); // random in [0, i]
[a[i], a[j]] = [a[j], a[i]]; // swap a[i] <-> a[j]
}
};
const slide = next => {
const start = performance.now() / 1000;
const step = () => {
const t = performance.now() / 1000 - start;
const w = svg.clientWidth;
const h = Math.min(300 * t, svg.clientHeight);
setPath(w, h, 20, []);
window.requestAnimationFrame(
(h < svg.clientHeight) ? step : next
);
};
window.requestAnimationFrame(step);
};
const holes = next => {
const start = performance.now() / 1000;
const r = 25;
const d = 2 * r;
const w = Math.floor(svg.clientWidth / d);
const h = Math.floor(svg.clientHeight / d);
const points = [];
for (let i = 0; i < h; ++i) {
for (let j = 0; j < w; ++j) {
points.push([r + j * d, r + i * d]);
}
}
shuffle(points);
points.splice(Math.floor(points.length * 3 / 4));
const step = () => {
const t = performance.now() / 1000 - start;
const n = Math.min(points.length, Math.floor(20 * t));
setPath(svg.clientWidth, svg.clientHeight, r, points.slice(0, n));
window.requestAnimationFrame(
(n < points.length) ? step : next(r, points[0])
);
};
window.requestAnimationFrame(step);
};
const dist = (p1, p2) => {
const dx = p1[0] - p2[0];
const dy = p1[1] - p2[1];
return Math.sqrt(dx * dx + dy * dy);
};
const zoom = (radius, center) => () => {
const start = performance.now() / 1000;
const w = svg.clientWidth ;
const h = svg.clientHeight;
const corners = [[0, 0], [w, 0], [0, h], [w, h]];
const maxDist = Math.max(...corners.map(p => dist(center, p)));
const maxScale = maxDist / radius;
const step = () => {
const t = performance.now() / 1000 - start;
const s = Math.exp(t * t);
const tx = (1 - s) * center[0];
const ty = (1 - s) * center[1];
g.setAttribute('transform', `translate(${tx}, ${ty}) scale(${s})`);
if (s < maxScale) {
window.requestAnimationFrame(step);
} else {
console.log('done');
}
};
window.requestAnimationFrame(step);
};
slide(() => holes(zoom));
An example of the Lorem ipsum placeholder text on a green and white webpage.
Using Lorem ipsum to focus attention on graphic elements in a webpage design proposal
One of the earliest examples of the Lorem ipsum placeholder text on 1960s advertising.
In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available. It is also used to temporarily replace text in a process called greeking, which allows designers to consider the form of a webpage or publication, without the meaning of the text influencing the design.
Lorem ipsum is typically a corrupted version of De finibus bonorum et malorum, a 1st-century BC text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical and improper Latin. The first two words themselves are a truncation of 'dolorem ipsum' ('pain itself').
Может быть кому пригодится тут сделал сброс стилей по завершению анимации и разные размеры кругов в зависимости от размера экрана
const container = document.getElementById("animation-container");
const createElement = (parent_, tag) => {
const element = document.createElementNS("http://www.w3.org/2000/svg", tag);
parent_.appendChild(element);
return element;
};
const svg = createElement(container, "svg");
const g = createElement(svg, "g");
g.setAttribute("transform", "");
const clipPath = createElement(g, "clipPath");
clipPath.setAttribute("id", "clipPath");
const path = createElement(clipPath, "path");
path.setAttribute("clip-rule", "evenodd");
const setPath = (w, h, r, holes) => {
const shapes = [`M 0 0 H ${w} V ${h} H -${w} z`];
for (const [x, y] of holes) {
const hole = `
M ${x - r} ${y}
a ${r} ${r} 0 1 0 ${2 * r} 0
a ${r} ${r} 0 1 0 ${-2 * r} 0
z
`;
shapes.push(hole);
}
path.setAttribute("d", shapes.join(" "));
};
const rect = createElement(g, "rect");
rect.setAttribute("width", "100%");
rect.setAttribute("height", "100%");
rect.setAttribute("clip-path", "url(#clipPath)");
rect.setAttribute("fill", "#c9fd5b");
const shuffle = (a) => {
for (let i = 1; i < a.length; ++i) {
const j = Math.floor(Math.random() * (i + 1)); // random in [0, i]
[a[i], a[j]] = [a[j], a[i]]; // swap a[i] <-> a[j]
}
};
const slide = (next) => {
const start = performance.now() / 1000;
const step = () => {
const t = performance.now() / 1000 - start;
const w = svg.clientWidth;
const h = Math.min(2000 * t, svg.clientHeight);
setPath(w, h, 20, []);
window.requestAnimationFrame(h < svg.clientHeight ? step : next);
};
window.requestAnimationFrame(step);
};
const holes = (next) => {
const start = performance.now() / 1000;
let r;
if (window.innerWidth < 600) {
r = 40;
} else if (window.innerWidth < 1200) {
r = 60;
} else if (window.innerWidth < 1920) {
r = 90;
} else {
r = 100;
}
const d = 2 * r;
const w = Math.floor(svg.clientWidth / d);
const h = Math.floor(svg.clientHeight / d);
const points = [];
for (let i = 0; i < h; ++i) {
for (let j = 0; j < w; ++j) {
points.push([r + j * d, r + i * d]);
}
}
shuffle(points);
points.splice(Math.floor((points.length * 3) / 4));
const step = () => {
const t = performance.now() / 1000 - start;
const n = Math.min(points.length, Math.floor(20 * t));
setPath(svg.clientWidth, svg.clientHeight, r, points.slice(0, n));
window.requestAnimationFrame(n < points.length ? step : next(r, points[0]));
};
window.requestAnimationFrame(step);
};
const dist = (p1, p2) => {
const dx = p1[0] - p2[0];
const dy = p1[1] - p2[1];
return Math.sqrt(dx * dx + dy * dy);
};
const zoom = (radius, center) => () => {
const start = performance.now() / 1000;
const w = svg.clientWidth;
const h = svg.clientHeight;
const corners = [[0, 0], [w, 0], [0, h], [w, h]];
const maxDist = Math.max(...corners.map((p) => dist(center, p)));
const maxScale = maxDist / radius;
const step = () => {
const t = performance.now() / 1000 - start;
const s = Math.exp(t * t);
const tx = (1 - s) * center[0];
const ty = (1 - s) * center[1];
g.style.transform = `translate(${tx}px, ${ty}px) scale(${s})`;
if (s < maxScale) {
window.requestAnimationFrame(step);
} else {
svg.style.position = "absolute";
// удаление стиля через 5 секунд после окончания анимации
setTimeout(() => {
g.removeAttribute("style");
}, 300);
setTimeout(() => {
g.removeAttribute("style");
path.setAttribute("d", ""); }, 300);
}
};
window.requestAnimationFrame(step);
};
let isAnimationRunning = false;
let secondClick = false;
const resetAnimation = () => {
clearAnimation();
resetTransform();
isAnimationRunning = false;
secondClick = false;
g.setAttribute("transform", "");
rect.setAttribute("fill", "#c9fd5b");
};
const clearAnimation = () => {
while (svg.lastChild) {
svg.removeChild(svg.lastChild);
}
resetTransform();
svg.style.position = "relative";
};
const toggleAnimation = () => {
if (!isAnimationRunning) {
svg.style.position = "fixed";
svg.style.left = 0;
svg.style.top = 0;
svg.style.width = "100%";
svg.style.height = "100%";
slide(() => holes(zoom))(null, [svg.clientWidth / 2, svg.clientHeight / 2]);
isAnimationRunning = true;
} else if (!secondClick) {
resetAnimation();
secondClick = true;
} else {
resetAnimation();
svg.style.position = "fixed";
svg.style.left = 0;
svg.style.top = 0;
svg.style.width = "100%";
svg.style.height = "100%";
slide(() => holes(zoom))(null, [svg.clientWidth / 2, svg.clientHeight / 2]);
secondClick = false;
}
};
svg.addEventListener("animationend", resetAnimation);
document.getElementById("switch").addEventListener("click", toggleAnimation);