Reputation: 33
I'm trying to make a layout with some cards, which, when clicked, open to reveal more information, like you can see in this image. I managed to get most of the stuff working, but I can't get the little banner on the left to morph into it's open state. From what I've seen, the best way to go about this is using anime.js, but it doesn't work, for some reason. Here's the code:
let cardToggle = document.querySelectorAll('.card-toggle');
for (i = 0; i < cardToggle.length; i++) {
cardToggle[i].addEventListener("click", function() {
//Get the required elements
let cardContent = this.nextElementSibling;
let cardBackground = this.parentElement;
let metaWrapper = this.querySelector('.spell-meta-wraper');
let spellBanner = this.querySelector('.spell-banner');
//spellBanner is the svg I'm trying to animate
//Close
if (cardContent.style.display === "block") {
cardContent.style.display = "none";
metaWrapper.style.display = "none";
cardContent.style.height = "0px";
cardBackground.style.height = "95px";
//Open
} else {
cardContent.style.display = "block";
metaWrapper.style.display = "block";
cardContent.style.height = "410px";
cardBackground.style.height = "505px";
anime({
targets: spellBanner,
points: [
{ value: 'M0,0h47v156l-23.5-21.14L0,156V0z' }
],
easing: 'easeOutQuad',
duration: 2000,
loop: true
});
}
});
}
It might be useful to point out that I'm trying to do this in wordpress, and the cards I'm trying to edit come from a while loop.
I have tried changing the "points" attribute to "d", but that didn't work. I also tested the "spellBanner" variable to see if it's referencing the right element, and it does. I also checked the page's source to see if wordpress is loading the anime.min.js script, and it is.
I have no idea what I'm doing wrong or what alternatives I have for lving this problem.
Upvotes: 2
Views: 1560
Reputation: 2822
Still a lot code for a tiny feature.
I suggest SVG <path>
+ CSS transition
for morphing the path (d
attribute).
(To avoid dublicate path
declaration, you could use CSS vars or SASS vars.)
path {
d: path("M0,0h50v140l-25-20L0,140V0z");
transition: d .18s linear;
}
path:hover {
d: path("M0,0h50v140l-25 20L0,140V0z");
}
<svg width="50" height="160"><path d=""/></svg>
Upvotes: 6
Reputation: 33
Ok, after a bunch more failed attempts, I decided to try something a lot simpler: doing it with CSS. Basically, instead of an svg, I used two divs, togheter, one in the shape of a rectangle, and one in the shape of a triangle.
That being said, this is probably not the ideal solution, and I'll probably implement enxaneta's solution
let spellBanner = document.querySelector('.card-banner');
let spellBannerTip = document.querySelector('.card-banner-tip');
spellBanner.addEventListener("click", function() {
spellBanner.style.height = "135px";
spellBannerTip.style.top = "133px";
spellBannerTip.style.borderBottom = "10px solid white";
spellBannerTip.style.borderTop = "0";
});
.card-banner {
position: absolute;
background-color: black;
width: 47px;
height: 75px;
transition: all 0.2s ease-in-out;
}
.card-banner-tip {
position: absolute;
top: 83px;
width: 0;
height: 0;
border-left: 23.5px solid transparent;
border-right: 23.5px solid transparent;
border-top: 10px solid rgb(0, 0, 0);
transition: all 0.2s ease-in-out;
}
<div class = "card-banner"></div>
<div class = "card-banner-tip"></div>
Upvotes: 0
Reputation: 33044
You don't need to use external libraries for this. In fact the animation you need is very simple since the d attribute will change from:
M0,0H47V156L23.5, 134.86L0,156V0z
to:
M0,0H47V156L23.5, 177.14L0,156V0z
The only thing changing here is the value of the y of one point. In order to get the path morphing you need to animate the y coordinate from 134.86 to 177.14. You can say that the actual value
of the y coordinate is 134.86 and the target
value is 177.14.
let rid = null;
// the value of the y coordinate of the point is changing between 134.86 and 177.14
let memory = [134.86, 177.14];
let target = memory[0];
let value = memory[1];
//a function that updates that value
function updateValue() {
let dist = target - value;
let vel = dist/10;
value += vel;
// stopping the animation when the distance between the target and the value is very small
if (Math.abs(dist) < .01) {
if(rid){window.cancelAnimationFrame(rid);
rid = null;
}
}
}
function updatePath() {
morphingPath.setAttributeNS(null, "d",`M0,0H47V156L23.5, ${value}L0,156V0z`);
}
function Frame() {
rid = window.requestAnimationFrame(Frame);
updateValue();
updatePath();
}
window.addEventListener(
"load",
updatePath,
false
);
svg.addEventListener(//animate the path on mousedown
"mousedown",
function() {
if (rid) {
window.cancelAnimationFrame(rid);
rid = null;
}
memory.reverse();
target = memory[1];
Frame();
},
false
);
svg{width:50px;}
<svg id="svg" viewBox="0 0 46 180"><path id="morphingPath" d="M0,0h47v156L23.5,134.86L0,156V0z" /></svg>
For more details please read the first part of this codepen post: Morphing in SVG - first steps
Upvotes: 2