function generateSVG(grid, config) { const { cellSize, radius, strokeWidth } = config; const width = grid[0].length * cellSize; const height = grid.length * cellSize; let svg = ``; for (let row = 0; row < grid.length; row++) { for (let col = 0; col < grid[row].length; col++) { const cx = col * cellSize + cellSize / 2; const cy = row * cellSize + cellSize / 2; const circumference = 2 * Math.PI * radius; const isClockwise = (row + col) % 2 === 0; const initialOffset = isClockwise ? circumference : -circumference; const squareX = cx - radius; const squareY = cy - radius; const squareSize = 2 * radius; svg += ``; svg += ``; if (grid[row][col]) { svg += ``; } } } svg += ''; return svg; } function replaceAndAnimate(grid, config) { const svgString = generateSVG(grid, config); const existingSVG = document.querySelector('svg'); if (existingSVG) { existingSVG.outerHTML = svgString; animateCircles(grid, config); } else { console.log('SVG не найден на странице'); } } function animateCircles(grid, config) { const { radius, cellSize, arcDur, arcDelayStep, fillDur, fillDelayStep, squareDur, shrinkDur, moveDur, shrinkFactor, moveFactor } = config; const rows = grid.length; const cols = grid[0].length; const centerRow = Math.floor(rows / 2); const centerCol = Math.floor(cols / 2); const centerX = centerCol * cellSize + cellSize / 2 - radius; const centerY = centerRow * cellSize + cellSize / 2 - radius; const circles = []; const squares = []; const inners = []; for (let row = 0; row < rows; row++) { circles[row] = []; squares[row] = []; inners[row] = []; for (let col = 0; col < cols; col++) { circles[row][col] = document.getElementById(`circle_${row}_${col}`); squares[row][col] = document.getElementById(`square_${row}_${col}`); inners[row][col] = grid[row][col] ? document.getElementById(`inner_${row}_${col}`) : null; } } const arcDelays = []; for (let row = 0; row < rows; row++) { arcDelays[row] = []; for (let col = 0; col < cols; col++) { arcDelays[row][col] = (row + col) * arcDelayStep; } } const maxDelayFirst = (rows + cols - 2) * arcDelayStep; for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const circle = circles[row][col]; if (circle) { const isClockwise = (row + col) % 2 === 0; setTimeout(() => { animateDashOffset(circle, isClockwise, 2 * Math.PI * radius, arcDur); }, arcDelays[row][col]); } } } setTimeout(() => { const fillDelays = []; let maxDistance = 0; for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { if (grid[row][col]) { const distance = Math.sqrt((row - centerRow) ** 2 + (col - centerCol) ** 2); fillDelays.push({ row, col, delay: distance * fillDelayStep }); maxDistance = Math.max(maxDistance, distance); } } } const maxDelaySecond = maxDistance * fillDelayStep; fillDelays.forEach(({ row, col, delay }) => { const innerCircle = inners[row][col]; if (innerCircle) { setTimeout(() => { animateRadius(innerCircle, 0, radius, fillDur); }, delay); } }); setTimeout(() => { for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const circle = circles[row][col]; const square = squares[row][col]; const inner = inners[row][col]; if (grid[row][col]) { animateToSquare(circle, square, inner, radius, squareDur); } else { animateFadeOut(circle, squareDur); square.remove(); } } } setTimeout(() => { for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { if (grid[row][col]) { const square = squares[row][col]; animateShrink(square, 2 * radius, 2 * radius * shrinkFactor, shrinkDur); } } } setTimeout(() => { for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { if (grid[row][col]) { const square = squares[row][col]; const currentX = parseFloat(square.getAttribute('x')); const currentY = parseFloat(square.getAttribute('y')); const toX = currentX + (centerX - currentX) * moveFactor; const toY = currentY + (centerY - currentY) * moveFactor; animateMove(square, currentX, currentY, toX, toY, moveDur); } } } setTimeout(() => { const svg = document.querySelector('svg'); svg.style.borderRadius = '10%'; svg.style.border = '5px black dotted'; }, moveDur); }, shrinkDur); }, squareDur); }, maxDelaySecond + fillDur); }, maxDelayFirst + arcDur); } function animateDashOffset(element, isClockwise, circumference, duration) { const startTime = performance.now(); const from = isClockwise ? circumference : -circumference; const to = 0; const step = () => { const elapsed = performance.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentOffset = from + (to - from) * progress; element.setAttribute('stroke-dashoffset', currentOffset); if (progress < 1) { requestAnimationFrame(step); } }; requestAnimationFrame(step); } function animateRadius(element, from, to, duration) { const startTime = performance.now(); const step = () => { const elapsed = performance.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentRadius = from + (to - from) * progress; element.setAttribute('r', currentRadius); if (progress < 1) { requestAnimationFrame(step); } }; requestAnimationFrame(step); } function animateToSquare(circle, square, inner, radius, duration) { const startTime = performance.now(); const step = () => { const elapsed = performance.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentRxRy = radius * (1 - progress); const currentOpacity = progress; square.setAttribute('rx', currentRxRy); square.setAttribute('ry', currentRxRy); square.setAttribute('opacity', currentOpacity); circle.setAttribute('opacity', 1 - progress); if (progress < 1) { requestAnimationFrame(step); } else { circle.remove(); if (inner) inner.remove(); if (currentOpacity === 1) square.removeAttribute('opacity'); } }; requestAnimationFrame(step); } function animateFadeOut(element, duration) { const startTime = performance.now(); const step = () => { const elapsed = performance.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentOpacity = 1 - progress; element.setAttribute('opacity', currentOpacity); if (progress < 1) { requestAnimationFrame(step); } else { element.remove(); } }; requestAnimationFrame(step); } function animateShrink(element, fromSize, toSize, duration) { const startTime = performance.now(); const step = () => { const elapsed = performance.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentSize = fromSize + (toSize - fromSize) * progress; element.setAttribute('width', currentSize); element.setAttribute('height', currentSize); if (progress < 1) { requestAnimationFrame(step); } }; requestAnimationFrame(step); } function animateMove(element, fromX, fromY, toX, toY, duration) { const startTime = performance.now(); const step = () => { const elapsed = performance.now() - startTime; const progress = Math.min(elapsed / duration, 1); const currentX = fromX + (toX - fromX) * progress; const currentY = fromY + (toY - fromY) * progress; element.setAttribute('x', currentX); element.setAttribute('y', currentY); if (progress < 1) { requestAnimationFrame(step); } }; requestAnimationFrame(step); } // Пример использования: const config = { cellSize: 22.5, radius: 10, strokeWidth: 2, arcDur: 500, arcDelayStep: 100, fillDur: 500, fillDelayStep: 100, squareDur: 2000, shrinkDur: 300, moveDur: 1000, shrinkFactor: 0.9, moveFactor: 0.2 }; const arr = [[true, true, true, true, true, true, true, false, false, false, false, true, false, false, true, true, false, false, true, true, false, false, true, true, true, true, true, true, true], [true, false, false, false, false, false, true, false, true, true, false, false, true, true, false, false, true, false, false, false, true, false, true, false, false, false, false, false, true], [true, false, true, true, true, false, true, false, false, false, true, true, true, false, false, false, false, false, true, false, true, false, true, false, true, true, true, false, true], [true, false, true, true, true, false, true, false, true, false, true, true, true, false, false, false, false, true, false, true, true, false, true, false, true, true, true, false, true], [true, false, true, true, true, false, true, false, true, true, false, true, false, true, true, false, false, true, true, true, false, false, true, false, true, true, true, false, true], [true, false, false, false, false, false, true, false, false, false, false, true, true, false, false, true, false, true, true, false, false, false, true, false, false, false, false, false, true], [true, true, true, true, true, true, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, true, true, true, true, true, true], [false, false, false, false, false, false, false, false, true, true, false, true, false, true, false, false, true, false, false, false, true, false, false, false, false, false, false, false, false], [false, true, false, true, true, true, true, false, true, true, false, false, false, false, false, false, false, false, true, true, true, true, true, false, true, true, false, true, false], [false, true, true, true, false, true, false, false, true, false, true, true, false, true, true, true, false, false, false, false, true, true, true, false, true, false, true, false, false], [false, false, true, true, false, true, true, false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, true, false, true, true, true, false, false], [false, true, true, false, false, false, false, true, false, false, true, true, false, true, true, false, true, true, true, false, false, false, true, true, true, false, false, false, true], [false, false, true, false, true, false, true, true, true, true, false, false, false, false, false, true, false, true, true, true, false, true, true, true, false, false, true, true, false], [true, true, false, false, false, true, false, false, false, true, false, false, true, false, false, true, false, true, true, false, false, false, true, false, true, false, true, false, false], [false, false, true, true, false, true, true, false, false, false, false, true, true, true, true, false, true, true, false, true, true, false, false, true, false, false, false, false, false], [false, true, true, true, true, false, false, false, true, false, true, false, true, true, false, true, false, true, true, true, false, false, false, true, false, true, true, false, false], [true, false, false, true, true, false, true, false, false, false, false, true, false, true, true, true, true, true, false, true, true, false, true, true, true, false, false, false, true], [true, false, false, true, true, true, false, true, false, true, true, false, false, false, false, false, true, false, true, false, true, false, true, false, false, true, true, false, true], [true, true, true, true, true, false, true, true, false, true, true, false, true, false, false, true, false, false, false, false, true, true, true, true, false, true, false, false, true], [true, true, false, true, false, true, false, true, false, false, true, false, false, false, true, false, false, true, true, false, false, false, false, true, false, false, false, false, false], [true, true, false, true, true, true, true, false, true, false, true, true, true, false, true, true, true, false, false, false, true, true, true, true, true, true, false, false, false], [false, false, false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, true, false, true, false, false, false, true, true, false, true, true], [true, true, true, true, true, true, true, false, false, false, false, false, true, true, true, false, false, true, true, true, true, false, true, false, true, false, true, true, false], [true, false, false, false, false, false, true, false, true, true, true, false, true, true, false, true, false, true, false, true, true, false, false, false, true, true, false, true, true], [true, false, true, true, true, false, true, false, true, true, false, false, true, false, true, false, true, true, true, true, true, true, true, true, true, true, false, true, true], [true, false, true, true, true, false, true, false, true, true, true, true, true, true, false, false, true, false, false, false, false, true, false, false, false, true, true, false, false], [true, false, true, true, true, false, true, false, false, false, true, false, false, false, true, false, true, false, true, false, false, false, false, true, true, false, false, true, true], [true, false, false, false, false, false, true, false, true, true, false, false, true, true, true, true, false, false, false, true, true, false, true, false, true, false, true, false, true], [true, true, true, true, true, true, true, false, false, true, false, false, true, false, false, false, true, true, true, false, true, false, false, false, false, true, true, false, false]]; replaceAndAnimate(arr, config);