document.addEventListener("DOMContentLoaded", (event) => {
const hamburgerButtons = document.querySelectorAll('.hm-button');
const hamburgerLines = [
{
closed: {
x1: 13,
x2: 37,
y1: 13,
y2: 13,
},
open: {
x1: 13,
x2: 37,
y1: 37,
y2: 13,
}
}, {
closed: {
x1: 13,
x2: 37,
y1: 25,
y2: 25,
},
open: null,
}, {
closed: {
x1: 13,
x2: 37,
y1: 37,
y2: 37,
},
open: {
x1: 13,
x2: 37,
y1: 13,
y2: 37,
}
}
]
const handleHamburgerClick = (event) => {
const button = event.target.closest('.hm-button');
const lines = button.querySelectorAll('line');
if (!button.classList.contains('open')) {
lines.forEach((line, idx) => {
if (hamburgerLines[idx].open) {
line.setAttribute('x1', hamburgerLines[idx].open.x1)
line.setAttribute('y1', hamburgerLines[idx].open.y1)
line.setAttribute('x2', hamburgerLines[idx].open.x2)
line.setAttribute('y2', hamburgerLines[idx].open.y2)
}
});
button.classList.add('open');
} else {
if (button.classList.contains('open')) {
lines.forEach((line, idx) => {
if (hamburgerLines[idx].closed) {
line.setAttribute('x1', hamburgerLines[idx].closed.x1)
line.setAttribute('y1', hamburgerLines[idx].closed.y1)
line.setAttribute('x2', hamburgerLines[idx].closed.x2)
line.setAttribute('y2', hamburgerLines[idx].closed.y2)
}
});
button.classList.remove('open');
}
}
};
hamburgerButtons.forEach((button) => {
button.addEventListener("click", handleHamburgerClick);
});
});
.hm-button {
width: 50px;
height: 50px;
cursor: pointer;
}
.hm-button.open .hm-line2 {
stroke: none;
}
.hm-outline {
x: 2px;
y: 2px;
width: 46px;
height: 46px;
rx: 4px;
fill: none;
}
.hm-outline,
[class^="hm-line"] {
stroke-width: 4px;
stroke: black;
}
[class^="hm-line"] {
stroke-linecap: round;
transition: all 0.25s ease;
}
<svg class="hm-button" role="button">
<rect class="hm-outline" />
<line class="hm-line1" x1="13" x2="37" y1="13" y2="13" />
<line class="hm-line2" x1="13" x2="37" y1="25" y2="25" />
<line class="hm-line3" x1="13" x2="37" y1="37" y2="37" />
</svg>
I’m attempting to create a hamburger menu icon using SVG and its child elements as a way to teach myself, but I’m having trouble transitioning the line positions. The change in position is instant, rather than gradual.
What I’m trying to achieve is, the middle line disappearing, and the left point of the first and third lines swap to form an X when the svg is clicked. When it is clicked again, the lines move back and the middle one reappears. Transition doesn’t seem to work when changing the position of the lines, however.
2
Answers
This is easier to control if you use a path. The
d
attribute of the path can styled using CSS, so the transition can be transitioning from one path to another.The only caveat when using a path is that the two paths need to have the same number of commands. In this case that is not an issue. The line in the middle becomes a dot hidden by the X.
You can do SVG animation with SMIL. Basically you define in the SVG how an element should be animated. In the following example there are two
<animate>
elements, one for opening and one for closing.If you specify an empty
begin
attribute, the animation will not start unless you define something else or callbeginElement()
(SVGAnimationElement, section about methods).