SVG Line Animation - Squishy Effect - javascript

***Update - solution listed below
I’ve got an svg animation bug I could use some expert help on.
I’m trying to create a similar animation that you can see here (http://springsummer.dk/) when you hover over some of their work (maybe half way down the page) — a line comes in and up and then kind of squishes to the right height.
I recreated this effect in a simple code pen that can be seen here: https://codepen.io/callmegoon/pen/EQRMjG (see code below).
However, in my testing, I came to learn that Safari does not support the negative stroke-dashoffset that I’m using in the animation.
Soooo, I’m trying to figure out a new solution - any ideas? Totally open to a completely different way to accomplish this (maybe a js solution?), but everything I’m doing hasn’t been working. My biggest issue has been the squish part at the end where the back/bottom of the line comes up - I haven’t figured out another way to accomplish that without the negative stroke-dashoffset.
Thanks!
/* Draw Line Animations */
#keyframes drawLine {
0% {
stroke-dasharray: 170;
stroke-dashoffset: 170;
}
100% {
stroke-dasharray: 170;
stroke-dashoffset: -50;
}
}
#keyframes unDrawLine {
0% {
stroke-dasharray: 170;
stroke-dashoffset: -50;
}
100% {
stroke-dasharray: 170;
stroke-dashoffset: 170;
}
}
.box {
background: red;
width: 500px;
height: 500px;
position: relative;
}
.box:hover .line {
animation: drawLine .6s cubic-bezier(.67,.02,.27,.95) forwards;
}
.line {
width: 4px;
height: 170px;
fill: none;
stroke: #fff;
stroke-width: 4px;
transform: rotate(195deg);
display: block;
position: absolute;
left: 50px;
bottom: 50px;
stroke-dasharray: 170;
stroke-dashoffset: 170;
animation: unDrawLine .6s cubic-bezier(.67,.02,.27,.95) forwards;
}
<div class="box">
<svg class="line" viewBox="0 0 4 170" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="0" x2="0" y2="170" />
</svg>
</div>
Solution (uses GreenSock)
I was able to find a solution that perfectly matched the effect I was going for while also working in Safari - it just required GSAP and the draw plugin.
Solution demo here...
https://codepen.io/anon/pen/MQqwdo
Solution explanation...
The hack/fix/trick is just slightly animating one of the coordinates of the points in the line - not noticeable to users.

Related

Make animation keyframes repeat infinitely

I am making a countdown timer circle. The animation works fine on the first iteration, but the circle animation always stays full after the first iteration and does not rest. The number continues to work correctly and rests back to 20, counting down again. I need the red countdown line to copy this.
First time:
Second time:
I have tried adding things like
animation: circletimer 59s linear infinite forwards;
and
animation-iteration-count: infinite
But I can't seem to make the animation happen more than once.
The code that I currently have is:
Countdown component -
interface IProps {
countdown: number
}
const CountDownCircle: FunctionComponent<IProps> = ({
countdown,
}) => {
console.log(countdown)
return (
<div className={'countdown__circle'}>
<svg className={'countdown__circle-svg'} width="200px" height="200px">
<circle className={'circle'} cx="100" cy="100" r="28" />
</svg>
<span className={'timer'}>{countdown}</span>
</div>
)
}
export default CountDownCircle
css(scss) -
.countdown__circle {
position: absolute;
bottom: 34px;
right: 47px;
}
#keyframes circletimer {
0% {
stroke-dashoffset: 500;
stroke-dasharray: 500;
}
100% {
stroke-dashoffset: 0;
stroke-dasharray: 500;
}
}
.countdown__circle-svg {
background-color: transparent;
position: absolute;
background-color: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotateZ(-90deg);
.circle {
stroke: $red;
stroke-width: 5;
stroke-linecap: round;
fill: transparent;
stroke-dashoffset: 500;
stroke-dasharray: 0;
animation: circletimer 59s linear infinite forwards;
}
}
.timer {
position: absolute;
display: block;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: $black;
font-size: 25px;
font-weight: 100;
font-family: $proximaBold;
}
Any advice on how to make this animation happen infinitely would be helpful.
You must remove forwards from your aniamtion, as forwards indicates that animation stays like it is after the first run.
animation: circletimer 59s linear infinite;
W3Schools description for aniamtion-fill-mode:forwards is: "The element will retain the style values that is set by the last keyframe (depends on animation-direction and animation-iteration-count)"

Radial Animation differs from desktop to mobile

I just started working with SVG in the last couple days and I've built a simple radial animation for a pomodoro application. I've come to find that the starting point of the animation differs from what I have it set to and see on a desktop browser (0 deg). The below screenshot from my iPhone 6 in Safari shows what I am seeing (same result in Chrome and Firefox as well). You can see this live for yourself here.
The circle's initial start path was at 90 deg so I applied the following to get the desired starting point:
<circle className={className} transform="rotate(-90, 50, 60)">
I've spent the morning searching through posts and the only thing I really found related to what I'm seeing is a comment at the bottom of this answer here. I went to the spec and read up on it, but I'm not really understanding if I am better off drawing the circle using path or this semi-circle idea that was purposed in the comment (which I'm not understanding at all). It's my understanding now that in the mobile browsers however <circle /> is interpreted the initial starting point is different than in a desktop browser (maybe I'm wrong and this is a OS related issue?).
Can anyone explain how to solve this? I'm not looking to have my problem solved by someone, just a nudge in the right direction. I've been reading through posts and googling for the last 2 hours with no luck and not much more understanding of the problem at large. Thank you in advance for your help.
Full Code for the <circle />:
import PropTypes from 'prop-types'
import { styles } from '../../lib'
/**
* #function Circle
* #description renders <circle /> that is conditionally animated.
*
* #prop {String} className
* #prop {Number} duration - desired length of timer formatted to seconds
* #returns React Element
*/
const Circle = ({ className, duration }) => (
<circle className={className} transform="rotate(-90, 50, 60)">
<style jsx>{`
circle {
cx: 50;
cy: 60;
fill: transparent;
r: 35;
stroke-width: 4;
}
.outer {
stroke: ${styles.colors.highLight};
}
.overlay {
stroke: none;
stroke-dasharray: 219; /* NOTE: https://stackoverflow.com/a/33922201/6520579 */
stroke-linecap: round;
}
.cooldownAnimation {
animation: ${duration}s linear normal forwards cooldown;
stroke: ${styles.colors.text};
}
.timerAnimation {
-webkit-animation: ${duration}s linear normal forwards timer;
animation: ${duration}s linear normal forwards timer;
stroke: ${styles.colors.text};
}
#-webkit-keyframes timer {
from {
stroke-dashoffset: 219;
}
to {
stroke-dashoffset: 0;
}
}
#keyframes timer {
from {
stroke-dashoffset: 219;
}
to {
stroke-dashoffset: 0;
}
}
#-webkit-keyframes cooldown {
from {
stroke-dashoffset: 0;
}
to {
stroke-dashoffset: 219;
}
}
#keyframes cooldown {
from {
stroke-dashoffset: 0;
}
to {
stroke-dashoffset: 219;
}
}
#media (orientation: landscape) {
circle {
r: 50;
}
.overlay {
stroke-dasharray: 314;
}
#-webkit-keyframes timer {
from {
stroke-dashoffset: 314;
}
to {
stroke-dashoffset: 0;
}
}
#keyframes timer {
from {
stroke-dashoffset: 314;
}
to {
stroke-dashoffset: 0;
}
}
#-webkit-keyframes cooldown {
from {
stroke-dashoffset: 0;
}
to {
stroke-dashoffset: 314;
}
}
#keyframes cooldown {
from {
stroke-dashoffset: 0;
}
to {
stroke-dashoffset: 314;
}
}
}
`}</style>
</circle>
)
Circle.propTypes = {
className: PropTypes.string,
duration: PropTypes.number
}
export default Circle
It seems I have fixed the issue and that it was two-fold:
It was in part to how I was writing the CSS. I normally work in Chrome and it didn't cause a fuss over what I was doing; however, after looking in the FireFox dev-tools and seeing that my <circle /> and animation of the stroke was not even visible I found out that...
this code is not valid:
circle {
cx: 50; /* NOT VALID */
cy: 60; /* NOT VALID */
fill: transparent;
r: 35; /* NOT VALID */
stroke-width: 4;
}
These attributes MUST be passed on the element:
<circle
className={className}
cx="50"
cy="60"
r="35"
transform="rotate(-90, 50, 60)"
>
After reading through comments of this article and finding this pen I found out that I did not need to be using stroke-dashoffset and could instead use only stroke-dasharray; but instead of writing in shorthand:
shorthand method:
.overlay {
stroke: none;
stroke-dasharray: 219; /* Set both length of stroke & gap to 219px */
stroke-linecap: round;
}
longhand method:
.overlay {
stroke: none;
stroke-dasharray: 0 219; /* Set stroke length to 0px & gap length to 219px */
stroke-linecap: round;
}
Now in the #keyframes I just had to switch to stroke-dasharray and travel from no stroke length to a length of the entire circumference of the circle and visa versa for the the reverse animation.
#keyframes timer {
from {
stroke-dasharray: 0 219;
}
to {
stroke-dasharray: 219 0;
}
}
In doing this the animation is now functioning as desired across Chrome, FireFox, & Safari on both my iPhone 6 & iPad mini 4. I have no non-Apple devices to test this further unfortunately. I hope this can help out the next guy or gal who runs into this issue, Happy Coding!

Css Animation Keyframes Doesn't start when tab out

I have a css animation that basically starts only after 10 seconds. However, sometimes, if I'm on another tab before the animation starts and I stay on that tab, the animation starts only when I return to the tab / page that has the animation.
document.getElementById('CircleTimer').getElementsByTagName('circle')[0].style.animation = ' countdown 10s linear infinite';
#CircleTimer circle {
stroke-dasharray: 200px;
stroke-dashoffset: 0px;
stroke-width: 2px;
stroke: #04e004;
fill: none;
}
#keyframes countdown {
from {
stroke: #04e004;
stroke-dashoffset: 0px;
}
to {
stroke: #dd0000;
stroke-dashoffset: 200px;
}
}
<svg id="CircleTimer">
<circle r="31" cx="35" cy="35"></circle>
<span id="CountDown">10</span>
</svg>
Update: I'm pretty sure now that the issue is with :
document.getElementById('CircleTimer').getElementsByTagName('circle')[0].style.animation = ' countdown 10s linear infinite';
It seems to not give those properties until I'm on the page.
I made a Jsfiddle to give you an example.
https://jsfiddle.net/8L8vfjsg/

How to create an responsive animated SVG donut with CSS?

How to create an cross browser (Mozilla, Chrome, Safari), animated SVG donut?
Currently I got this: https://jsfiddle.net/odnmhvm6/1/
This seems to be fine and exactly what I want (macOS, Chrome) but some problems appear: (ordered by priority)
Its oversized r="3200" to fit mostly all resolutions, how to make it responsive? -> Later I would add content in the inner-circle (solved by viewBox attribute )
Do not work on mobile (iPhone 6, Safari & Chrome) (done by using fixed values instead of calc())
Without the CSS Offset of .circle > stroke-dashoffset > -50 it laggs at start
I'm not that comfortable with SVG and that's the best result I reached so far, maybe I made it completly wrong for my purpose?
It should be responsive, fill out the complete screen always and work well with the common browsers. JS would be fine too, but is it necessary to reach it?
.item {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
.circle {
stroke-dasharray: 20106.192982975;
stroke-dashoffset: 20106.192982975;
-webkit-animation: circle-in 3s ease-in-out forwards, fade-in 0.5s ease-out forwards;
}
#-webkit-keyframes circle-in {
to {
stroke-dashoffset: 0;
}
}
<div class="item">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
<g>
<circle id="circle" class="circle" r="3200" cy="50%" cx="50%" stroke-width="6200" stroke="#000000" fill="none"/>
</g>
</svg>
</div>

Change class after hovering for a second over svg polygon

I have a SVG polygon displayed, what I want to do is:
When mouse is hovered over the object, wait for one second and then change the class.
If user hovers out, before one second nothing happens.
What I would like to achieve is something like http://codepen.io/jdsteinbach/pen/CsypF but the svg element must only glow after a second.
What I have so far is:
$("#firstObject").stop().hover(
function() { //hovered in
//delay it and add new class
console.log("hovered in");
setTimeout(function() {
console.log("hovered in in");
$("#firstObject").attr("class", "SVGOverVideo1 hoveredObject");
}, 1000);
}, function() { //hovered out
//remove class
$("#firstObject").attr("class", "SVGOverVideo1");
console.log("hovered out");
}
);
.SVGOverVideo1 {
fill: transparent;
stroke: purple;
stroke-width: 2;
position: absolute;
z-index: 1;
top: 0%;
left: 0%;
}
.hoveredObject {
border: double;
border-color: white;
}
<svg class="SVGOverVideo" id="objectsOverVideoContainer">
<polygon id="firstObject" class="SVGOverVideo1" points="200,10 250,190 160,210"></polygon>
Sorry, your browser does not support inline SVG.
</svg>
Thanks!!
You can do it with css only using transition with delay:
transition: stroke 0.01s 1s;
The 1s delays the actual transition, and the actual transition time is so small to that no actual transition occurs.
body {
background: black;
}
.SVGOverVideo1 {
fill: transparent;
stroke: purple;
stroke-width: 2;
position: absolute;
z-index: 1;
top: 0%;
left: 0%;
}
.SVGOverVideo1:hover {
stroke: white;
transition: stroke 0.001s 1s;
}
<svg class="SVGOverVideo" id="objectsOverVideoContainer">
<polygon id="firstObject" class="SVGOverVideo1" points="200,10 250,190 160,210"></polygon>
Sorry, your browser does not support inline SVG.
</svg>

Categories

Resources