From abd6bf45811b0cc7fd24493defee621f25b15504 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 13 Oct 2019 13:42:38 +1100 Subject: [PATCH 1/2] add animateOnView --- src/js/percircle.js | 444 ++++++++++++++++++++++++-------------------- 1 file changed, 239 insertions(+), 205 deletions(-) diff --git a/src/js/percircle.js b/src/js/percircle.js index aff56ba..8aacd4a 100644 --- a/src/js/percircle.js +++ b/src/js/percircle.js @@ -1,210 +1,244 @@ (function (factory) { - "use strict"; + "use strict"; + + if (typeof define === 'function' && define.amd) { // AMD + define(['jquery'], factory); + } else if (typeof exports == "object" && typeof module == "object") { // CommonJS + module.exports = factory(require('jquery')); + } else { // Browser + factory(jQuery); + } +})(function ($, undefined) { + "use strict"; + + var percircleOnView = []; + + $.fn.percircle = function (options) { + // default options + var defaultOptions = { + animate: true, + animateOnView: false, // if true, will start animations once circle is in view + }; + + var rotationMultiplier = 3.6; + + $(window).scroll(function(e) { + $.each(percircleOnView, function(i, el) { + if (!el.completed) { + if (inViewport(el.instance)) { + el.completed = true; + $(el.instance).percircle(el.options); + } + } + }); + }); + + // for each element matching selector + return this.each(function () { + // extend with any provided options, options are local to each instance in case of data attributes + var localOptions = $.extend({}, defaultOptions, options); + /* + If it is about dynamic value update, ensure the filling of the bar will start from 0 degrees. + Without this line, if it as about a dynamic update from a value > 50 to a value < 50, the bar + filling will start from 180 degrees. + However, this fix is not the best one, UX-wise, since user loses the gradual degredation of the bar. + The way this should be best implemented is to perform a smooth animation from > 50deg back to the + requested value, instead of first moving to 0deg and then "drawing" again. + */ + if ($(this).hasClass('gt50')) $(this).removeClass('gt50'); + + var percircle = $(this); + var isUpdate = false; + + if (!percircle.data('initialised')) { + percircle.data('initialised', true); + } else { + isUpdate = true; + } + + var progressBarStyle = {}; + + // When user tries adding a custom progress bar color, the text color should be updated as well. + var changeTextColor = function (context, color) { + // Change color text the same with progress bar color + percircle.on('mouseover', function () { + context.children('span').css('color', color); + }); - if (typeof define === 'function' && define.amd) { // AMD - define(['jquery'], factory); + percircle.on('mouseleave', function () { + context.children('span').attr('style', ''); + }); + }; + + // add percircle class for styling + if (!percircle.hasClass('percircle')) percircle.addClass('percircle'); + // apply options + if (typeof (percircle.attr('data-animate')) !== 'undefined') localOptions.animate = percircle.attr('data-animate') == 'true'; + if (localOptions.animate) percircle.addClass('animate'); + if (typeof (percircle.attr('data-animateOnView')) !== 'undefined') localOptions.animateOnView = percircle.attr('data-animateOnView') == 'true'; + + if (typeof (percircle.attr('data-progressBarColor')) !== 'undefined') { + localOptions.progressBarColor = percircle.attr('data-progressBarColor'); + } + progressBarStyle = { + 'border-color': localOptions.progressBarColor + } + changeTextColor($(this), localOptions.progressBarColor); + + var percent = percircle.attr('data-percent') || localOptions.percent || 0; + var perclock = percircle.attr('data-perclock') || localOptions.perclock || 0; + var perdown = percircle.attr('data-perdown') || localOptions.perdown || 0; + + var animateOnView = localOptions.animate && localOptions.animateOnView && !inViewport(this); + + if (animateOnView && !isUpdate) { + percircleOnView.push({ + instance: this, + options: localOptions + }); + percent = 0; + } + if (percent || localOptions.displayTextAtZero) { + if (percent > 50) percircle.addClass('gt50'); + var text = percircle.attr('data-text') || localOptions.text || percent + '%'; + percircle.html('' + text + ''); + + // add divs for structure + $('
').appendTo(percircle); + $('.slice .bar, .slice .fill', percircle).css(progressBarStyle); + + if (percent > 50) + $('.bar', percircle).css({ + '-webkit-transform': 'rotate(180deg)', + '-moz-transform': 'rotate(180deg)', + '-ms-transform': 'rotate(180deg)', + '-o-transform': 'rotate(180deg)', + 'transform': 'rotate(180deg)' + }); + var rotationDegrees = rotationMultiplier * percent; + // set timeout causes the animation to be visible on load + setTimeout(function () { + $('.bar', percircle).css({ + '-webkit-transform': 'rotate(' + rotationDegrees + 'deg)', + '-moz-transform': 'rotate(' + rotationDegrees + 'deg)', + '-ms-transform': 'rotate(' + rotationDegrees + 'deg)', + '-o-transform': 'rotate(' + rotationDegrees + 'deg)', + 'transform': 'rotate(' + rotationDegrees + 'deg)' + }); + }, 0); + } else if (perclock && !isUpdate) { + if (!percircle.hasClass('perclock')) percircle.addClass('perclock'); + + setInterval(function () { + var d = new Date(); // without params it defaults to "now" + var text = getPadded(d.getHours()) + ":" + getPadded(d.getMinutes()) + ":" + getPadded(d.getSeconds()); + + percircle.html('' + text + ''); + // add divs for structure + $('
').appendTo(percircle); + + var seconds = d.getSeconds(); + if (seconds === 0) percircle.removeClass('gt50'); + if (seconds > 30) { + percircle.addClass('gt50'); + $('.bar', percircle).css({ + '-webkit-transform': 'rotate(180deg);scale(1,3)', + '-moz-transform': 'rotate(180deg);scale(1,3)', + '-ms-transform': 'rotate(180deg);scale(1,3)', + '-o-transform': 'rotate(180deg);scale(1,3)', + 'transform': 'rotate(180deg);scale(1,3)' + }); + } + + var rotationDegrees = 6 * seconds; // temporary clockwise rotation value + $('.bar', percircle).css({ + '-webkit-transform': 'rotate(' + rotationDegrees + 'deg)', + '-moz-transform': 'rotate(' + rotationDegrees + 'deg)', + '-ms-transform': 'rotate(' + rotationDegrees + 'deg)', + '-o-transform': 'rotate(' + rotationDegrees + 'deg)', + 'transform': 'rotate(' + rotationDegrees + 'deg)' + }); + }, 1000); + } else if (perdown && !isUpdate) { + getCountdown(percircle, localOptions, progressBarStyle); + } + }); + }; + + var inViewport = function(el) { + var elementTop = $(el).offset().top+$(el).height(); + var viewportTop = $(window).scrollTop(); + var viewportBottom = viewportTop + $(window).height(); + return elementTop < viewportBottom; + } + + // move to another file - functions + var getCountdown = function (percircle, options, progressBarColor) { + var secs = percircle.attr('data-secs') || options.secs; + var timeUpText = percircle.attr('data-timeUpText') || options.timeUpText; + var reset = percircle[0].hasAttribute('data-reset') || options.reset; + + if (timeUpText.length > 8) timeUpText = 'the end'; + + var counter; + + if (reset) { + percircle.on("click", timerReset); } - else if (typeof exports == "object" && typeof module == "object") { // CommonJS - module.exports = factory(require('jquery')); + + function timer() { + secs -= 1; + + if (secs > 30) percircle.addClass('gt50'); + if (secs < 30) percircle.removeClass('gt50'); + + timerUpdate(); + + if (secs <= 0) { + timerStop(); + percircle.html('' + timeUpText + ''); + return; + } } - else { // Browser - factory(jQuery); + + function timerStart() { + counter = setInterval(timer, 1000); } -}) (function($, undefined) { - "use strict"; - - $.fn.percircle = function(options) { - // default options - var defaultOptions = { - animate: true - }; - - // extend with any provided options - if (!options) options = {}; - $.extend(options, defaultOptions); - - var rotationMultiplier = 3.6; - - // for each element matching selector - return this.each(function(){ - /* - If it is about dynamic value update, ensure the filling of the bar will start from 0 degrees. - Without this line, if it as about a dynamic update from a value > 50 to a value < 50, the bar - filling will start from 180 degrees. - However, this fix is not the best one, UX-wise, since user loses the gradual degredation of the bar. - The way this should be best implemented is to perform a smooth animation from > 50deg back to the - requested value, instead of first moving to 0deg and then "drawing" again. - */ - if ($(this).hasClass('gt50')) $(this).removeClass('gt50'); - - var percircle = $(this); - var progressBarColor = ''; - - // When user tries adding a custom progress bar color, the text color should be updated as well. - var changeTextColor = function (context, color) { - // Change color text the same with progress bar color - percircle.on('mouseover', function(){ - context.children('span').css('color', color); - }); - - percircle.on('mouseleave', function(){ - context.children('span').attr('style', ''); - }); - }; - - // add percircle class for styling - if (!percircle.hasClass('percircle')) percircle.addClass('percircle'); - // apply options - if (typeof(percircle.attr('data-animate')) !== 'undefined') options.animate = percircle.attr('data-animate') == 'true'; - if (options.animate) percircle.addClass('animate'); - - if (typeof(percircle.attr('data-progressBarColor')) !== 'undefined') { - options.progressBarColor = percircle.attr('data-progressBarColor'); - progressBarColor = "style='border-color: "+ options.progressBarColor +"'"; - changeTextColor($(this), options.progressBarColor); - } else { - if (typeof options.progressBarColor !== 'undefined'){ - progressBarColor = "style='border-color: "+ options.progressBarColor +"'"; - changeTextColor($(this), options.progressBarColor); - } - } - - var percent = percircle.attr('data-percent') || options.percent || 0; - var perclock = percircle.attr('data-perclock') || options.perclock || 0; - var perdown = percircle.attr('data-perdown') || options.perdown || 0; - if (percent || options.displayTextAtZero) { - if (percent > 50) percircle.addClass('gt50'); - var text = percircle.attr('data-text') || options.text || percent + '%'; - - percircle.html(''+text+''); - - // add divs for structure - $('
').appendTo(percircle); - if (percent > 50) - $('.bar', percircle).css({ - '-webkit-transform' : 'rotate(180deg)', - '-moz-transform' : 'rotate(180deg)', - '-ms-transform' : 'rotate(180deg)', - '-o-transform' : 'rotate(180deg)', - 'transform' : 'rotate(180deg)' - }); - var rotationDegrees = rotationMultiplier * percent; - // set timeout causes the animation to be visible on load - setTimeout(function(){ - $('.bar', percircle).css({ - '-webkit-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-moz-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-ms-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-o-transform' : 'rotate(' + rotationDegrees + 'deg)', - 'transform' : 'rotate(' + rotationDegrees + 'deg)' - }); - }, 0); - } else if(perclock){ - if (!percircle.hasClass('perclock')) percircle.addClass('perclock'); - - setInterval(function(){ - var d = new Date(); // without params it defaults to "now" - var text = getPadded(d.getHours()) + ":" + getPadded(d.getMinutes()) + ":" + getPadded(d.getSeconds()); - - percircle.html(''+text+''); - // add divs for structure - $('
').appendTo(percircle); - - var seconds = d.getSeconds(); - if (seconds === 0) percircle.removeClass('gt50'); - if (seconds > 30){ - percircle.addClass('gt50'); - $('.bar', percircle).css({ - '-webkit-transform' : 'rotate(180deg);scale(1,3)', - '-moz-transform' : 'rotate(180deg);scale(1,3)', - '-ms-transform' : 'rotate(180deg);scale(1,3)', - '-o-transform' : 'rotate(180deg);scale(1,3)', - 'transform' : 'rotate(180deg);scale(1,3)' - }); - } - - var rotationDegrees = 6 * seconds; // temporary clockwise rotation value - $('.bar', percircle).css({ - '-webkit-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-moz-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-ms-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-o-transform' : 'rotate(' + rotationDegrees + 'deg)', - 'transform' : 'rotate(' + rotationDegrees + 'deg)' - }); - }, 1000); - } else if(perdown) { - getCountdown(percircle, options, progressBarColor); - } - }); - }; - - // move to another file - functions - var getCountdown = function(percircle, options, progressBarColor) { - var secs = percircle.attr('data-secs') || options.secs; - var timeUpText = percircle.attr('data-timeUpText') || options.timeUpText; - var reset = percircle[0].hasAttribute('data-reset') || options.reset; - - if (timeUpText.length > 8) timeUpText='the end'; - - var counter; - - if (reset) { - percircle.on("click", timerReset); - } - - function timer() { - secs-=1; - - if (secs > 30) percircle.addClass('gt50'); - if (secs < 30) percircle.removeClass('gt50'); - - timerUpdate(); - - if (secs <= 0) { - timerStop(); - percircle.html(''+timeUpText+''); - return; - } - } - - function timerStart() { - counter = setInterval(timer, 1000); - } - - function timerStop() { - clearInterval(counter); - } - - function timerReset() { - timerStop(); - - secs = options.secs; - timerUpdate(); - - timerStart(); - } - - function timerUpdate() { - percircle.html(''+secs+''); - // add divs for structure - $('
').appendTo(percircle); - - var rotationDegrees = 6 * secs; // temporary clockwise rotation value - $('.bar', percircle).css({ - '-webkit-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-moz-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-ms-transform' : 'rotate(' + rotationDegrees + 'deg)', - '-o-transform' : 'rotate(' + rotationDegrees + 'deg)', - 'transform' : 'rotate(' + rotationDegrees + 'deg)' - }); - } - - // Initialize timer - timerStart(); - }; - - // display a presentable format of current time - var getPadded = function(val){ - return val < 10 ? ('0' + val) : val; - }; -}); + + function timerStop() { + clearInterval(counter); + } + + function timerReset() { + timerStop(); + + secs = options.secs; + timerUpdate(); + + timerStart(); + } + + function timerUpdate() { + percircle.html('' + secs + ''); + // add divs for structure + $('
').appendTo(percircle); + + var rotationDegrees = 6 * secs; // temporary clockwise rotation value + $('.bar', percircle).css({ + '-webkit-transform': 'rotate(' + rotationDegrees + 'deg)', + '-moz-transform': 'rotate(' + rotationDegrees + 'deg)', + '-ms-transform': 'rotate(' + rotationDegrees + 'deg)', + '-o-transform': 'rotate(' + rotationDegrees + 'deg)', + 'transform': 'rotate(' + rotationDegrees + 'deg)' + }); + } + + // Initialize timer + timerStart(); + }; + + // display a presentable format of current time + var getPadded = function (val) { + return val < 10 ? ('0' + val) : val; + }; +}); \ No newline at end of file From d767066e738a909d0cf14fb66f480cfe37455ecd Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 13 Oct 2019 14:04:08 +1100 Subject: [PATCH 2/2] fix dangling comma --- src/js/percircle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/percircle.js b/src/js/percircle.js index 8aacd4a..15b8a38 100644 --- a/src/js/percircle.js +++ b/src/js/percircle.js @@ -17,7 +17,7 @@ // default options var defaultOptions = { animate: true, - animateOnView: false, // if true, will start animations once circle is in view + animateOnView: false // if true, will start animations once circle is in view }; var rotationMultiplier = 3.6;