Search code examples
javascriptcountdowntimer

Include Countdown Timer multiple times


I found this countdown timer that I would like to include in one of my modules. Unfortunately I can't figure out how to use the timer multiple times on one page (the classic one - only the last timer is running) - I also know that probably (?) the go function should be the reason - but currently I can't figure out how to instantiate the timer.

What I have so far (but it does not work):

Codepen Original: https://codepen.io/l422y/pen/cdwhm

I have in-depth JS basic knowledge but this is still beyond my horizon.

var ringer = {
  //countdown_to: "10/31/2014",
  //countdown_to: targetdaytime,
  rings: {
    'DAYS': {
      s: 86400000, // mseconds in a day,
      max: 365
    },
    'HOURS': {
      s: 3600000, // mseconds per hour,
      max: 24
    },
    'MINUTES': {
      s: 60000, // mseconds per minute
      max: 60
    },
    'SECONDS': {
      s: 1000,
      max: 60
    },
    'MICROSEC': {
      s: 10,
      max: 100
    }
  },
  r_count: 5,
  r_spacing: 10, // px
  r_size: 100, // px
  r_thickness: 2, // px
  update_interval: 11, // ms


  init: function(container, targetdaytime) {

    $r = ringer;
    $r.cvs = document.createElement('canvas');

    $r.size = {
      w: ($r.r_size + $r.r_thickness) * $r.r_count + ($r.r_spacing * ($r.r_count - 1)),
      h: ($r.r_size + $r.r_thickness)
    };

    $r.cvs.setAttribute('width', $r.size.w);
    $r.cvs.setAttribute('height', $r.size.h);
    $r.ctx = $r.cvs.getContext('2d');
    $(container).append($r.cvs);
    $r.cvs = $($r.cvs);
    $r.ctx.textAlign = 'center';
    $r.actual_size = $r.r_size + $r.r_thickness;
    $r.countdown_to_time = new Date(targetdaytime).getTime();
    $r.cvs.css({
      width: $r.size.w + "px",
      height: $r.size.h + "px"
    });
    $r.go();
  },
  ctx: null,
  go: function() {
    var idx = 0;

    $r.time = (new Date().getTime()) - $r.countdown_to_time;


    for (var r_key in $r.rings) $r.unit(idx++, r_key, $r.rings[r_key]);

    setTimeout($r.go, $r.update_interval);
  },
  unit: function(idx, label, ring) {
    var x, y, value, ring_secs = ring.s;
    value = parseFloat($r.time / ring_secs);
    $r.time -= Math.round(parseInt(value)) * ring_secs;
    value = Math.abs(value);

    x = ($r.r_size * .5 + $r.r_thickness * .5);
    x += +(idx * ($r.r_size + $r.r_spacing + $r.r_thickness));
    y = $r.r_size * .5;
    y += $r.r_thickness * .5;


    // calculate arc end angle
    var degrees = 360 - (value / ring.max) * 360.0;
    var endAngle = degrees * (Math.PI / 180);

    $r.ctx.save();

    $r.ctx.translate(x, y);
    $r.ctx.clearRect($r.actual_size * -0.5, $r.actual_size * -0.5, $r.actual_size, $r.actual_size);

    // first circle
    $r.ctx.strokeStyle = "rgba(128,128,128,0.2)";
    $r.ctx.beginPath();
    $r.ctx.arc(0, 0, $r.r_size / 2, 0, 2 * Math.PI, 2);
    $r.ctx.lineWidth = $r.r_thickness;
    $r.ctx.stroke();

    // second circle
    $r.ctx.strokeStyle = "rgba(253, 128, 1, 0.9)";
    $r.ctx.beginPath();
    $r.ctx.arc(0, 0, $r.r_size / 2, 0, endAngle, 1);
    $r.ctx.lineWidth = $r.r_thickness;
    $r.ctx.stroke();

    // label
    $r.ctx.fillStyle = "#ffffff";

    $r.ctx.font = '12px Helvetica';
    $r.ctx.fillText(label, 0, 23);
    $r.ctx.fillText(label, 0, 23);

    $r.ctx.font = 'bold 40px Helvetica';
    $r.ctx.fillText(Math.floor(value), 0, 10);

    $r.ctx.restore();
  }
}

jQuery(document).ready(function($) {
  new ringer.init('#cd-container-1', '12/24/2020');
  new ringer.init('#cd-container-2', '10/31/2020');
});
body,
html {
  width: 100%;
  height: 100%;
  margin: 0;
}

html {
  display: table;
}

canvas {
  width: 900px;
  height: 200px;
  display: block;
  position: relative;
  background: transparent;
  margin: 40px auto;
}

body {
  background: #000000;
  background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/12399/free-pumpkin-wallpaper-25771-26455-hd-wallpapers.jpg");
  background-position: top center;
  background-size: cover;
  color: #fff;
  margin: 0;
  padding: 0;
  overflow: hidden;
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="cd-container-1"></div>
<div id="cd-container-2"></div>


Solution

  • It is because your object is not a JS class, therefore it is not possible to create instances of it. Its basically a single instance of a class.

    I transformed it to a class for you, so now you can initialize multiple instances of it:

    class Ringer { // <-- make class from it
      rings = { // <-- object properties to class fields
        'DAYS': {
          s: 86400000, // mseconds in a day,
          max: 365
        },
        'HOURS': {
          s: 3600000, // mseconds per hour,
          max: 24
        },
        'MINUTES': {
          s: 60000, // mseconds per minute
          max: 60
        },
        'SECONDS': {
          s: 1000,
          max: 60
        },
        'MICROSEC': {
          s: 10,
          max: 100
        }
      };
      r_count = 5;
      r_spacing = 10; // px
      r_size = 100; // px
      r_thickness = 2; // px
      update_interval = 11; // ms
      ctx = null; 
    
    
      init (container, targetdaytime) { // <-- functions to class methods
    
        this.cvs = document.createElement('canvas'); // <-- just access this instead of reference to the object
    
        this.size = {
          w: (this.r_size + this.r_thickness) * this.r_count + (this.r_spacing * (this.r_count - 1)),
          h: (this.r_size + this.r_thickness)
        };
    
        this.cvs.setAttribute('width', this.size.w);
        this.cvs.setAttribute('height', this.size.h);
        this.ctx = this.cvs.getContext('2d');
        $(container).append(this.cvs);
        this.cvs = $(this.cvs);
        this.ctx.textAlign = 'center';
        this.actual_size = this.r_size + this.r_thickness;
        this.countdown_to_time = new Date(targetdaytime).getTime();
        this.cvs.css({
          width: this.size.w + "px",
          height: this.size.h + "px"
        });
        this.go();
      }
      
      go() {
        var idx = 0;
    
        this.time = (new Date().getTime()) - this.countdown_to_time;
        
        for (var r_key in this.rings) this.unit(idx++, r_key, this.rings[r_key]);
    
        setTimeout(() => this.go(), this.update_interval); // () => this.go() instead of $r.go
      }
      
      unit(idx, label, ring) {
        var x, y, value, ring_secs = ring.s;
        value = parseFloat(this.time / ring_secs);
        this.time -= Math.round(parseInt(value)) * ring_secs;
        value = Math.abs(value);
    
        x = (this.r_size * .5 + this.r_thickness * .5);
        x += +(idx * (this.r_size + this.r_spacing + this.r_thickness));
        y = this.r_size * .5;
        y += this.r_thickness * .5;
    
    
        // calculate arc end angle
        var degrees = 360 - (value / ring.max) * 360.0;
        var endAngle = degrees * (Math.PI / 180);
    
        this.ctx.save();
    
        this.ctx.translate(x, y);
        this.ctx.clearRect(this.actual_size * -0.5, this.actual_size * -0.5, this.actual_size, this.actual_size);
    
        // first circle
        this.ctx.strokeStyle = "rgba(128,128,128,0.2)";
        this.ctx.beginPath();
        this.ctx.arc(0, 0, this.r_size / 2, 0, 2 * Math.PI, 2);
        this.ctx.lineWidth = this.r_thickness;
        this.ctx.stroke();
    
        // second circle
        this.ctx.strokeStyle = "rgba(253, 128, 1, 0.9)";
        this.ctx.beginPath();
        this.ctx.arc(0, 0, this.r_size / 2, 0, endAngle, 1);
        this.ctx.lineWidth = this.r_thickness;
        this.ctx.stroke();
    
        // label
        this.ctx.fillStyle = "#ffffff";
    
        this.ctx.font = '12px Helvetica';
        this.ctx.fillText(label, 0, 23);
        this.ctx.fillText(label, 0, 23);
    
        this.ctx.font = 'bold 40px Helvetica';
        this.ctx.fillText(Math.floor(value), 0, 10);
    
        this.ctx.restore();
      }
    }
    
    jQuery(document).ready(function($) {
      (new Ringer()).init('#cd-container-1', '12/24/2020'); // <-- init new instance with new Ringer() and call init() on that instance
      (new Ringer()).init('#cd-container-2', '10/31/2020');
    });
    body,
    html {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    
    html {
      display: table;
    }
    
    canvas {
      width: 900px;
      height: 200px;
      display: block;
      position: relative;
      background: transparent;
      margin: 40px auto;
    }
    
    body {
      background: #000000;
      background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/12399/free-pumpkin-wallpaper-25771-26455-hd-wallpapers.jpg");
      background-position: top center;
      background-size: cover;
      color: #fff;
      margin: 0;
      padding: 0;
      overflow: hidden;
      display: table-cell;
      vertical-align: middle;
      text-align: center;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="cd-container-1"></div>
    <div id="cd-container-2"></div>

    If you want to read more into classes and how they work, I found a cool little summary here: https://www.digitalocean.com/community/tutorials/js-objects-prototypes-classes