diff --git a/static/css/combo-style.css b/static/css/combo-style.css index 3dcbd96..fdbdf81 100644 --- a/static/css/combo-style.css +++ b/static/css/combo-style.css @@ -815,3 +815,21 @@ div.textcell.transition { color: #eee; } } + +span#counter { + position: absolute; + bottom: 1em; + right: 0px; + font-size: 60px; + max-width: 6em; + line-height: 100%; + text-align: right; + font-family: Abeezee; + color: #444; +} + +@media(max-width: 900px) { + span#counter { + display: none; + } +} diff --git a/static/js/combo.public.js b/static/js/combo.public.js index b26bdee..8adcd86 100644 --- a/static/js/combo.public.js +++ b/static/js/combo.public.js @@ -16,4 +16,19 @@ $(function() { $ecrans.css('background-position', (offset + pos/2) + 'px 0px'); }); } + if ($('div[id|=chiffres]').length) { + var $chiffres_section = $('div[id|=chiffres]'); + $chiffres_section.css('position', 'relative'); + var total = $chiffres_section.attr('id').split('-')[1]; + var $counter = $('').appendTo($chiffres_section); + var options = { +    useEasing : true, +    useGrouping : true, +    separator : ' ', +    prefix : '', +   suffix : ' usagers' + }; + var count = new CountUp("counter", 1234, parseInt(total), 0, 4.5, options); + $chiffres_section.on('inview', function() { count.start(); }); + } }); diff --git a/static/js/countUp.js b/static/js/countUp.js new file mode 100644 index 0000000..f378668 --- /dev/null +++ b/static/js/countUp.js @@ -0,0 +1,196 @@ +/* + + countUp.js + by @inorganik + +*/ + +// target = id of html element or var of previously selected html element where counting occurs +// startVal = the value you want to begin at +// endVal = the value you want to arrive at +// decimals = number of decimal places, default 0 +// duration = duration of animation in seconds, default 2 +// options = optional object of options (see below) + +var CountUp = function(target, startVal, endVal, decimals, duration, options) { + + // make sure requestAnimationFrame and cancelAnimationFrame are defined + // polyfill for browsers without native support + // by Opera engineer Erik Möller + var lastTime = 0; + var vendors = ['webkit', 'moz', 'ms', '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) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + + // default options + this.options = { + useEasing : true, // toggle easing + useGrouping : true, // 1,000,000 vs 1000000 + separator : ',', // character to use as a separator + decimal : '.' // character to use as a decimal + }; + // extend default options with passed options object + for (var key in options) { + if (options.hasOwnProperty(key)) { + this.options[key] = options[key]; + } + } + if (this.options.separator === '') this.options.useGrouping = false; + if (!this.options.prefix) this.options.prefix = ''; + if (!this.options.suffix) this.options.suffix = ''; + + this.d = (typeof target === 'string') ? document.getElementById(target) : target; + this.startVal = Number(startVal); + this.endVal = Number(endVal); + this.countDown = (this.startVal > this.endVal); + this.frameVal = this.startVal; + this.decimals = Math.max(0, decimals || 0); + this.dec = Math.pow(10, this.decimals); + this.duration = Number(duration) * 1000 || 2000; + var self = this; + + this.version = function () { return '1.6.0'; }; + + // Print value to target + this.printValue = function(value) { + var result = (!isNaN(value)) ? self.formatNumber(value) : '--'; + if (self.d.tagName == 'INPUT') { + this.d.value = result; + } + else if (self.d.tagName == 'text' || self.d.tagName == 'tspan') { + this.d.textContent = result; + } + else { + this.d.innerHTML = result; + } + }; + + // Robert Penner's easeOutExpo + this.easeOutExpo = function(t, b, c, d) { + return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b; + }; + this.count = function(timestamp) { + + if (!self.startTime) self.startTime = timestamp; + + self.timestamp = timestamp; + + var progress = timestamp - self.startTime; + self.remaining = self.duration - progress; + + // to ease or not to ease + if (self.options.useEasing) { + if (self.countDown) { + self.frameVal = self.startVal - self.easeOutExpo(progress, 0, self.startVal - self.endVal, self.duration); + } else { + self.frameVal = self.easeOutExpo(progress, self.startVal, self.endVal - self.startVal, self.duration); + } + } else { + if (self.countDown) { + self.frameVal = self.startVal - ((self.startVal - self.endVal) * (progress / self.duration)); + } else { + self.frameVal = self.startVal + (self.endVal - self.startVal) * (progress / self.duration); + } + } + + // don't go past endVal since progress can exceed duration in the last frame + if (self.countDown) { + self.frameVal = (self.frameVal < self.endVal) ? self.endVal : self.frameVal; + } else { + self.frameVal = (self.frameVal > self.endVal) ? self.endVal : self.frameVal; + } + + // decimal + self.frameVal = Math.round(self.frameVal*self.dec)/self.dec; + + // format and print value + self.printValue(self.frameVal); + + // whether to continue + if (progress < self.duration) { + self.rAF = requestAnimationFrame(self.count); + } else { + if (self.callback) self.callback(); + } + }; + // start your animation + this.start = function(callback) { + self.callback = callback; + self.rAF = requestAnimationFrame(self.count); + return false; + }; + // toggles pause/resume animation + this.pauseResume = function() { + if (!self.paused) { + self.paused = true; + cancelAnimationFrame(self.rAF); + } else { + self.paused = false; + delete self.startTime; + self.duration = self.remaining; + self.startVal = self.frameVal; + requestAnimationFrame(self.count); + } + }; + // reset to startVal so animation can be run again + this.reset = function() { + self.paused = false; + delete self.startTime; + self.startVal = startVal; + cancelAnimationFrame(self.rAF); + self.printValue(self.startVal); + }; + // pass a new endVal and start animation + this.update = function (newEndVal) { + cancelAnimationFrame(self.rAF); + self.paused = false; + delete self.startTime; + self.startVal = self.frameVal; + self.endVal = Number(newEndVal); + self.countDown = (self.startVal > self.endVal); + self.rAF = requestAnimationFrame(self.count); + }; + this.formatNumber = function(nStr) { + nStr = nStr.toFixed(self.decimals); + nStr += ''; + var x, x1, x2, rgx; + x = nStr.split('.'); + x1 = x[0]; + x2 = x.length > 1 ? self.options.decimal + x[1] : ''; + rgx = /(\d+)(\d{3})/; + if (self.options.useGrouping) { + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + self.options.separator + '$2'); + } + } + return self.options.prefix + x1 + x2 + self.options.suffix; + }; + + // format startVal on initialization + self.printValue(self.startVal); +}; + +// Example: +// var numAnim = new countUp("SomeElementYouWantToAnimate", 0, 99.99, 2, 2.5); +// numAnim.start(); +// numAnim.update(135); +// with optional callback: +// numAnim.start(someMethodToCallOnComplete); diff --git a/static/js/jquery.inview.js b/static/js/jquery.inview.js new file mode 100644 index 0000000..362c34b --- /dev/null +++ b/static/js/jquery.inview.js @@ -0,0 +1,147 @@ +/** + * author Christopher Blum + * - based on the idea of Remy Sharp, http://remysharp.com/2009/01/26/element-in-view-event-plugin/ + * - forked from http://github.com/zuk/jquery.inview/ + */ +(function (factory) { + if (typeof define == 'function' && define.amd) { + // AMD + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node, CommonJS + module.exports = factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + var inviewObjects = {}, viewportSize, viewportOffset, + d = document, w = window, documentElement = d.documentElement, expando = $.expando, timer; + + $.event.special.inview = { + add: function(data) { + inviewObjects[data.guid + "-" + this[expando]] = { data: data, $element: $(this) }; + + // Use setInterval in order to also make sure this captures elements within + // "overflow:scroll" elements or elements that appeared in the dom tree due to + // dom manipulation and reflow + // old: $(window).scroll(checkInView); + // + // By the way, iOS (iPad, iPhone, ...) seems to not execute, or at least delays + // intervals while the user scrolls. Therefore the inview event might fire a bit late there + // + // Don't waste cycles with an interval until we get at least one element that + // has bound to the inview event. + if (!timer && !$.isEmptyObject(inviewObjects)) { + timer = setInterval(checkInView, 250); + } + }, + + remove: function(data) { + try { delete inviewObjects[data.guid + "-" + this[expando]]; } catch(e) {} + + // Clear interval when we no longer have any elements listening + if ($.isEmptyObject(inviewObjects)) { + clearInterval(timer); + timer = null; + } + } + }; + + function getViewportSize() { + var mode, domObject, size = { height: w.innerHeight, width: w.innerWidth }; + + // if this is correct then return it. iPad has compat Mode, so will + // go into check clientHeight/clientWidth (which has the wrong value). + if (!size.height) { + mode = d.compatMode; + if (mode || !$.support.boxModel) { // IE, Gecko + domObject = mode === 'CSS1Compat' ? + documentElement : // Standards + d.body; // Quirks + size = { + height: domObject.clientHeight, + width: domObject.clientWidth + }; + } + } + + return size; + } + + function getViewportOffset() { + return { + top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop, + left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft + }; + } + + function checkInView() { + var $elements = [], elementsLength, i = 0; + + $.each(inviewObjects, function(i, inviewObject) { + var selector = inviewObject.data.selector, + $element = inviewObject.$element; + $elements.push(selector ? $element.find(selector) : $element); + }); + + elementsLength = $elements.length; + if (elementsLength) { + viewportSize = viewportSize || getViewportSize(); + viewportOffset = viewportOffset || getViewportOffset(); + + for (; i viewportOffset.top && + elementOffset.top < viewportOffset.top + viewportSize.height && + elementOffset.left + elementSize.width > viewportOffset.left && + elementOffset.left < viewportOffset.left + viewportSize.width) { + visiblePartX = (viewportOffset.left > elementOffset.left ? + 'right' : (viewportOffset.left + viewportSize.width) < (elementOffset.left + elementSize.width) ? + 'left' : 'both'); + visiblePartY = (viewportOffset.top > elementOffset.top ? + 'bottom' : (viewportOffset.top + viewportSize.height) < (elementOffset.top + elementSize.height) ? + 'top' : 'both'); + visiblePartsMerged = visiblePartX + "-" + visiblePartY; + if (!inView || inView !== visiblePartsMerged) { + $element.data('inview', visiblePartsMerged).trigger('inview', [true, visiblePartX, visiblePartY]); + } + } else if (inView) { + $element.data('inview', false).trigger('inview', [false]); + } + } + } + } + + $(w).bind("scroll resize scrollstop", function() { + viewportSize = viewportOffset = null; + }); + + // IE < 9 scrolls to focused elements without firing the "scroll" event + if (!documentElement.addEventListener && documentElement.attachEvent) { + documentElement.attachEvent("onfocusin", function() { + viewportOffset = null; + }); + } +})); diff --git a/templates/combo/page_template.html b/templates/combo/page_template.html index dcf4e2a..a9e9589 100644 --- a/templates/combo/page_template.html +++ b/templates/combo/page_template.html @@ -11,6 +11,8 @@ + +