Decimal Counter Animation Fix

User
jQuery(document).ready(function($) {
$('.elementor-counter-number').css('opacity', 0); // Hide numbers initially
$('.elementor-counter').each(function(index) {
var counter = $(this);
var countTo = parseFloat(counter.find('.elementor-counter-number').data('to-value')); // Get the target value as a float
counter.find('.elementor-counter-number').text(0); // Set the initial count to zero

// Start counting with a delay
setTimeout(function() {
counter.find('.elementor-counter-number').css('opacity', 1); // Make the number visible
$({ countNum: 0 }).animate({ countNum: countTo }, {
duration: 2000, // Duration for counting
easing: 'linear',
step: function() {
// Display the number with decimals
counter.find('.elementor-counter-number').text(this.countNum.toFixed(2));
},
complete: function() {
// Ensure it shows the final value with decimals
counter.find('.elementor-counter-number').text(this.countNum.toFixed(2));
}
});
}, index * 1500); // Delay between each counter
});
});
apply decimal to decimal counter only

Assistant
To ensure decimals are applied only to counters that have a decimal target value, you can add a check for the presence of a decimal in the `countTo` value. If `countTo` has no decimals, use integer formatting.

Here's the updated code:

jQuery(document).ready(function($) {
    $('.elementor-counter-number').css('opacity', 0); // Hide numbers initially
    $('.elementor-counter').each(function(index) {
        var counter = $(this);
        var countTo = parseFloat(counter.find('.elementor-counter-number').data('to-value')); // Get the target value as a float
        var hasDecimals = countTo % 1 !== 0; // Check if the number has decimals
        counter.find('.elementor-counter-number').text(0); // Set the initial count to zero

        // Start counting with a delay
        setTimeout(function() {
            counter.find('.elementor-counter-number').css('opacity', 1); // Make the number visible
            $({ countNum: 0 }).animate({ countNum: countTo }, {
                duration: 2000, // Duration for counting
                easing: 'linear',
                step: function() {
                    // Display the number with or without decimals
                    counter.find('.elementor-counter-number').text(
                        hasDecimals ? this.countNum.toFixed(2) : Math.floor(this.countNum)
                    );
                },
                complete: function() {
                    // Ensure it shows the final value with or without decimals
                    counter.find('.elementor-counter-number').text(
                        hasDecimals ? this.countNum.toFixed(2) : Math.floor(this.countNum)
                    );
                }
            });
        }, index * 1500); // Delay between each counter
    });
});

### Key Changes:
1. **Check for Decimals**:
– Added a `hasDecimals` variable to determine if `countTo` contains decimal points.

2. **Formatting Based on `hasDecimals`**:
– Used `.toFixed(2)` for numbers with decimals.
– Used `Math.floor()` for integers to avoid decimal display.

This way, only decimal counters will show decimal values during the animation, while integer counters will display whole numbers.

User
but apply above js when in counter viewport
Assistant
To execute the animation only when the counter enters the viewport, you can use the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). Here's the updated JavaScript code:

### Updated Code:

jQuery(document).ready(function($) {
    $('.elementor-counter-number').css('opacity', 0); // Hide numbers initially

    // Function to animate the counter
    function animateCounter(counter) {
        var countTo = parseFloat(counter.find('.elementor-counter-number').data('to-value')); // Get the target value as a float
        var hasDecimals = countTo % 1 !== 0; // Check if the number has decimals
        counter.find('.elementor-counter-number').text(0); // Set the initial count to zero

        counter.find('.elementor-counter-number').css('opacity', 1); // Make the number visible
        $({ countNum: 0 }).animate({ countNum: countTo }, {
            duration: 2000, // Duration for counting
            easing: 'linear',
            step: function() {
                // Display the number with or without decimals
                counter.find('.elementor-counter-number').text(
                    hasDecimals ? this.countNum.toFixed(2) : Math.floor(this.countNum)
                );
            },
            complete: function() {
                // Ensure it shows the final value with or without decimals
                counter.find('.elementor-counter-number').text(
                    hasDecimals ? this.countNum.toFixed(2) : Math.floor(this.countNum)
                );
            }
        });
    }

    // Use Intersection Observer to trigger animation when in viewport
    var observer = new IntersectionObserver(function(entries) {
        entries.forEach(function(entry) {
            if (entry.isIntersecting) {
                var counter = $(entry.target);
                if (!counter.hasClass('animated')) { // Avoid re-animation
                    counter.addClass('animated');
                    animateCounter(counter);
                }
            }
        });
    }, {
        threshold: 0.5 // Trigger when 50% of the element is visible
    });

    // Observe each counter
    $('.elementor-counter').each(function() {
        observer.observe(this);
    });
});

### Explanation:

1. **Intersection Observer**:
– Monitors when `.elementor-counter` elements enter or exit the viewport.

2. **Avoid Re-animation**:
– Added a check with the `animated` class to ensure each counter animates only once.

3. **Animation Trigger**:
– Animates the counter only when at least 50% of it is visible (`threshold: 0.5`).

4. **Performance**:
– Using `IntersectionObserver` is more efficient than continuously checking with scroll events.

This ensures the counters animate only when they are visible in the viewport, enhancing performance and user experience.

Leave a Comment

Your email address will not be published. Required fields are marked *