example.js
· 18 KiB · SVG
Raw
function generateSVG(grid) {
// Размер ячейки для каждого кружка (в пикселях) - как в первой версии
const cellSize = 22.5;
// Радиус кружка
const radius = 10;
// Ширина обводки для кружков
const strokeWidth = 2;
// Ширина и высота SVG
const width = grid[0].length * cellSize;
const height = grid.length * cellSize;
// Начало SVG
let svg = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">`;
// Проходим по 2D массиву (true - закрашенный, false - пустой)
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;
// Длина окружности для stroke-dasharray
const circumference = 2 * Math.PI * radius;
// Направление: шахматное (чётная сумма row+col - по часовой, нечётная - против)
const isClockwise = (row + col) % 2 === 0;
const initialOffset = isClockwise ? circumference : -circumference;
// Квадрат для финальной анимации, начальный rx=ry=radius, opacity=0
const squareSize = 2 * radius;
const squareX = cx - radius;
const squareY = cy - radius;
svg += `<rect x="${squareX}" y="${squareY}" width="${squareSize}" height="${squareSize}" rx="${radius}" ry="${radius}" fill="black" opacity="0" id="square_${row}_${col}"></rect>`;
if (grid[row][col]) { // true - закрашенный
// Закрашенный кружок: дуга без заливки, затем внутренний чёрный кружок
svg += `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="black" stroke-width="${strokeWidth}" stroke-dasharray="${circumference}" stroke-dashoffset="${initialOffset}" id="circle_${row}_${col}"></circle>`;
// Внутренний кружок для расширения и заполнения чёрным из центра, начальный r=0
svg += `<circle cx="${cx}" cy="${cy}" r="0" fill="black" id="inner_${row}_${col}"></circle>`;
} else { // false - пустой
// Пустой кружок: только дуга без заливки
svg += `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="black" stroke-width="${strokeWidth}" stroke-dasharray="${circumference}" stroke-dashoffset="${initialOffset}" id="circle_${row}_${col}"></circle>`;
}
}
}
// Конец SVG
svg += '</svg>';
return svg;
}
// Функция для замены первого SVG на странице и запуска анимации
function replaceAndAnimate(grid) {
const svgString = generateSVG(grid);
const existingSVG = document.querySelector('svg');
if (existingSVG) {
existingSVG.outerHTML = svgString;
// Теперь запускаем анимацию
animateCircles(grid);
} else {
console.log('SVG не найден на странице');
}
}
// Функция анимации через JS
function animateCircles(grid) {
const radius = 10;
const arcDur = 500; // Длительность рисования дуги (мс)
const arcDelayStep = 100; // Задержка между кружками (мс)
const fillDur = 500; // Длительность расширения внутреннего (мс)
const fillDelayStep = 100; // Задержка для расширения (мс)
const squareDur = 2000; // Длительность превращения в квадрат (мс)
// Центр сетки
const centerRow = Math.floor(grid.length / 2);
const centerCol = Math.floor(grid[0].length / 2);
// Максимальная задержка для первого этапа (рисование дуг)
const maxDelayFirst = (grid.length + grid[0].length - 2) * arcDelayStep;
// Анимация рисования дуг для всех кружков
for (let row = 0; row < grid.length; row++) {
for (let col = 0; col < grid[row].length; col++) {
const delay = (row + col) * arcDelayStep;
const circle = document.getElementById(`circle_${row}_${col}`);
if (circle) {
const isClockwise = (row + col) % 2 === 0;
setTimeout(() => {
animateDashOffset(circle, isClockwise, 2 * Math.PI * radius, arcDur);
}, delay);
}
}
}
// Анимация расширения для закрашенных после первого этапа
setTimeout(() => {
for (let row = 0; row < grid.length; row++) {
for (let col = 0; col < grid[row].length; col++) {
if (grid[row][col]) { // true - закрашенный
const distance = Math.sqrt((row - centerRow) ** 2 + (col - centerCol) ** 2);
const delay = distance * fillDelayStep;
const innerCircle = document.getElementById(`inner_${row}_${col}`);
if (innerCircle) {
setTimeout(() => {
animateRadius(innerCircle, 0, radius, fillDur);
}, delay);
}
}
}
}
// Финальная анимация превращения в квадраты после расширения
const maxDelaySecond = Math.max(...grid.flatMap((row, r) => row.map((_, c) => Math.sqrt((r - centerRow) ** 2 + (c - centerCol) ** 2)))) * fillDelayStep;
setTimeout(() => {
for (let row = 0; row < grid.length; row++) {
for (let col = 0; col < grid[row].length; col++) {
const circle = document.getElementById(`circle_${row}_${col}`);
const square = document.getElementById(`square_${row}_${col}`);
if (grid[row][col]) { // Только для закрашенных
animateToSquare(circle, square, radius, squareDur);
} else { // Для незакрашенных - плавное исчезновение
animateFadeOut(circle, squareDur);
}
}
}
}, maxDelaySecond + fillDur);
}, maxDelayFirst + arcDur);
}
// Вспомогательная функция для анимации stroke-dashoffset (рисование дуги)
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);
}
// Вспомогательная функция для плавного превращения в квадрат (теперь с border-radius эффектом)
function animateToSquare(circle, square, 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; // opacity от 0 до 1
square.setAttribute('rx', currentRxRy);
square.setAttribute('ry', currentRxRy);
square.setAttribute('opacity', currentOpacity);
// Скрываем круг, устанавливая opacity=0
circle.setAttribute('opacity', 1 - progress);
if (progress < 1) {
requestAnimationFrame(step);
}
};
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; // opacity от 1 до 0
element.setAttribute('opacity', currentOpacity);
if (progress < 1) {
requestAnimationFrame(step);
}
};
requestAnimationFrame(step);
}
// Пример использования:
replaceAndAnimate([[true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, true, false, false, false, true, true, true, true, true, true, true], [true, false, false, false, false, false, true, false, false, false, true, false, false, true, true, true, true, true, false, false, true, true, false, true, true, false, false, false, true, false, true, false, false, false, false, false, true], [true, false, true, true, true, false, true, false, true, true, true, true, false, true, true, false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, true, false, true, true, true, false, true], [true, false, true, true, true, false, true, false, true, false, false, false, true, true, false, false, false, false, true, false, true, false, false, true, true, true, false, false, true, false, true, false, true, true, true, false, true], [true, false, true, true, true, false, true, false, false, true, false, false, false, true, true, true, false, false, true, false, true, false, false, false, true, true, false, true, true, false, true, false, true, true, true, false, true], [true, false, false, false, false, false, true, false, true, false, false, true, false, false, false, true, true, false, false, true, false, true, false, false, true, true, true, true, 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, false, true, false, true, false, true, false, true, true, true, true, true, true, true], [false, false, false, false, false, false, false, false, true, false, false, true, true, true, true, false, true, true, false, false, false, true, false, true, false, false, true, true, true, false, false, false, false, false, false, false, false], [false, true, false, true, false, true, true, true, true, true, false, false, true, false, false, true, true, false, false, true, false, false, true, true, true, false, false, false, true, true, true, true, false, true, true, false, true], [true, false, false, false, false, false, false, true, true, false, false, true, true, true, false, false, true, true, false, true, true, false, true, true, true, true, true, true, true, false, false, true, false, false, true, true, false], [true, false, false, false, true, false, true, false, false, false, true, false, true, true, false, false, true, false, true, false, false, true, true, true, true, false, false, true, false, true, false, false, true, false, true, true, false], [false, true, false, true, true, false, false, true, true, false, true, true, false, true, true, true, false, false, false, true, true, false, false, true, true, true, false, false, true, false, false, true, true, false, true, false, true], [false, true, false, true, true, true, true, true, true, true, false, true, true, true, false, true, true, true, false, true, true, false, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false], [true, false, false, false, true, true, false, true, false, false, true, false, false, true, false, false, false, true, true, false, false, false, true, false, true, false, false, true, false, true, true, false, false, true, false, false, false], [true, true, true, false, false, true, true, true, true, false, false, true, true, false, false, false, false, true, true, false, false, true, false, true, true, false, false, true, true, true, true, true, false, false, true, true, true], [true, false, true, true, true, true, false, true, true, false, true, false, false, false, true, true, false, true, false, true, true, false, false, true, true, true, true, false, true, false, false, true, false, false, false, false, false], [false, true, false, false, false, true, true, false, true, true, true, false, false, false, false, true, false, true, false, false, false, false, true, true, true, true, true, true, true, false, true, true, true, false, true, false, true], [false, false, true, true, false, false, false, true, true, false, false, false, true, false, false, true, false, false, true, false, false, true, false, false, true, false, false, true, false, true, false, false, false, false, true, false, false], [true, true, true, true, false, false, true, true, true, true, false, false, false, true, true, true, true, false, false, false, true, false, true, true, true, true, false, false, false, true, false, false, false, false, false, true, true], [false, true, false, false, true, true, false, false, false, true, false, false, false, true, true, true, true, true, true, false, false, true, false, false, true, true, false, true, true, true, true, true, true, false, false, false, false], [true, false, true, false, false, false, true, false, false, true, false, false, false, false, false, false, true, true, true, true, true, false, true, false, true, false, true, false, true, false, false, false, true, false, true, false, false], [false, true, true, false, true, false, false, true, false, true, false, true, true, false, false, false, true, false, false, false, true, false, false, true, true, false, false, false, false, true, false, false, false, true, false, false, false], [true, false, true, false, true, true, true, false, true, true, true, false, false, false, true, false, true, true, false, true, true, true, true, true, true, false, false, false, false, true, false, true, true, true, false, false, true], [false, false, false, true, true, false, false, true, true, false, true, true, false, false, false, false, false, true, false, true, true, false, false, true, true, false, true, true, true, false, true, false, false, true, false, true, false], [true, false, false, true, false, false, true, false, false, true, false, true, true, false, true, true, true, true, true, true, false, false, false, true, false, false, false, true, false, true, false, false, true, true, true, false, true], [false, false, false, true, true, false, false, true, true, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true, false, true, false, true, true, true, false, true, true, true, true, false, false], [true, false, false, true, true, false, true, true, false, true, false, true, false, false, false, false, true, false, true, false, false, false, true, true, false, true, true, false, false, false, false, false, true, true, true, true, true], [false, true, false, false, false, false, false, false, true, true, false, true, true, true, true, true, false, false, false, false, false, true, true, true, false, true, true, true, false, false, true, false, true, true, false, false, false], [true, true, true, true, false, false, true, false, false, true, true, true, true, true, false, true, false, false, true, true, true, false, false, false, true, false, false, false, true, true, true, true, true, true, false, false, true], [false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, true, false, false, true, true, true, true, true, true, true, false, false, false, true, false, false, false, true, false, false, false, false], [true, true, true, true, true, true, true, false, true, true, false, true, true, true, false, false, true, true, true, true, false, false, true, false, true, true, false, false, true, false, true, false, true, true, true, true, false], [true, false, false, false, false, false, true, false, true, true, true, false, true, false, true, true, true, false, false, false, false, true, false, false, false, true, false, true, true, false, false, false, true, false, true, true, true], [true, false, true, true, true, false, true, false, false, true, false, true, false, true, true, true, true, true, true, false, false, false, true, true, false, false, true, false, true, true, true, true, true, false, false, false, false], [true, false, true, true, true, false, true, false, true, true, true, true, true, true, true, false, false, true, true, true, true, false, true, true, false, true, false, false, false, true, false, true, false, true, false, true, false], [true, false, true, true, true, false, true, false, false, true, false, false, true, false, true, false, false, true, true, false, true, true, true, false, false, true, true, false, false, false, false, false, false, true, false, false, true], [true, false, false, false, false, false, true, false, true, true, true, true, false, true, true, true, true, false, false, false, false, false, false, false, false, true, false, true, false, true, true, true, false, true, false, false, false], [true, true, true, true, true, true, true, false, false, true, true, false, false, false, false, true, false, false, true, true, true, false, false, false, false, false, true, true, false, false, false, true, false, true, true, true, true]]);
text2json_qr.py
· 1.7 KiB · Python
Raw
import qrcode, json
def generate_qr_boolean_array(text):
"""
Generates a QR code for the given text using version 5 and error correction level Q,
then converts it to a 2D boolean array where each element represents a module (square):
- True for black (dark) modules
- False for white (light) modules
Note: QR version 5 has a fixed size of 37x37 modules (excluding borders).
If the text is too long, it may not fit; adjust version or error correction as needed.
"""
# Create QR code object with specified parameters
qr = qrcode.QRCode(
version=5, # Fixed to version 5
error_correction=qrcode.constants.ERROR_CORRECT_Q, # Q level error correction
box_size=1, # Each module is 1 pixel (not relevant for boolean array)
border=0, # No border to get raw 37x37 modules
)
# Add data and generate the QR code
qr.add_data(text)
qr.make(fit=True) # This will attempt to fit, but version is fixed to 5
# Generate the image (black and white)
img = qr.make_image(fill_color="black", back_color="white")
# Get image dimensions (should be 37x37 for version 5 with border=0)
width, height = img.size
# Convert to 2D boolean list
boolean_array = []
for y in range(height):
row = []
for x in range(width):
pixel = img.getpixel((x, y))
# Check if pixel is black (0 for grayscale, or (0,0,0) for RGB)
if pixel == 0 or pixel == (0, 0, 0):
row.append(True) # Black module
else:
row.append(False) # White module
boolean_array.append(row)
return boolean_array
# Example usage:
print(json.dumps(generate_qr_boolean_array("Hello, world!")))
| 1 | import qrcode, json |
| 2 | |
| 3 | def generate_qr_boolean_array(text): |
| 4 | """ |
| 5 | Generates a QR code for the given text using version 5 and error correction level Q, |
| 6 | then converts it to a 2D boolean array where each element represents a module (square): |
| 7 | - True for black (dark) modules |
| 8 | - False for white (light) modules |
| 9 | |
| 10 | Note: QR version 5 has a fixed size of 37x37 modules (excluding borders). |
| 11 | If the text is too long, it may not fit; adjust version or error correction as needed. |
| 12 | """ |
| 13 | # Create QR code object with specified parameters |
| 14 | qr = qrcode.QRCode( |
| 15 | version=5, # Fixed to version 5 |
| 16 | error_correction=qrcode.constants.ERROR_CORRECT_Q, # Q level error correction |
| 17 | box_size=1, # Each module is 1 pixel (not relevant for boolean array) |
| 18 | border=0, # No border to get raw 37x37 modules |
| 19 | ) |
| 20 | |
| 21 | # Add data and generate the QR code |
| 22 | qr.add_data(text) |
| 23 | qr.make(fit=True) # This will attempt to fit, but version is fixed to 5 |
| 24 | |
| 25 | # Generate the image (black and white) |
| 26 | img = qr.make_image(fill_color="black", back_color="white") |
| 27 | |
| 28 | # Get image dimensions (should be 37x37 for version 5 with border=0) |
| 29 | width, height = img.size |
| 30 | |
| 31 | # Convert to 2D boolean list |
| 32 | boolean_array = [] |
| 33 | for y in range(height): |
| 34 | row = [] |
| 35 | for x in range(width): |
| 36 | pixel = img.getpixel((x, y)) |
| 37 | # Check if pixel is black (0 for grayscale, or (0,0,0) for RGB) |
| 38 | if pixel == 0 or pixel == (0, 0, 0): |
| 39 | row.append(True) # Black module |
| 40 | else: |
| 41 | row.append(False) # White module |
| 42 | boolean_array.append(row) |
| 43 | |
| 44 | return boolean_array |
| 45 | |
| 46 | # Example usage: |
| 47 | print(json.dumps(generate_qr_boolean_array("Hello, world!"))) |