Можно ли оптимизировать js-код для запуска и реверса smil-анимации при клике на svg-иконки когда их несколько?
Помогите, пожалуйста, разобраться, можно ли как-то оптимизировать js-код для реализации функционала запуска по клику вложенной smil-анимации svg-иконок и реверсивной анимации при следующем клике — в условиях, когда иконок на странице много, и в перспективе могут добавляться новые?
(При этом: — используются inline svg, которые стилизуются через переменные; — при необходимости все кнопки с svg-иконкой могут иметь как минимум один общий класс)
В найденном здесь решении для запуска\реверса анимации одиночной svg-иконки по клику смущает необходимость дублирования всего этого js-кода для каждой отдельной иконки и, соответственно, необходимость в каждом случае создания уникальных id. Есть ли варианты для более правильного\универсального\изящного решения?
(Например, можно ли как-то для всех svg-иконок с определенным классом (например, .button_svg) на странице иметь один общий js-код для реализации вложенной интерактивной анимации по кликам? (Например, если для значений < animate > задавать не уникальные id, а унифицированные классы (либо типизированные id, например "buttonsvg_start_1", "buttonsvg_start_2")?)
Вот пример с дублированием js-логики на примере анимации двух иконок - бургер-меню и иконки поиска (лупы). Для удобства в каждом случае я положил js-код в < defs >:
.wrapper {
width: 3rem;
height: 3rem;
margin: 3rem;
}
.button_icon {
cursor: pointer;
}
.animate_icon {
stroke: black;
stroke-width: 12;
stroke-miterlimit: 12;
stroke-linecap: round;
stroke-linejoin: round;
}
<div class="wrapper">
<div class="button_icon">
<svg version="1.1" id="iconburger" class="animate_icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128" overflow="visible" xml:space="preserve">
<defs>
<script>
var iconburger = document.getElementById("iconburger"),
close = document.getElementById("iconburgerClose"),
open = document.getElementById("iconburgerOpen");
let iconburgerFlag = true;
iconburger.addEventListener('click', function() {
if (iconburgerFlag == true) {
iconburgerClose.beginElement();
iconburgerFlag = false;
} else {
iconburgerOpen.beginElement();
iconburgerFlag = true;
}
});
</script>
</defs>
<path id="iconburger-active" d="M 0 4 L 64 4 M 0 64 L 128 64 M 0 124 L 96 124" stroke="black" stroke-width="8" stroke-linecap="round" >
<animate id="iconburgerClose" begin="indefinite" fill="freeze" attributeName="d" dur="0.2s" to="M 4 4 L 64 64 M 4 124 L 124 4 M 64 64 L 124 124"/>
<animate id="iconburgerOpen" begin="indefinite" fill="freeze" attributeName="d" dur="0.2s" to="M 0 4 L 64 4 M 0 64 L 128 64 M 0 124 L 96 124"/>
</path>
</svg>
</div>
</div>
<div class="wrapper">
<div class="button_icon">
<svg version="1.1" id="iconsearch" class="animate_icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128" overflow="visible" xml:space="preserve">
<defs>
<script>
var iconsearch = document.getElementById("iconsearch"),
close = document.getElementById("iconsearchClose"),
open = document.getElementById("iconsearchOpen");
let iconsearchFlag = true;
iconsearch.addEventListener('click', function() {
if (iconsearchFlag == true) {
iconsearchClose.beginElement();
iconsearchFlag = false;
} else {
iconsearchOpen.beginElement();
iconsearchFlag = true;
}
});
</script>
</defs>
<path id="iconsearch-active" d="M 18.398 18.398 C 11.743 25.053 8 34.088 8 43.5 C 8 52.912 11.743 61.947 18.398 68.602 C 25.053 75.257 34.088 79 43.5 79 C 52.912 79 61.947 75.257 68.602 68.602 C 75.257 61.947 79 52.912 79 43.5 C 79 34.088 75.257 25.053 68.602 18.398 C 61.947 11.743 52.912 8 43.5 8 C 34.088 8 25.053 11.743 18.398 18.398 M 124 124 L 70 70" fill="none" stroke="black" stroke-width="8" stroke-linecap="round" >
<animate id="iconsearchClose" begin="indefinite" fill="freeze" attributeName="d" dur="0.2s" to="M 18.398 18.398 C 11.743 25.053 8 34.088 8 43.5 C 8 52.912 11.743 61.947 18.398 68.602 C 25.053 75.257 34.088 79 43.5 79 C 52.912 79 61.947 75.257 68.602 68.602 C 75.257 61.947 79 52.912 79 43.5 C 79 34.088 75.257 25.053 68.602 18.398 C 61.947 11.743 52.912 8 43.5 8 C 34.088 8 25.053 11.743 18.398 18.398 M 124 124 L 70 70"/>
<animate id="iconsearchOpen" begin="indefinite" fill="freeze" attributeName="d" dur="0.2s" to="M 4 124 C 8.333 119.667 12.667 115.333 17 111 C 22.333 105.667 27.667 100.333 33 95 C 38.333 89.667 43.667 84.333 49 79 C 54 74 59 69 64 64 C 69.333 58.667 74.667 53.333 80 48 C 85.333 42.667 90.667 37.333 96 32 C 101.333 26.667 106.667 21.333 112 16 C 116 12 120 8 124 4 M 124 124 L 4 4"/>
</path>
</svg>
</div>
</div>
P. S. Прошу прощения, если вопрос некорректный или глупый, либо оформлен неверно — это первое мое обращение на stackoverflow, перед этим я добросовестно перелопатил десятки страниц по соответствeющим моей задаче запросам, документацию по smil-анимации etc., и я пока только начинаю осваивать js. Заранее спасибо, если кто-то сможет помочь мне и подскажет хороший паттерн реализации такой интерактивности.
Ответы (1 шт):
const svg_icons = document.getElementsByClassName("animate_icon");
for (let i = 0; i < svg_icons.length; i++) {
let icon = svg_icons[i]
let flag = true
// https://developer.mozilla.org/ru/docs/Web/API/Document/querySelector <animate class="open"></animate>
let open = icon.querySelector("animate.open")
let close = icon.querySelector("animate.close")
icon.addEventListener("click", () => {
if (flag) {
open.beginElement()
flag = false
} else {
close.beginElement()
flag = true
}
})
}