1

On my website I have an automatic counter that retrieves the data from my database, now to make it more beautiful graphically I would like to use a script that simulates the split flap display like that in the airfields or train station.

I'm using the following script taken from http://www.jqueryscript.net/text/Airport-Like-Text-Flip-Animation-with-jQuery-CSS3-splitFlap.html

JS

/* Split Flap Display for jQuery */
(function ($) {
 'use strict';

 /***************************************************************************
  * Tools
  **************************************************************************/

  // Date.now
 Date.now = (Date.now || function () {
  return (new Date()).getTime();
 });

 (function () {
  var lastTime = 0;
  var vendors = [ 'ms', 'moz', 'webkit', 'o' ];

  for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x ) {
   window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
   window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
  }

  if ( window.requestAnimationFrame === undefined ) {
   window.requestAnimationFrame = function (callback, element) {
    var currTime = Date.now(), timeToCall = Math.max(0, 16 - ( currTime - lastTime ));
    var id = window.setTimeout(function () {
     callback(currTime + timeToCall);
    }, timeToCall);
    lastTime = currTime + timeToCall;
    return id;
   };
  }

  window.cancelAnimationFrame = window.cancelAnimationFrame || function (id) {
   window.clearTimeout(id);
  };
 }());

 // Bind
 var bind = function (method, instance) {
  return function () {
   return method.apply(instance, arguments);
  };
 };

 /***************************************************************************
  * Letter
  **************************************************************************/

 /**
  * Letter Constructor
  *
  * @param settings
  * @constructor
  */
 function Letter(settings) {
  this.settings = settings;

  this.domObject = document.createElement('div');
  $(this.domObject)
    .css({
     '-webkit-transform-style': 'preserve-3d',
     '-moz-transform-style':    'preserve-3d',
     '-ms-transform-style':     'preserve-3d',
     'transform-style':         'preserve-3d'
    })
    .addClass("char");

  this.upperObject = document.createElement('div');
  this.lowerObject = document.createElement('div');
  this.flippingObject = document.createElement('div');

  var c = {
   'background-image': 'url(' + this.settings.image + ')'
  };
  if ( this.settings.imageSize != '' ) {
   c[ 'background-size' ] = this.settings.imageSize;
  }

  $(this.upperObject)
    .width(this.settings.charWidth)
    .height(this.settings.charHeight >> 1)
    .css(c)
    .addClass("upper");

  $(this.lowerObject)
    .width(this.settings.charWidth)
    .height(this.settings.charHeight >> 1)
    .css(c)
    .addClass("lower");

  $(this.flippingObject)
    .width(this.settings.charWidth)
    .height(this.settings.charHeight >> 1)
    .css($.extend(c, {
     'position': 'absolute',
     'left':     0,
     'top':      0
    }))
    .hide()
    .addClass("flipping");

  $(this.domObject).append(this.upperObject);
  $(this.domObject).append(this.lowerObject);
  $(this.domObject).append(this.flippingObject);
 }

 var lp = Letter.prototype;

 /**
  * Return DOM Object
  *
  * @returns {HTMLElement|*}
  */
 lp.getDOMObject = function () {
  return this.domObject;
 }

 lp.getCharOffset = function (someChar) {
  return Math.max(0, this.settings.charsMap.indexOf(someChar));
 }

 /**
  * Set Letter character
  * @param char
  * @param charFrom
  * @param ratio
  */
 lp.setChar = function (someChar, charFrom, ratio) {
  if ( typeof ratio == 'undefined' ) {
   ratio = 1;
  }

  var offset = this.getCharOffset(someChar);

  $(this.upperObject).css({
   'background-position': '-' + (offset * this.settings.charWidth) + 'px 0px'
  });

  if ( ratio >= 1 ) {
   $(this.lowerObject).css({
    'background-position': '-' + (offset * this.settings.charWidth) + 'px -' + (this.settings.charHeight >> 1) + 'px'
   });
   $(this.flippingObject).hide();
  }
  else if ( ratio <= 0 ) {
   var offsetFrom = this.getCharOffset(charFrom);
   $(this.upperObject).css({
    'background-position': '-' + (offsetFrom * this.settings.charWidth) + 'px 0px'
   });
   $(this.lowerObject).css({
    'background-position': '-' + (offsetFrom * this.settings.charWidth) + 'px -' + (this.settings.charHeight >> 1) + 'px'
   });
   $(this.flippingObject).hide();
  }
  else {
   var offsetFrom = this.getCharOffset(charFrom);
   $(this.lowerObject).css({
    'background-position': '-' + (offsetFrom * this.settings.charWidth) + 'px -' + (this.settings.charHeight >> 1) + 'px'
   });

   var d;
   if ( ratio < 0.5 ) {
    d = (90 * 2 * ratio);
    $(this.flippingObject)
      .css({
       'top':                      0,
       'z-index':                  Math.round(d),
       '-webkit-transform':        'rotateX(-' + d + 'deg)',
       '-moz-transform':           'rotateX(-' + d + 'deg)',
       '-ms-transform':            'rotateX(-' + d + 'deg)',
       'transform':                'rotateX(-' + d + 'deg)',
       '-webkit-transform-origin': 'bottom center 0',
       '-moz-transform-origin':    'bottom center 0',
       '-ms-transform-origin':     'bottom center 0',
       'transform-origin':         'bottom center 0',
       'background-position':      '-' + (offsetFrom * this.settings.charWidth) + 'px 0px'
      });
   }
   else {
    d = (90 * 2 * (1 - ratio));
    $(this.flippingObject)
      .css({
       'top':                      (this.settings.charHeight >> 1) + 'px',
       'z-index':                  Math.round(d),
       '-webkit-transform':        'rotateX(' + d + 'deg)',
       '-moz-transform':           'rotateX(' + d + 'deg)',
       '-ms-transform':            'rotateX(' + d + 'deg)',
       'transform':                'rotateX(' + d + 'deg)',
       '-webkit-transform-origin': 'top center 0',
       '-moz-transform-origin':    'top center 0',
       '-ms-transform-origin':     'top center 0',
       'transform-origin':         'top center 0',
       'background-position':      '-' + (offset * this.settings.charWidth) + 'px -' + (this.settings.charHeight >> 1) + 'px'
      });
   }
   $(this.flippingObject).show();
  }
 }

 /***************************************************************************
  * SplitFlap
  **************************************************************************/

 function SplitFlap(settings) {
  this.settings = settings;

  this.domObject = document.createElement('div');
  this.letters = new Array();

  $(this.domObject).addClass("splitflap");
 }

 var sp = SplitFlap.prototype;

 sp.getDOMObject = function () {
  return this.domObject;
 }

 sp.build = function (size) {
  $(this.domObject)
    .css({
     position: 'relative'
    })
    .width(size * this.settings.charWidth)
    .height(this.settings.charHeight);

  for ( var i = 0; i < size; i++ ) {
   var letter = new Letter(this.settings);

   var o = letter.getDOMObject();
   $(o).css({
    position: 'absolute',
    left:     i * this.settings.charWidth,
    top:      0
   });

   $(this.domObject).append(o);

   this.letters[ i ] = letter;
  }
 }

 sp.setText = function (text, textFrom) {
  var animated;
  if ( typeof textFrom == 'undefined' ) {
   textFrom = text;
   animated = false;
  }
  else {
   animated = true;
  }

  // Normalize text
  while ( textFrom.length < this.letters.length ) {
   if ( this.settings.padDir == 'left' ) {
    textFrom = textFrom + this.settings.padChar;
   }
   else {
    textFrom = this.settings.padChar + textFrom;
   }
  }

  // Initialise display
  var charsFrom = (new String(textFrom)).split("");

  for ( var i = 0, l = this.letters.length; i < l; i++ ) {
   var letter = this.letters[ i ];
   letter.setChar(charsFrom[ i ]);
  }

  // Animation
  if ( animated ) {
   // Normalize text
   while ( text.length < this.letters.length ) {
    if ( this.settings.padDir == 'left' ) {
     text = text + this.settings.padChar;
    }
    else {
     text = this.settings.padChar + text;
    }
   }

   var chars = (new String(text)).split("");

   this.animation = {
    letters: new Array()
   };

   for ( var i = 0, l = this.letters.length; i < l; i++ ) {
    var al = {
     ratio:   0,
     speed:   this.settings.speed + Math.random() * this.settings.speedVariation,
     letters: new Array(charsFrom[ i ])
    };

    var index = this.letters[ i ].getCharOffset(charsFrom[ i ]);
    while ( this.settings.charsMap.charAt(index) != chars[ i ] ) {
     index = (index + 1) % this.settings.charsMap.length;
     al.letters.push(this.settings.charsMap.charAt(index));
    }

    this.animation.letters[ i ] = al;
   }

   if ( this.settings.autoplay ) {
    this.animate();
   }
  }
 }

 sp.animate = function () {
  var t = new Date().getTime();
  if ( typeof this.animation.time == 'undefined' ) {
   this.animation.time = t;
  }

  var dt = 0.001 * (t - this.animation.time);

  var n = 0;

  for ( var i = 0, l = this.animation.letters.length; i < l; i++ ) {
   var letter = this.letters[ i ];
   var al = this.animation.letters[ i ];

   if ( al.letters.length > 1 ) {
    al.ratio += al.speed * dt;
    if ( (al.ratio > 1) && (al.letters.length > 1) ) {
     al.ratio = 0;
     al.letters.shift();
    }
   }

   if ( al.letters.length > 1 ) {
    letter.setChar(al.letters[ 1 ], al.letters[ 0 ], al.ratio);
    n++;
   }
   else {
    letter.setChar(al.letters[ 0 ]);
   }
  }

  this.animation.time = t;

  if ( n > 0 ) {
   requestAnimationFrame(bind(this.animate, this));
  }
  else {
   this.settings.onComplete(this);
  }
 }

 /***************************************************************************
  * jQuery
  **************************************************************************/

 $.fn.splitFlap = function (options) {
  if ( options == 'splitflap' ) {
   if ( this.length < 0 ) {
    return false;
   }

   var o = this.get(0);
   if ( typeof o.splitflap == 'undefined' ) {
    return false;
   }

   return o.splitflap;
  }

  var settings = $.extend({
   image:          '/img/char/numbers.png',
   imageSize:      '',
   charsMap:       '0123456789',
   charWidth:      50,
   charHeight:     100,
   charSubstitute: ' ',
   padDir:         'left',
   padChar:        ' ',
   speed:          3,
   speedVariation: 2,
   text:           '',
   textInit:       '',
   autoplay:       true,
   onComplete:     function () {}
  }, options);

  return this.each(function () {
   var text = (new String(settings.text)).toUpperCase();
   if ( text == '' ) {
    text = (new String($(this).html())).toUpperCase();
   }

   // Verify chars
   for ( var i = 0, l = text.length; i < l; i++ ) {
    var c = text.charAt(i);
    if ( settings.charsMap.indexOf(c) < 0 ) {
     text = text.replace(c, settings.charSubstitute);
    }
   }

   var textInit = settings.textInit.toUpperCase().substr(0, text.length);
   // Verify chars
   for ( var i = 0, l = textInit.length; i < l; i++ ) {
    var c = textInit.charAt(i);
    if ( settings.charsMap.indexOf(c) < 0 ) {
     textInit = textInit.replace(c, settings.charSubstitute);
    }
   }

   while ( textInit.length < text.length ) {
    textInit = textInit + settings.charsMap.charAt(Math.floor(settings.charsMap.length * Math.random()));
   }

   this.splitflap = new SplitFlap(settings);
   this.splitflap.build(text.length);

   $(this).empty().append(this.splitflap.getDOMObject());

   this.splitflap.setText(text, textInit);
  });
 };
})(jQuery);

So this is my function that count from DB

my.js

(function( my ) {
 "use strict";

 // Namespace trick explained here: http://stackoverflow.com/a/5947280/587641
 // For a public property or function, use "sin.xxx = ..."
 // For a private property use "var xxx = "
 // For a private function use "function xxx(..."

 /**
 *Assign a value to the counter, adding digits to the left when they are serving,
  * And resetting the digits to the left when they are no longer needed, but without removing them.
 */
 sin2.setCounter = function(value) {
  var i = 0;
  var stringValue = '' + value;
  var valueDigitNum = stringValue.length; // Number of digits in value
  var htmlDigitNum = $('.example .splitflap').length; // Number of digits in HTML
  // I enter the digits missing for millions in HTML
  for (i = htmlDigitNum; i < valueDigitNum; i++) {
   $('.example').prepend($('.example .splitflap').first().clone());
   htmlDigitNum++;
  }
  // I value the figures, putting zeros on the left
  var htmlDigits = $('.example .splitflap');
  for (i=0; i<htmlDigitNum; i++) {
   var htmlIndex = i;
   var valueIndex = valueDigitNum-htmlDigitNum+i;
   var digit = '0';
   if (valueIndex>=0) {
    digit = stringValue[valueIndex];
   }
   htmlDigits.eq(htmlIndex).text(digit);
  }
 }

}( window.my = window.my || {} ));

Old counter that i want to change

<div class="Counter">
  <div th:each="digit,status : ${@commonData.counterDigits}" 
   th:with="pos=${status.size-status.count}" 
   class="Box img-responsive" 
   th:classappend="${pos>5?'BoxMilions':pos>2?'BoxThousand':'BoxHundreds'}">    
         <span class="Count" th:text="${digit}">.</span>
     </div>
 </div>

New counter split flap

<script yada:src="@{/res/js/jquery.splitflap.js}" type="text/javascript"></script>
    <script type="text/javascript" th:inline="javascript">
        (function ($) {
            $(document).ready(function () {
                $('.do-splitflap')
                        .splitFlap();
            });
        })(jQuery);
</script>
<div class="example">
 <div th:each="digit,status : ${@commonData.counterDigits}" 
    class="splitflap img-responsive"> 
 <span class="do-splitflap" th:text="${digit}">.</span></div>
</div>
    
    

If I try to use the new counter when the counter number is updated the css style does not work.

OLD CSS

.Box {
 background-size: cover;
 -moz-background-size: cover;
 -webkit-background-size: cover;
    text-align: center;
 float: left;
    width: 4em;
 margin: 2px auto;
 border-style:solid;
 border-color:#EFF6F0; 
}

.BoxMilions{
 background:url(images/counter/milions.jpg) no-repeat;
    background-size: cover;
}

.BoxThousands {
 background:url(images/counter/thou.jpg) no-repeat;
    background-size: cover;
}

.BoxHundreds {
 background:url(images/counter/hundr.jpg) no-repeat;
    background-size: cover;
}

.Count {
 font-family: sans-serif;
   font-size: 97px;
 background-color: transparent;
 text-align: center;
 width: 100%;
 color: #fff;
 line-height: 104%;
}

NEW CSS

.example {
            padding: 0 0 10px;
            margin-bottom: 20px;
        }

.splitflap {
            margin: 0 auto;
            float: left;
            padding-right: 5px;
            

            -webkit-perspective-origin: top center;
            -moz-perspective-origin: top center;
            -ms-perspective-origin: top center;
            perspective-origin: top center;

            -webkit-perspective: 900px;
            -moz-perspective: 900px;
            -ms-perspective: 900px;
            perspective: 900px;
        }

The animation after the first update of the number disappears and only the numbers are continuously updated.

Any solution for use split flap correctly?

peppe71-19
  • 212
  • 1
  • 5
  • 20

0 Answers0