3

I have simple code to run countdown, my phtml file Counter.phtml

<script>
    require([
        'jquery',
        'circularCountDown',
        'domReady!'
    ], function($) {
        $('.timer').circularCountDown({

            size: 250,
            borderSize: 10,
            colorCircle: '#de7546',
            background: '#947769',
            fontFamily: 'sans-serif',
            fontColor: '#333333',
            fontSize: 20,
            delayToFadeIn: 0,
            delayToFadeOut: 0,
            reverseLoading: false,
            reverseRotation: false,
            duration: {
                hours: 5,
                minutes: 0,
                seconds: 0
            },
            beforeStart: function(){},
            end: function(){}
        });

    });
</script>

circular-countdown.js

define([
    'jquery'
], function($) {

    var defaultOptions = {
        size: 60,
        borderSize: 10,
        colorCircle: 'gray',
        background: 'white',
        fontFamily: 'sans-serif',
        fontColor: '#333333',
        fontSize: 16,
        delayToFadeIn: 0,
        delayToFadeOut: 0,
        reverseLoading: false,
        reverseRotation: false,
        duration: {
            hours: 0,
            minutes: 0,
            seconds: 10
        },
        beforeStart: function(){},
        end: function(){}
    };

    $.fn.circularCountDown = function (options) {
        new CircularCountDown(options, $(this));
    };

    function CircularCountDown(data, element) {
        this.element = element;
        this.data = jQuery.extend(true, defaultOptions, data);
        this.init();
    }

    CircularCountDown.prototype = {

        init: function () {
            this.formatData();
            this.draw();
            this.start();
        },
        start: function () {
            if (typeof this.data.beforeStart == "function") {
                this.data.beforeStart(this);
            }
            this.show();
            this.starDecrementTimeEvent();
            var time = this.getFormattedTimeByCircle();
            this.animate(time);
        },
        animate: function(time) {
            var that = this;

            if (!that.data.reverseLoading) {
                that.wrapperCircles.css(
                    'clip',
                    'rect(0px, ' + that.data.size + 'px, ' + that.data.size + 'px, ' + (that.data.size / 2) + 'px)'
                );
                that.rotate(that.circlelRight, 0, 180, time, function () {
                    that.wrapperCircles.css('clip', 'rect(auto, auto, auto, auto)');
                    that.rotate(that.circlelLeft, 180, 360, time);
                });
            } else {
                that.rotate(that.circlelRight, 180, 360, time, function() {
                    that.wrapperCircles.css(
                        'clip',
                        'rect(0px, ' + (that.data.size / 2) + 'px, ' + that.data.size + 'px, 0px)'
                    );
                    that.rotate(that.circlelLeft, 0, 180, time);
                    that.rotate(that.circlelRight, 0, 180, time);
                });
            }
        },
        formatData: function () {
            this.time =
                this.data.duration.seconds +
                (this.data.duration.minutes * 60) +
                (this.data.duration.hours * 3600)
            ;
            this.data.size = parseInt(this.data.size);
            this.data.borderSize = parseInt(this.data.borderSize);

            if (this.data.borderSize % 2 != 0) {
                this.data.borderSize++;
            }

            if (this.data.size % 2 != 0) {
                this.data.size++;
            }
        },
        draw: function () {
            this.hide();
            this.circlelLeft = this.drawCircle().addClass('coutndown-circle-left');
            this.circlelRight = this.drawCircle().addClass('coutndown-circle-right');
            this.wrapperCircles = $('<div>')
                .addClass('coutndown-wrapper')
                .css({
                    'width': this.data.size + 'px',
                    'height': this.data.size + 'px',
                    'position': 'absolute',
                })
                .append(this.circlelLeft)
                .append(this.circlelRight)
            ;
            this.wrapperTime = this.drawTime();
            this.element
                .css({
                    'position': 'relative',
                    'box-sizing': 'content-box'
                })
                .append(this.wrapperCircles)
                .append(this.wrapperTime)
            ;
            this.setTime(this.getStringTime(this.time));
        },
        drawCircle: function () {
            var size = this.data.size - (this.data.borderSize * 2);
            size += 'px';
            return $('<div>')
                .addClass('coutndown-circle')
                .css({
                    'width': size,
                    'height': size,
                    'border': this.data.borderSize + 'px solid ' + this.data.colorCircle,
                    '-moz-border-radius': this.data.size + 'px',
                    '-webkit-border-radius': this.data.size + 'px',
                    '-o-border-radius': this.data.size + 'px',
                    '-ms-border-radius': this.data.size + 'px',
                    'border-radius': this.data.size + 'px',
                    'box-sizing': 'content-box',
                    'background-color': this.data.background,
                    'position': 'absolute',
                    'clip': 'rect(0px, ' + (this.data.size / 2) + 'px, ' + this.data.size + 'px, 0px)'
                });
        },
        rotate: function (elem, startAngle, endAngle, duration, complete) {
            $({deg: startAngle}).animate({deg: endAngle}, {
                duration: duration,
                easing: 'linear',
                step: function (now) {
                    elem.css({
                        '-moz-transform': 'rotate(' + now + 'deg)',
                        '-webkit-transform': 'rotate(' + now + 'deg)',
                        '-o-transform': 'rotate(' + now + 'deg)',
                        '-ms-transform': 'rotate(' + now + 'deg)',
                        'transform': 'rotate(' + now + 'deg)'
                    });
                },
                complete: complete || $.noop
            });
        },
        hide: function () {
            this.element.fadeOut(this.data.delayToFadeOut);
            this.visible = false;
        },
        show: function () {
            this.element.fadeIn(this.data.delayToFadeIn);
            this.visible = true;
        },
        isVisible: function () {
            return this.visible;
        },
        getStringTime: function (time) {
            var duration = this.secondsToTime(time);

            if (duration.h > 0) {
                return this.addDigit(duration.h) + ':' + this.addDigit(duration.m) + ':' + this.addDigit(duration.s);
            }
            if (duration.m > 0) {
                return this.addDigit(duration.m) + ':' + this.addDigit(duration.s);
            }
            return this.addDigit(duration.s);
        },
        addDigit: function (number) {
            return ("0" + number).slice(-2);
        },
        secondsToTime: function (time) {
            var hours = Math.floor(time / (60 * 60));
            var divisor_for_minutes = time % (60 * 60);
            var minutes = Math.floor(divisor_for_minutes / 60);
            var divisor_for_seconds = divisor_for_minutes % 60;
            var seconds = Math.ceil(divisor_for_seconds);
            return {
                h: hours,
                m: minutes,
                s: seconds
            };
        },
        getFormattedTimeByCircle: function () {
            var time = this.time / 2 * 1000;

            if (time % 2 != 0) {
                time++;
            }
            return time;
        },
        starDecrementTimeEvent: function () {
            var that = this;
            this.decrementTimeEvent = setInterval(function () {
                that.time -= 1;
                that.setTime(that.getStringTime(that.time));
                if (that.time <= 0) {
                    that.time = 0;
                    that.stopDecrementTimeEvent();

                    if (typeof that.data.end == "function") {
                        that.data.end(that);
                    }
                }
            }, 1000);
        },
        stopDecrementTimeEvent: function () {
            clearInterval(this.decrementTimeEvent);
        },
        drawTime: function () {
            return $('<div>')
                .addClass('coutndown-wrapper-time')
                .css({
                    'position': 'absolute',
                    'height': this.data.size + 'px',
                    'width': this.data.size + 'px',
                    'line-height': this.data.size + 'px',
                    'text-align': 'center',
                    'font-size': this.data.fontSize + 'px',
                    'font-family': this.data.fontFamily,
                    'color': this.data.fontColor
                })
                ;
        },
        setTime: function (time) {
            this.wrapperTime.html(time);
        },
        destroy: function() {
            this.hide();
            this.element.html('').attr('style', null);
            this.circlelLeft = null;
            this.circlelRight = null;
            this.wrapperCircles = null;
            this.element = null;
            this.data = defaultOptions;
        }
    };
    return CircularCountDown;
});

I want add button in admin to reset this counter? How can I do this?

1
  • I might be missing something but in order to reset the countdown, the front-end needs to be fetching some configuration/value dynamically from the back-end. That doesn't appear to be the case at the moment. Feb 15, 2018 at 3:00

1 Answer 1

2
+50

What I would do is:

Create a settings page and add a text field to your modules, heres a tutorial on creating a settings page. That one shows you how to use a text field, which should be fine, but I believe you can also add a hidden field which would change some things later on.

Next, you need to get the setting to your phtml file. You may need to first create a Helper or add a function to your block like below to get the value for your setting.

Create a Helper class

<?php
namespace Vendor\Module\Helper;

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    public function getConfig($config_path)
    {
        return $this->scopeConfig->getValue(
            $config_path,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
    }
}

Inject the Helper into your Counter.phtml block.

If your phtml file is not using a custom block create one like so:

<?php
namespace VendorName\ModuleName\Block;
class Counter extends \Magento\Framework\View\Element\Template
{
    /**
     * @var VendorName\ModuleName\Helper\Data
     */
    protected $dataHelper;

    public function __construct(
        \VendorName\ModuleName\Helper\Data $dataHelper,
        \Magento\Framework\View\Element\Template\Context $context
    )
    {
        $this->dataHelper = $dataHelper;
        parent::__construct($context);
    }

    public function getCounterResetTime(){
        // 'modulename/counter/counterreset' is defined in your system.xml
        // and may be different
        $counterreset = $this->dataHelper->getConfig('modulename/counter/counterreset');

        return intVal($counterreset);
    }

    public function getSettingsJson()
    {

        $data = [ 

            "counterResetTime" => $this->getCounterResetTime()

            // optionally add more settings here if you want

        ];
        return json_encode($data);
    }
}

The code above also added a function to get the data which will be used later. In the xml file referencing the Block, change the class to your block if necessary.

<block class="VendorName\ModuleName\Block\Counter" name="counter" template="VendorName_ModuleName::Counter.phtml" />

If you already had a Block defined, add the getSettingsJson() function and inject our \VendorName\ModuleName\Helper\Data object as I did above in the Counter block.

Using the getSettingsJson() function, retrieve the value in Counter.phtml. Because the script is using hours, minutes and seconds, you'll need to convert the setting to this format.

<script>
...
    ], function($) {

        var moduleSettings = <?php echo $block->getSettingsJson(); ?>;
        var now = Date.now();

        var reset_time_setting = new Date(moduleSettings['counterResetTime']);
        var counterreset = reset_time_setting.getTime();  

        // add 5 hours 
        // (or however much time in milliseconds you want the counter to be in the future)

        counterreset = counterreset + (5 * 60 * 60 * 1000);

        var diff = reset_time_setting - now;

        // You'll probably only want to use the counter if
        // the time difference is more than a second (maybe more)
        if ( diff > 1000) {
            var seconds = Math.ceil( diff / 1000 ) % 60;
            var minutes = Math.floor( diff / 60000 ) % 60;
            var hours = Math.floor( diff / 3600000 );
            var duration = { 
                hours : hours,
                minutes : minutes,
                seconds : seconds
            };
            // then you can feed the ciruclarCountDown your duration
                //... 
                $('.timer').circularCountDown({
                //... 
                    duration: duration,
                // ...
        } else {
            // you may want to do something different if the time has
            // already been reached
        }

You stated that you wanted to have a button reset the counter, heres how you can do that.

Basically you'll want to add custom JS to your settings page, and then use the JS to hide the text input setting you created earlier (unless of course, you figured out how to add a hidden field!).

Using the JS, you can also add a button. Try this link which is about creating validation in your settings page. We don't need validation, but it shows you how to inject JS to the settings page created in our first step.

The js for the button's would look something like this:

// add code to hide the input element if necessary
// add code to create the button and insert into DOM

// select the input/hidden field we created (I'm not sure what the id will be)
var dateTime = $('#counterResetTime');

var setCounterResetTime() = function(){
    var now = new Date();
    time = now.getTime()
    dateTime.val(time);
}
// where #myButton is a button you created and inserted into DOM
$('#myButton').on("click", function(event)){
    setCounterResetTime();
    event.preventDefault();
}

After clicking the button your setting field will be populated with a current unix timestamp in milliseconds, which will work with the rest of the code. If you then save the settings page, you would have to clear all caches (or just config) and the countdown should work from when you clicked the button.

After all of this php bin/magento setup:upgrade will be neccessary.

Additional References

Edits - I think I've cleaned up my answer to a stable point, let me know how it goes.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.