diff --git a/README.md b/README.md index 53d228f..776222f 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ Генерирует SVG халфтоновый эффект из изображения с анимацией мерцания точек. Например: - -![Пример](img/img_2j.svg) +![Пример](img/lp.svg) ## Что делает @@ -48,4 +47,4 @@ poetry run python main.py img/img_2.jpg или наоборот ""распустить") - `fill:#80000090` -- цвет точек (с прозрачностью) -![Еще пример](img/lp_8j.svg) +![Еще пример](img/cc.svg) diff --git a/img/cc.svg b/img/cc.svg new file mode 100644 index 0000000..4a7ed92 --- /dev/null +++ b/img/cc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/img_2.jpg b/img/img_2.jpg deleted file mode 100644 index 9efaeab..0000000 Binary files a/img/img_2.jpg and /dev/null differ diff --git a/img/img_2j.svg b/img/img_2j.svg deleted file mode 100644 index b3a1841..0000000 --- a/img/img_2j.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/img/lp.svg b/img/lp.svg new file mode 100644 index 0000000..278e2e0 --- /dev/null +++ b/img/lp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/lp_8j.svg b/img/lp_8j.svg deleted file mode 100644 index dc96562..0000000 --- a/img/lp_8j.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/main.py b/main.py index 0874ab4..382b8a9 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,7 @@ def encode_to_base36(num): if num < 0: raise ValueError("Number must be non-negative") - charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" if num < len(charset): return charset[num] @@ -19,8 +19,9 @@ def encode_to_base36(num): return ''.join(reversed(result)) -def generate_halftone_svg(image_path, output_path, cols=80, max_radius=6, animation_variants=12): +def generate_halftone_svg(image_path, output_path, cols=80, max_radius=8, animation_variants=12): import random + from collections import defaultdict img = Image.open(image_path).convert('L') aspect_ratio = img.height / img.width @@ -31,14 +32,21 @@ def generate_halftone_svg(image_path, output_path, cols=80, max_radius=6, animat width = cols * step height = rows * step - svg_elements = [] - # Генерируем CSS-классы для разных задержек animation_classes = [] for i in range(animation_variants): delay = (i / animation_variants) * 3 encoded_i = encode_to_base36(i) - animation_classes.append(f".a{encoded_i}{{--d:{delay:.2f}s}}") + # Убрать .0 для целых чисел: 1.0s → 1s + if delay == int(delay): + delay_str = f"{int(delay)}" + else: + delay_str = f"{delay:.1f}".lstrip('0') + animation_classes.append(f".{encoded_i}{{--d:{delay_str}s}}") + + # Собираем элементы группируя по классам: {class: [(radius, cx, cy), ...]} + elements_by_class = defaultdict(list) + unique_radii = set() for y in range(rows): for x in range(cols): @@ -48,20 +56,41 @@ def generate_halftone_svg(image_path, output_path, cols=80, max_radius=6, animat if factor < 0.15: continue - radius = factor * max_radius + radius = int(factor * max_radius) + if radius == 0: + continue + cx = x * step + step // 2 cy = y * step + step // 2 - # Каждой точке свой класс с уникальной задержкой anim_class = random.randint(0, animation_variants - 1) encoded_class = encode_to_base36(anim_class) - svg_elements.append( - f'' - ) + + elements_by_class[encoded_class].append((radius, cx, cy)) + unique_radii.add(radius) + + # Создаем с определениями кружков для каждого радиуса + defs = '' + for radius in sorted(unique_radii): + encoded_radius = encode_to_base36(radius) + defs += f'' + defs += '' + + # Создаем группы по классам + groups = [] + for anim_class in sorted(elements_by_class.keys()): + group_elements = elements_by_class[anim_class] + uses = ''.join( + f'' + for radius, cx, cy in group_elements + ) + if uses: + groups.append(f'{uses}') animation_css = "".join(animation_classes) + groups_html = "".join(groups) - svg_template = f"""{"".join(svg_elements)}""" + svg_template = f'''{defs}{groups_html}''' with open(output_path, 'w') as f: f.write(svg_template) @@ -69,4 +98,4 @@ def generate_halftone_svg(image_path, output_path, cols=80, max_radius=6, animat if __name__ == '__main__': file_name_in = sys.argv[-1] - generate_halftone_svg(file_name_in, f'{file_name_in.split(".")[0]}j.svg', cols=90, max_radius=5) + generate_halftone_svg(file_name_in, f'{file_name_in.split(".")[0]}j.svg', cols=63, max_radius=7)