Guide to animating an svg icon when hovering and when it comes into view on the screen

Seeking a way to animate the SVG icon upon mouse hover or when it comes into view.

Encountering an issue where the icon animates on appearance but not on mouse hover.

Any straightforward solutions for this?

Below is the code snippet. Using window.addEventListener in JavaScript to monitor the visibility of the button. When visible, a class is added to trigger the animation.

In CSS, the .animateButton class is responsible for animating specific components of the SVG icon selected via paths.

:hover is supposed to initiate the same animation but is currently inactive.

window.addEventListener('scroll', () => {
  
    var buttonTop = button.getBoundingClientRect().top;
    var buttonIsVisible = (buttonTop - window.innerHeight+25) < 0 && buttonTop > 0;
    //console.log(buttonIsVisible);
    if (buttonIsVisible) {
        if (!button.classList.contains("animateButton")) {
        //console.log(button.className);
            button.classList.add("animateButton");
        }
    } else {
        button.className = "";
    }
})
#button{
width:150px;
background-color:green;
color:white;
padding:.5rem;
text-align: center;
margin-top:500px;
margin-bottom:500px;
}

#button:hover path:nth-child(1){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon .8s ease forwards;
}
#button:hover path:nth-child(2){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon  .8s ease forwards .3s;
}
#button:hover path:nth-child(3), #button:hover path:nth-child(4){
    stroke-dasharray: 7px;
    stroke-dashoffset: 7px;
    animation: animate-icon .5s ease forwards .5s;
}

.animateButton path:nth-child(1){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon .8s ease forwards;
}
.animateButton path:nth-child(2){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon  .8s ease forwards .3s;
}
.animateButton path:nth-child(3), .animateButton path:nth-child(4){
    stroke-dasharray: 7px;
    stroke-dashoffset: 7px;
    animation: animate-icon .5s ease forwards .5s;
}

@keyframes animate-icon{
    to{
        stroke-dashoffset: 0px;
    }
}
<div>
Scroll down to see animation!!
</div>

<div id="button">
 <svg id="circle" width="25" height="25" viewBox="0 0 25 25" fill="none"
                                xmlns="http://www.w3.org/2000/svg">
                                <path
                                    d="M16.6666 21.875V19.7917C16.6666 18.6866 16.2276 17.6268 15.4462 16.8454C14.6648 16.064 13.605 15.625 12.5 15.625H5.20829C4.10322 15.625 3.04342 16.064 2.26201 16.8454C1.48061 17.6268 1.04163 18.6866 1.04163 19.7917V 21.875"
                                    stroke="url(#paint0_linear)" stroke-width="2" stroke-linecap="round"
                                    stroke-linejoin="round" />
                                <path
                                    d="M8.85417 11.4583C11.1554 11.4583 13.0208 9.59285 13.0208 7.29167C13.0208 4.99048 11.1554 3.125 8.85417 3.125C6.55298 3.125 4.6875 4.99048 4.6875 7.29167C4.6875 9.59285 6.55298 11.4583 8.85417 11.4583Z"
                                    stroke="url(#paint1_linear)" stroke-width="2" stroke-linecap="round"
                                    stroke-linejoin="round" />
                                <path d="M20.8334 8.33337V14.5834" stroke="url(#paint2_linear)" stroke-width="2"
                                    stroke-linecap="round" stroke-linejoin="round" />
                                <path d="M23.9584 11.4584H17.7084" stroke="url(#paint3_linear)" stroke-width="2"
                                    stroke-linecap="round" stroke-linejoin="round" />
                                <defs>
                                    <linearGradient id="paint0_linear" x1="2.18637" y1="15.625" x2="8.84787"
                                        y2="24.9627" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint1_linear" x1="5.29803" y1="3.125" x2="13.3121" y2="7.61847"
                                        gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint2_linear" x1="20.9066" y1="8.33337" x2="22.1606"
                                        y2="8.44587" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint3_linear" x1="18.1663" y1="11.4584" x2="18.7611"
                                        y2="13.543" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                </defs>
                            </svg>
<span class="buttonText"> Hover Me! </span>
</div>

Answer №1

Utilize the Intersection Observer method for triggering animations on scroll.

const intersectRatio = .2;
var options = {
  root: null,
  rootMargin: '0px',
  threshold: intersectRatio
}

var handleIntersect = function(entries, observer) {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      console.log("visible")
      entry.target.classList.add('animateButton');
      observer.unobserve(entry.target)
    } else
      console.log("invisible")
  });
  console.log("Handle");
}

var observer = new IntersectionObserver(handleIntersect, options);
document.querySelectorAll('#button').forEach(function(r) {
  observer.observe(r);
})
#button {
  width: 150px;
  background-color: green;
  color: white;
  padding: .5rem;
  text-align: center;
  margin-top: 500px;
  margin-bottom: 500px;
}

#button:hover path:nth-child(1) {
  stroke-dasharray: 25px;
  stroke-dashoffset: 25px;
  animation: animate-icon .8s ease forwards;
}

#button:hover path:nth-child(2) {
  stroke-dasharray: 25px;
  stroke-dashoffset: 25px;
  animation: animate-icon .8s ease forwards .3s;
}

#button:hover path:nth-child(3),
#button:hover path:nth-child(4) {
  stroke-dasharray: 7px;
  stroke-dashoffset: 7px;
  animation: animate-icon .5s ease forwards .5s;
}

.animateButton path:nth-child(1) {
  stroke-dasharray: 25px;
  stroke-dashoffset: 25px;
  animation: animate-icon .8s ease forwards;
}

.animateButton path:nth-child(2) {
  stroke-dasharray: 25px;
  stroke-dashoffset: 25px;
  animation: animate-icon .8s ease forwards .3s;
}

.animateButton path:nth-child(3),
.animateButton path:nth-child(4) {
  stroke-dasharray: 7px;
  stroke-dashoffset: 7px;
  animation: animate-icon .5s ease forwards .5s;
}

@keyframes animate-icon {
  to {
    stroke-dashoffset: 0px;
  }
}
<div>
  Scroll down to see animation!!
</div>

<div id="button">
  <svg id="circle" width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
                                <path
                                    d="M16.6666 21.875V19.7917C16.6666 18.6866 16.2276 17.6268 15.4462 16.8454C14.6648 16.064 13.605 15.625 12.5 15.625H5.20829C4.10322 15.625 3.04342 16.064 2.26201 16.8454C1.48061 17.6268 1.04163 18.6866 1.04163 19.7917V21.875"
                                    stroke="url(#paint0_linear)" stroke-width="2" stroke-linecap="round"
                                    stroke-linejoin="round" />
                                <path
                                    d="M8.85417 11.4583C11.1554 11.4583 13.0208 9.59285 13.0208 7.29167C13.0208 4.99048 11.1554 3.125 8.85417 3.125C6.55298 3.125 4.6875 4.99048 4.6875 7.29167C4.6875 9.59285 6.55298 11.4583 8.85417 11.4583Z"
                                    stroke="url(#paint1_linear)" stroke-width="2" stroke-linecap="round"
                                    stroke-linejoin="round" />
                                <path d="M20.8334 8.33337V14.5834" stroke="url(#paint2_linear)" stroke-width="2"
                                    stroke-linecap="round" stroke-linejoin="round" />
                                <path d="M23.9584 11.4584H17.7084" stroke="url(#paint3_linear)" stroke-width="2"
                                    stroke-linecap="round" stroke-linejoin="round" />
                                <defs>
                                    <linearGradient id="paint0_linear" x1="2.18637" y1="15.625" x2="8.84787"
                                        y2="24.9627" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint1_linear" x1="5.29803" y1="3.125" x2="13.3121" y2="7.61847"
                                        gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint2_linear" x1="20.9066" y1="8.33337" x2="22.1606"
                                        y2="8.44587" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint3_linear" x1="18.1663" y1="11.4584" x2="18.7611"
                                        y2="13.543" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                </defs>
                            </svg>
  <span class="buttonText"> Hover Me! </span>
</div>

Answer №2

Dealing with restarting a CSS animation can be tricky, but there are solutions available. One approach is to reinsert the element into the page, as discussed in a helpful article by css-tricks: https://css-tricks.com/restart-css-animation/. Another workaround involves using the void element.offsetWidth; technique.

Here is an implementation of the reinsert solution mentioned in the article. To prevent automatic triggering of the mouseenter event on mousemove after cloning the element, two nested events were utilized.

let btn = document.getElementById('button');

window.addEventListener('scroll', () => {
  
    var buttonTop = btn.getBoundingClientRect().top;
    var buttonIsVisible = (buttonTop - window.innerHeight+25) < 0 && buttonTop > 0;
    
    if (buttonIsVisible) {
        if (!btn.classList.contains("animateButton")) {
            btn.classList.add("animateButton");
        }
    } else {
        btn.className = "";
    }
})

let animateIt = () => {
    let newone = btn.cloneNode(true);
    btn.parentNode.replaceChild(newone, btn);
    btn = document.getElementById('button');
    
    btn.addEventListener('mouseout', () =>
        btn.addEventListener('mouseenter', animateIt)
    );
};

btn.addEventListener('mouseenter', animateIt);
#button {
width:150px;
background-color:green;
color:white;
padding:.5rem;
text-align: center;
margin-top:500px;
margin-bottom:500px;
}

#button:hover path:nth-child(1){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon .8s ease forwards;
}
#button:hover path:nth-child(2){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon  .8s ease forwards .3s;
}
#button:hover path:nth-child(3), #button:hover path:nth-child(4){
    stroke-dasharray: 7px;
    stroke-dashoffset: 7px;
    animation: animate-icon .5s ease forwards .5s;
}

.animateButton path:nth-child(1){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon .8s ease forwards;
}
.animateButton path:nth-child(2){
    stroke-dasharray: 25px;
    stroke-dashoffset: 25px;
    animation: animate-icon  .8s ease forwards .3s;
}
.animateButton path:nth-child(3), .animateButton path:nth-child(4){
    stroke-dasharray: 7px;
    stroke-dashoffset: 7px;
    animation: animate-icon .5s ease forwards .5s;
}

@keyframes animate-icon{
    to{
        stroke-dashoffset: 0px;
    }
}
<div>
Scroll down to see animation!!
</div>

<div id="button">
 <svg id="circle" width="25" height="25" viewBox="0 0 25 25" fill="none"
                                xmlns="http://www.w3.org/2000/svg">
                                <path
                                    d="M16.6666 21.875V19.7917C16.6666 18.6866 16.2276 17.6268 15.4462 16.8454C14.6648 16.064 13.605 15.625 12.5 15.625H5.20829C4.10322 15.625 3.04342 16.064 2.26201 16.8454C1.48061 17.6268 1.04163 18.6866 1.04163 19.7917V21.875"
                                    stroke="url(#paint0_linear)" stroke-width="2" stroke-linecap="round"
                                    stroke-linejoin="round" />
                                <path
                                    d="M8.85417 11.4583C11.1554 11.4583 13.0208 9.59285 13.0208 7.29167C13.0208 4.99048 11.1554 3.125 8.85417 3.125C6.55298 3.125 4.6875 4.99048 4.6875 7.29167C4.6875 9.59285 6.55298 11.4583 8.85417 11.4583Z"
                                    stroke="url(#paint1_linear)" stroke-width="2" stroke-linecap="round"
                                    stroke-linejoin="round" />
                                <path d="M20.8334 8.33337V14.5834" stroke="url(#paint2_linear)" stroke-width="2"
                                    stroke-linecap="round" stroke-linejoin="round" />
                                <path d="M23.9584 11.4584H17.7084" stroke="url(#paint3_linear)" stroke-width="2"
                                    stroke-linecap="round" stroke-linejoin="round" />
                                <defs>
                                    <linearGradient id="paint0_linear" x1="2.18637" y1="15.625" x2="8.84787"
                                        y2="24.9627" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint1_linear" x1="5.29803" y1="3.125" x2="13.3121" y2="7.61847"
                                        gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint2_linear" x1="20.9066" y1="8.33337" x2="22.1606"
                                        y2="8.44587" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                    <linearGradient id="paint3_linear" x1="18.1663" y1="11.4584" x2="18.7611"
                                        y2="13.543" gradientUnits="userSpaceOnUse">
                                        <stop stop-color="#242E31" />
                                        <stop offset="1" stop-color="#0C1314" />
                                    </linearGradient>
                                </defs>
                            </svg>
<span class="buttonText"> Hover Me! </span>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering an issue of receiving "Undefined" while attempting to access constant data from a ReactJS file within my NodeJS

I am currently working on a file named check-rates that stores useStates() which are inputs provided by users. These inputs are essential for me to calculate and provide an estimated value for their shipment using the DHL API. In my nodejs express server, ...

React URL displaying query

I am currently developing a React app using HashRouter. Within the app, there is a form with inputs where, after the user submits data, some of the information ends up in the URL like this: http://localhost:3000/?State=Alabama#/profile However, it should ...

TypeScript code runs smoothly on local environment, however encounters issues when deployed to production

<div> <div style="text-align:center"> <button class="btnClass">{{ submitButtonCaption }}</button> <button type="button" style="margin-left:15px;" class="cancelButton" (click)="clearSearch()"> {{ clearButtonCapt ...

Prevent the default keyboard from appearing when focusing on a field in an Android/IOS browser using JavaScript

Currently, I am creating a custom 'component' using javascript and I need to prevent the default behavior of the native keyboard popping open when focusing on an input element. Does anyone have any advice or suggestions on the most effective appr ...

I must ensure that the page does not appear until the necessary script has been executed

Is there a way to prevent content from displaying before a redirect on my webpage? <script type = "text/javascript"> if(typeof window.orientation !== 'undefined'){ var ortvalue = "defined"; } if(ortvalue != "defined") { document.location.r ...

Ensure that the default value in the text box remains unchanged until new text is input by utilizing jQuery

Can you please review my code snippet on http://jsfiddle.net/7995m/? The issue I'm facing is that the default text in the text box disappears as soon as it's clicked. What I want is for the default text to remain until the user starts typing. Her ...

Is it possible to retrieve real-time data from a database using AJAX technology?

The information I am retrieving is stored in a specific database. By using an arduino-box, the data is transmitted. The data is then presented using the following CSS & HTML code: <div class="event"> <img src="http://dummyimage. ...

What is the reason for not being able to align breadcrumb text to the right

This section contains HTML code and CSS for a breadcrumb container. The issue is that the breadcrumb is currently displayed on the left side. How can the breadcrumb be right-aligned? .breadcrumb-container { font-family: "Work Sans", sans-serif; ...

When you try to log a DOM object in a content script for a Firefox WebExtension, it will display "<unavailable>" as the

Working on developing a browser extension using the WebExtension API in FireFox. Currently, while writing a content script, I've encountered an issue where any DOM object passed into console.log results in the string <unavailable> being displaye ...

Focus on targeting each individual DIV specifically

Is there a way to toggle a specific div when its title is clicked? I have tried the following code snippet: $( '.industrial-product' ).find('.show-features').click(function() { $('.industrial-product').find('.ip-feat ...

How do I effectively replace content with a banner or alert message for users experiencing issues with the website on Internet Explorer?

mysite seems to be struggling with displaying 3D content on Internet Explorer compared to Chrome. Is there a way I can replace the content with a banner or alert informing users that they will have a better experience using Chrome or other supported browse ...

Understanding how to utilize jQuery or prototype to interpret onclick event containing "setLocation(...)" can enhance the functionality

I am dealing with a specific tag: <button onclick="setLocation('http://mysite.com/checkout/cart/add/product/17/')" /> My goal is to trigger an ajax request when the button is clicked. I want to extract the URL segment "product/17" and app ...

Using HTML/CSS to create custom styled boxes for reusability in Bookdown

I'm looking to create reusable sections for my bookdown project. Successes: I've created a style.css including: .titeldefbox{ display: flex; width: 100%; left: 0; top: 0; position: absolute; background-color: darkslategray; height: ...

Angular's separate scope variables allow for isolated data manipulation within individual components

Struggling with one scope variable affecting multiple elements in a div in AngularJS. Looking for help as a beginner in handling this issue. For a clearer understanding, here's an example: JS: /* controller-home.js ********************************* ...

What is the correct way to encode this URL using JavaScript or jQuery?

My goal is to properly encode the link below, but I seem to be encountering an issue with the "#" symbol: var text = "hello, how are you? &am fine"; var link = "http://example.com/test#zzzzzzzzz"; url = "http://twitter.com/share?url=" + link + "& ...

What could be the reason for the sudden malfunction of the .tabs li:hover property on my

I'm having trouble with the :hover effect not working on my .tabs li element. I've tried commenting out my jQuery script, but it still won't apply. Can anyone help me figure out why? Here's a JSFiddle link for reference: https://jsfidd ...

Can someone assist me in setting a maximum height for the autocomplete list items?

Working on an HTML autocomplete box example, I have it functioning well. I am now seeking guidance on setting a height for the autocomplete items to prevent them from becoming too lengthy when there are numerous items. I would like to establish a max-heigh ...

The GitHub Pages website is not displaying exactly like it does when running locally with a live server

Currently in the process of creating a compact website where users can input an anagram and receive all potential words that can be formed from it. Additionally, upon receiving these words, there is an option to click on one for its definitions. The site ...

Semantic UI table with rowspan feature

I am a beginner with semantic ui and I have been trying to replicate this specific layout. While going through semantic ui's documentation, I found something similar but not quite identical. Below is the HTML code: <div class="ui celled grid cont ...

Using a dynamic HTML interface, select from a vast array of over 50,000 values by leveraging the power

I am working on a project that includes a multiselect option with over 50,000 records. We are using AJAX to fetch data from the server based on user searches, which works fine. However, when a user tries to select all records by clicking the "check all" ...