Reputation: 31
I am trying to make a flowing/drawing animation of a rounded line. The solution I have works, but is not my favorite. My goal is to have this line flowing down and then to the right when the page is opened.
I've tried recreating the environment this animation will be in so that anyone who can help will have a better idea.
The solution I made has an unflattering glitch at the rounded corner. I've tried looked into other solution with p5.js and svg animtions, but I am not sure what would be best. I think its the corners fault for the complexity but that is the shape I need, unfortunately.
body {
background-color: black;
color: white;
overflow: hidden;
}
.layout {
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
position: relative;
}
.l-shape {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: flex-start;
pointer-events: none;
z-index: -999;
}
.l-line {
position: absolute !important;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
border-left: 20px solid transparent;
border-bottom: 20px solid transparent;
border-bottom-left-radius: 100px;
position: relative;
overflow: hidden;
z-index: 1;
}
.l-line::after {
content: "";
position: absolute !important;
top: -30px;
left: 0;
width: 0;
height: 0;
border-left: 20px solid var(--active-line-color);
border-bottom: 20px solid transparent;
border-bottom-left-radius: 100px;
animation: trace-border 1s ease-in-out forwards;
z-index: 2;
}
@keyframes trace-border {
0% {
height: 0;
width: 0;
border-bottom-color: transparent;
}
50% {
height: 100%;
width: 0;
border-bottom-color: transparent;
}
100% {
height: 100%;
width: 100%;
border-bottom-color: var(--active-line-color);
}
}
.layout[data-active-section="red"] {
--active-line-color: #D85140;
}
.layout[data-active-section="blue"] {
--active-line-color: #3555B4;
}
.layout[data-active-section="white"] {
--active-line-color: #E6E6E6;
}
.layout[data-active-section="yellow"] {
--active-line-color: #F6B90B;
}
.navbar {
display: flex;
justify-content: center;
gap: 20px;
padding: 20px;
}
.navbar button {
background: none;
border: 1px solid white;
color: white;
padding: 10px 20px;
}
<body>
<div class="layout" data-active-section="red">
<div class="l-shape">
<div class="l-line"></div>
</div>
<nav class="navbar">
<button onclick="changeSection('red')">red</button>
<button onclick="changeSection('blue')">blue</button>
<button onclick="changeSection('white')">white</button>
<button onclick="changeSection('yellow')">yellow</button>
</nav>
</div>
<script>
function changeSection(section) {
const layout = document.querySelector(".layout");
const line = document.querySelector(".l-line");
layout.setAttribute("data-active-section", section);
const newLine = line.cloneNode(true);
line.parentNode.replaceChild(newLine, line);
}
</script>
</body>
Upvotes: 3
Views: 97
Reputation: 21291
Use pathLength=100
so all (animate) units always calculate (any) path at 100 total length
Wrapped in a <svg-liner>
Web Component, because... well... Web Components are cool
Use https://yqnn.github.io/svg-path-editor/# to edit your d-path
<script>
customElements.define("svg-liner", class extends HTMLElement {
connectedCallback() {
let color = this.getAttribute("color");
this.innerHTML = `<div style="display:inline-block;width:190px;zoom:.9">
<button>red</button> <button>blue</button> <button>white</button> <button>yellow</button><br>
<svg viewBox="0 0 100 100" style="background:lightgreen">
<path fill="none" stroke="${color}" pathLength="100"
stroke-width="10" d="M10,8c0,79-2,77,21,77s45,0,62,0">
<animate attributeName="stroke-dasharray" from="0,100" to="100,0"
dur=".5s" repeatCount="once" />
</path>
</svg></div>`;
this.onclick = evt => {
let {tagName , textContent } = evt.target;
if (tagName == "BUTTON") {
this.querySelector('path').style.stroke = textContent;
this.querySelector('animate').beginElement();
}
}
}
});
</script>
<svg-liner color="red"></svg-liner>
<svg-liner color="yellow"></svg-liner>
<svg-liner color="blue"></svg-liner>
JSWC
Upvotes: 2
Reputation: 2275
I think you don't have to use any library for that, you can use simple [choosen] svg shape and animate it with stroke-dasharray
and stroke-dashoffset
attributes..
const cPath = document.getElementById("cPath");
const animateBtn = document.getElementById("animateBtn");
const animation = cPath.animate(
[{ strokeDashoffset: 157 }, { strokeDashoffset: 0 }],
{
duration: 2000,
easing: "ease",
fill: "forwards"
}
);
animation.cancel();
animateBtn.addEventListener("click", function () {
animation.cancel();
animation.play();
});
#cPath {
stroke: #FF0000;
stroke-width: 7;
fill: none;
stroke-dasharray: 157;
stroke-dashoffset: 157;
}
<button id="animateBtn">Draw</button>
<svg width="300" height="200">
<path id="cPath" d="m143.89029,99c0,78.8543 -2.08511,77 21.10971,77c23.19483,0 45.08445,0 62.08375,0" />
</svg>
My goal is to have this line flowing down and then to the right when the page is opened.
Just add script
<script>
window.addEventListener("load", (event) => {
const cPath = document.getElementById("cPath");
cPath.animate(
[{
strokeDashoffset: 157
}, {
strokeDashoffset: 0
}], {
duration: 2000,
easing: "ease",
fill: "forwards"
}
);
});
</script>
Small advice, probably you would like add some sequences of animations, like in modern websites... good idea will be use some animations library eg. GSAP. And don't use, in your scenario, DOMContentLoaded
, because this will break whole experience, what [probably] you looking for.
Upvotes: 1