COUNTER (Sonstige Elemente )

src/app/shared/components/counter/templates

Demo Section

Each variation will be presented in the following section.

Default

22300.54%

Readme

counter (component)

Description

Include a Counter with the option to attach a unit and a description text before or after the counter


Requirements


Installation

Installation with Veams from local machine

veams install bp absolute/filepath/to/counter


Fields

counter.hbs

Settings

Parameter Type Default Description
settings.counterClasses String '' Modifier classes for component
settings.counterContextClass String 'default' Context class of component

Content

Parameter Type Description
content.counterbox Object Object with properties to make settings for the counter

counter__box.hbs

Content

Parameter Type Description
counterbox.count-from Int/Float the number the element should start at
counterbox.count-to Int/Float the number the element should end at
counterbox.count-speed Int how long it should take to count between the target numbers count-from-number to count-to-number
counterbox.count-decimals Int the number of decimal places to show
counterbox.count-refreshInterval Int how often the element should be updated in the speed-time
counterbox.count-unit String unit of the number (f.e. kg, liter, …)

JavaScript Options

The module gives you the possibility to override default options:

Option Type Default Description
classes.activeClass String 'is-inview' Define that the counter was shown in View class for counter elements
classes.activeClass String 'is-inactive' Define the counter is not in action for counter elements (in use for Print-, Screenreaderversion, )
classes.activeClass String 'is-active' Define the counter is in action for counter elements
counterOptions Object Define a object for the counter with relevant informations
selectors.counterWrapper String '[data-js-item="counter__js__timer"]' Define the counterbox, relevant for starting the animation,…
selectors.counterNumber String '[data-js-item="counter__js__number"]' Define the area in which the counting number is animated
selectors.counterNumberTo String '[data-js-item="counter__js__numberto"]' Define the area in which the end result (number) is located, relevant for printing and screenreading

SASS

Variables

There are multiple variables which you can change:

Variable Value Description

Modifier Classes

There are modifier classes you can add to counter__timer:

Modifier Description
is-inview For the Counter in Viewport
is-inactive For the inactive Counter
is-active For the active animated Counter

Templates

counter.hbs

<div class="c-counter{{#if settings.counterContextClass}}--{{settings.counterContextClass}}{{else}}--default{{/if}}{{#if settings.counterClasses}} {{settings.counterClasses}}{{/if}}"
     data-css="c-counter" data-js-module="counter"{{#if
     settings.counterJsOptions}}
     data-js-options='{{stringify settings.counterJsOptions}}'{{/if}}>
    {{#if (isObject content.counterbox)}}
        {{> counter__box content.counterbox}}
    {{/if}}
</div>

counter__box.hbs

{{#if count-to}}<div class="counter__timer is-inactive" data-js-item="counter__js__timer" {{#if count-lng}}data-js-lng="{{count-lng}}"{{else}}data-js-lng="de"{{/if}} data-to="{{count-to}}"{{#if count-from}} data-from="{{count-from}}"{{/if}}{{#if count-speed}} data-speed="{{count-speed}}"{{/if}}{{#if count-refreshInterval}} data-refreshInterval="{{count-refreshInterval}}"{{/if}}{{#if count-decimals}} data-decimals="{{count-decimals}}"{{/if}}><span class="counter__number" data-js-item="counter__js__number" aria-hidden="true">{{count-to}}</span><span class="counter__numberto" data-js-item="counter__js__numberto">{{count-to}}</span>{{#if count-unit}}<span class="counter__unit">{{count-unit}}</span>{{/if}}</div>{{/if}}

Data File

counter.hjson

{
	"title": "Counter",
	"variations": {
		"default": {
			"docs": {
				"variationName": "Default"
			},
			"settings": {
				"counterContextClass": "default",
				"counterClasses": ""
			},
			"content": {
				"counterbox": {
					"count-from": "100",
					"count-to": "22300.54",
					"count-speed": "2500",
					"count-decimals": "2",
					"count-unit": "%",
					"count-lng": "en"
				}
			}
		}
	}
}

Styles

counter.scss

/* ===================================================
component: counter
=================================================== */

/* ---------------------------------------------------
Global Styles
--------------------------------------------------- */
[data-css="c-counter"] {

	.counter__timer {
		color: $color-green;
		font-family: $font-bold;
		font-size: 7rem;
		line-height: (82/70);
		letter-spacing: 0;
		display: flex;
		justify-content: center;
		align-items: center;

		@include bp($bp-tablet-p) {
			font-size: 9.5rem;
			line-height: (100/95);
			letter-spacing: .03rem;
		}

		@include bp($bp-tablet-l) {
			font-size: 7rem;
			line-height: (82/70);
			letter-spacing: 0;
		}

		@include bp($bp-desktop-l) {
			font-size: 9.5rem;
			line-height: (100/95);
			letter-spacing: .03rem;
		}

		&.is-inactive {

			.counter__number {
				display: none;
			}

			.counter__numberto {
				display: block;
			}
		}

		&.is-active {

			.counter__number {
				display: block;

				@include print() {
					display: none;
				}
			}

			.counter__numberto {
				@include hidden-text();

				@include print() {
					color: $color-black;
					font-family: $font-bold;
					font-size: 7rem;
					line-height: (82/70);
					letter-spacing: 0;
				}
			}
		}
	}
}

/* ---------------------------------------------------
Context: Default
--------------------------------------------------- */
.c-counter--default {
	width: 100%;
	font-family: $font-regular;
	padding: 5% 0;
	position: relative;
}

Scripts

counter.js

/**
 * Description of Counter.
 * Counter with start and end number (Int and float) and the opportunity to append a unit
 * Animate the counter by in Viewport
 *
 * Class properties and decorators are supported.
 *
 * @module Counter
 * @version v1.0.0
 *
 * @author Ralf Arnert
 */

// Imports
import $ from 'jquery';
import Component from '@veams/component';
import isInViewport from '@veams/helpers/lib/detection/is-in-viewport';

class Counter extends Component {
	/**
	 * Class Properties
	 */
	$el = $(this.el);
	$counterWrapper = $(this.options.selectors.counterWrapper, this.el);
	$counterNumber = $(this.options.selectors.counterNumber, this.el);
	$counterNumberTo = $(this.options.selectors.counterNumberTo, this.el);

	/**
	 * Constructor for our class
	 *
	 * @see module.js
	 *
	 * @param {Object} obj - Object which is passed to our class
	 * @param {Object} obj.el - element which will be saved in this.el
	 * @param {Object} obj.options - options which will be passed in as JSON object
	 */
	constructor(obj) {
		let options = {
			selectors: {
				counterWrapper: '[data-js-item="counter__js__timer"]',
				counterNumber: '[data-js-item="counter__js__number"]',
				counterNumberTo: '[data-js-item="counter__js__numberto"]'
			},
			classes: {
				inview: 'is-inview',
				inactive: 'is-inactive',
				active: 'is-active'
			},
			counterOptions: {
				from: 0, // the number the element should start at
				to: 0, // the number the element should end at
				toFormated: 0, // the number the element should end at in special format
				speed: 800, // how long it should take to count between the target numbers
				refreshInterval: 100, // how often the element should be updated
				decimals: 0, // decimals to show
				countnr: 0, //the active count number
				intervalOne: '', // contain the Interval to clear it
				countlng: 'de'
			}
		};

		super(obj, options);
	}

	/** =================================================
	 * EVENTS
	 * ================================================ */

	// Event Handlers
	get events() {
		return {
			'{{context.EVENTS.mouseover}} {{this.options.selectors.counterWrapper}}':
				'wrapperMouseOver',
			'{{context.EVENTS.touchstart}} {{this.options.selectors.counterWrapper}}':
				'wrapperMouseOver'
		};
	}

	get subscribe() {
		return {
			'{{context.EVENTS.pagescrollThrottled}}': 'onPageScroll'
		};
	}

	/**
	 * Adjust height and vertical position of controller depending on components viewport height and vertical map
	 * position
	 *
	 * @param {Object} [e] - Event object
	 */
	onPageScroll(e) {
		this.startCounter(500);
	}

	/**
	 * Render class
	 */
	render() {
		let cdec = this.options.counterOptions.decimals;

		if (
			this.$counterWrapper.attr('data-decimals') != null &&
			!isNaN(parseFloat(this.$counterWrapper.attr('data-decimals')))
		) {
			this.options.counterOptions.decimals = parseFloat(
				this.$counterWrapper.attr('data-decimals')
			);
			cdec = this.options.counterOptions.decimals;
		}

		if (
			this.$counterWrapper.attr('data-from') != null &&
			!isNaN(parseFloat(this.$counterWrapper.attr('data-from')))
		) {
			this.options.counterOptions.from = parseFloat(
				this.$counterWrapper.attr('data-from')
			).toFixed(cdec);
		}
		this.options.counterOptions.countnr = this.options.counterOptions.from;

		if (
			this.$counterWrapper.attr('data-to') != null &&
			!isNaN(parseFloat(this.$counterWrapper.attr('data-to')))
		) {
			this.options.counterOptions.to = parseFloat(
				this.$counterWrapper.attr('data-to')
			).toFixed(cdec);
		}

		if (
			this.$counterWrapper.attr('data-speed') != null &&
			!isNaN(parseFloat(this.$counterWrapper.attr('data-speed')))
		) {
			this.options.counterOptions.speed = parseFloat(this.$counterWrapper.attr('data-speed'));
		}

		if (
			this.$counterWrapper.attr('data-refreshInterval') != null &&
			!isNaN(parseFloat(this.$counterWrapper.attr('data-refreshInterval')))
		) {
			this.options.counterOptions.refreshInterval = parseFloat(
				this.$counterWrapper.attr('data-refreshInterval')
			);
		}

		if (
			this.$counterWrapper.attr('data-js-lng') != null
		) {
			this.options.counterOptions.countlng = this.$counterWrapper.attr('data-js-lng');
		}

		// how many times to update the value, and how much to increment the value on each update
		this.options.counterOptions.loops = Math.ceil(
			this.options.counterOptions.speed / this.options.counterOptions.refreshInterval
		);
		this.options.counterOptions.increment =
			(this.options.counterOptions.to - this.options.counterOptions.from) /
			this.options.counterOptions.loops;
		this.options.counterOptions.loopCount = 0;
		this.options.counterOptions.toFormated = this.formatter(this.options.counterOptions.to);
		this.$counterNumberTo.html(this.options.counterOptions.toFormated);
		this.$counterNumber.html(this.options.counterOptions.toFormated);

		this.startCounter(1500);

		return this;
	}

	/** =================================================
	 * CUSTOM COUNTER METHODS
	 * ================================================= */

	/**
	 *  Start Counter depending on viewport and a class to prevent a second start
	 *
	 * @param {number} timeout - Milliseconds waiting to start the function
	 *
	 */
	startCounter(timeout) {
		setTimeout(() => {
			if (
				isInViewport(this.el) &&
				!this.$counterWrapper.hasClass(this.options.classes.inview)
			) {
				this.$counterWrapper.addClass(this.options.classes.inview);
				this.updateTimer();
			}
		}, timeout);
	}

	/**
	 *  Count with the settings
	 */
	countTo() {
		// references & variables that will change with each update
		let value = this.options.counterOptions.countnr;
		this.options.counterOptions.countnr =
			parseFloat(this.options.counterOptions.countnr) +
			parseFloat(this.options.counterOptions.increment);

		if (
			parseFloat(this.options.counterOptions.countnr) >=
			parseFloat(this.options.counterOptions.to)
		) {
			value = this.options.counterOptions.to;
			this.counterRender(value);
		} else {
			this.counterRender(value);
		}
	}

	/**
	 * Update the Count after a time defined by JSON or default
	 */
	updateTimer() {
		this.setActive();
		this.options.counterOptions.intervalOne = setInterval(() => {
			if (
				parseFloat(this.options.counterOptions.countnr) >=
				parseFloat(this.options.counterOptions.to)
			) {
				clearInterval(this.options.counterOptions.intervalOne);
				this.setInactive();
			} else {
				this.countTo();
			}
		}, this.options.counterOptions.loops);
	}

	/**
	 * Format the Number to show for the counting
	 *
	 * @param {string} value - Nummer
	 *
	 * @return {string}
	 */
	formatter(value) {
		if (this.options.counterOptions.countlng == "de") {
			return parseFloat(value)
			.toFixed(this.options.counterOptions.decimals)
			.replace(/\B(?=(?:\d{3})+(?!\d))/g, '.');
		} else {
			return parseFloat(value)
			.toFixed(this.options.counterOptions.decimals)
			.replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
		}
	}

	/**
	 * Write the formated number in the element
	 *
	 * @param {string} value - Nummer
	 *
	 */
	counterRender(value) {
		let formattedValue = this.formatter(value);
		this.$counterNumber.html(formattedValue);
	}

	/**
	 * Event handler for mouse over on counter
	 *
	 * @param {Object} [e] - Event object
	 *
	 */
	wrapperMouseOver(e) {
		if (!this.$counterWrapper.hasClass(this.options.classes.inactive)) {
			clearInterval(this.options.counterOptions.intervalOne);
			this.options.counterOptions.countnr = this.options.counterOptions.to;
			this.countTo();
			this.setInactive();
		}
	}

	/**
	 * set a counter active by removing a class in-active
	 */
	setActive() {
		if (this.$counterWrapper.hasClass(this.options.classes.inactive)) {
			this.$counterWrapper.removeClass(this.options.classes.inactive);
			this.$counterWrapper.addClass(this.options.classes.active);
		}
	}

	/**
	 * set a counter inactive by adding a class in-active
	 */
	setInactive() {
		if (this.$counterWrapper.hasClass(this.options.classes.active)) {
			this.$counterWrapper.removeClass(this.options.classes.active);
			this.$counterWrapper.addClass(this.options.classes.inactive);
		}
	}
}

export default Counter;

HTML Output

Default

<div class="c-counter--default" data-css="c-counter" data-js-module="counter">
	<div class="counter__timer is-inactive" data-js-item="counter__js__timer" data-js-lng="en" data-to="22300.54" data-from="100" data-speed="2500" data-decimals="2"><span class="counter__number" data-js-item="counter__js__number" aria-hidden="true">22300.54</span><span class="counter__numberto" data-js-item="counter__js__numberto">22300.54</span><span class="counter__unit">%</span></div>
</div>

Wonach suchst du?