PageSourceSearch

https://viva.com.br/static/js/cinquentometro.js?260925122501

js viva.com.br collected 2026-09-25 14:27:50 UTC 5,210 bytes, 138 lines download raw bytes

1/* ============================================================
2   Cinquentômetro — contador estimado da população 50+ no Brasil
3   Portal Viva · layoutV2
4
5   Método PRÓPRIO do projeto. NÃO consome IBGE, censo nem API externa:
6   é uma PROJEÇÃO client-side a partir de um valor de referência que
7   cresce de forma constante ao longo do tempo.
8
9       valor = inicial + incremento * floor( segundos_decorridos / intervalo )
10
11   Config via data-* no elemento #cinquentometro:
12     data-inicial     valor de referência (ex.: 62234770)
13     data-base        data em que o valor de referência valia (ISO, ex.: 2026-08-03)
14     data-seg         intervalo em segundos entre incrementos (ex.: 20)
15     data-incremento  quanto soma a cada incremento (opcional, padrão 1)
16
17   Renderiza cada algarismo como <span class="portalviva_contador__digito"> e
18   cada separador de milhar como <span class="portalviva_contador__sep">.
19
20   ANIMAÇÃO (pedido do cliente): cada quadro (dígito) que muda gira de baixo p/
21   cima (flip). Se muda só a última casa, gira só ela; se mudam 2, 3, … giram
22   todas as que mudaram. Anima SÓ os dígitos alterados, via Web Animations API
23   (re-dispara limpo, sem classes; funciona em Chrome/Firefox/Safari-iOS).
24   O limiar CASAS_MIN_ANIMACAO controla a partir de quantas casas anima (padrão 1).
25   A perspectiva 3D vai no próprio transform (perspective(...) aqui no animar());
26   o CSS só define transform-origin (centro) e backface-visibility.
27   ============================================================ */
28(function () {
29	'use strict';
30
31	// Anima CADA quadro (dígito) que muda. 1 = anima sempre que um dígito é
32	// atualizado — inclusive quando só a última casa muda (comportamento pedido).
33	// Aumente p/ só animar quando N+ casas mudarem juntas.
34	var CASAS_MIN_ANIMACAO = 1;
35
36	function initCinquentometro() {
37		var el = document.getElementById('cinquentometro');
38		if (!el) return;
39
40		var inicial = parseInt(el.getAttribute('data-inicial'), 10);
41		var base = Date.parse(el.getAttribute('data-base') + 'T00:00:00Z');
42		var intervalo = parseInt(el.getAttribute('data-seg'), 10);
43		var incremento = parseInt(el.getAttribute('data-incremento'), 10);
44		if (isNaN(incremento) || incremento <= 0) incremento = 1;
45
46		// sem config válida, não renderiza nada (evita "NaN" na tela)
47		if (isNaN(inicial) || isNaN(base) || isNaN(intervalo) || intervalo <= 0) return;
48
49		var spans = [];       // 1 <span> por caractere (dígitos e separadores)
50		var ultimoTxt = null; // string formatada anterior (ex.: "62.234.770")
51		var ultimoValor = null;
52
53		function valorAtual() {
54			var decorridos = Math.floor((Date.now() - base) / 1000);
55			if (decorridos < 0) decorridos = 0; // data de referência no futuro → mostra o inicial
56			return inicial + incremento * Math.floor(decorridos / intervalo);
57		}
58
59		// (re)constrói toda a estrutura de spans a partir do texto
60		function construir(txt) {
61			el.textContent = '';
62			spans = [];
63			var frag = document.createDocumentFragment();
64			for (var i = 0; i < txt.length; i++) {
65				var ch = txt.charAt(i);
66				var span = document.createElement('span');
67				if (ch >= '0' && ch <= '9') {
68					span.className = 'portalviva_contador__digito';
69					span.textContent = ch;
70				} else {
71					span.className = 'portalviva_contador__sep';
72					span.setAttribute('aria-hidden', 'true');
73				}
74				frag.appendChild(span);
75				spans.push(span);
76			}
77			el.appendChild(frag);
78		}
79
80		// giro de baixo p/ cima do dígito (WAAPI). Sem suporte → só troca o número.
81		function animar(span) {
82			if (typeof span.animate !== 'function') return;
83			// perspective() DENTRO do transform → ponto de fuga próprio de cada dígito
84			// (giro 3D vertical em torno do centro, sem escorregar pro lado).
85			span.animate(
86				[
87					{ transform: 'perspective(320px) rotateX(-90deg)', opacity: 0 },
88					{ transform: 'perspective(320px) rotateX(0deg)', opacity: 1 }
89				],
90				{ duration: 650, easing: 'cubic-bezier(.2,.7,.3,1)' }
91			);
92		}
93
94		function render() {
95			var valor = valorAtual();
96			if (valor === ultimoValor) return; // só mexe quando o número muda
97			ultimoValor = valor;
98
99			var txt = valor.toLocaleString('pt-BR');
100
101			// 1º render ou mudança de estrutura (nº de casas/separadores) → reconstrói
102			if (ultimoTxt === null || txt.length !== ultimoTxt.length) {
103				construir(txt);
104				ultimoTxt = txt;
105				return;
106			}
107
108			// mesma estrutura: acha as posições que mudaram (são sempre dígitos)
109			var mudaram = [];
110			for (var i = 0; i < txt.length; i++) {
111				if (txt.charAt(i) !== ultimoTxt.charAt(i)) mudaram.push(i);
112			}
113
114			// atualiza o texto dos dígitos que mudaram
115			for (var j = 0; j < mudaram.length; j++) {
116				spans[mudaram[j]].textContent = txt.charAt(mudaram[j]);
117			}
118
119			// anima só se >= CASAS_MIN_ANIMACAO casas mudaram de uma vez
120			if (mudaram.length >= CASAS_MIN_ANIMACAO) {
121				for (var k = 0; k < mudaram.length; k++) {
122					animar(spans[mudaram[k]]);
123				}
124			}
125
126			ultimoTxt = txt;
127		}
128
129		render();
130		setInterval(render, 1000);
131	}
132
133	if (document.readyState === 'loading') {
134		document.addEventListener('DOMContentLoaded', initCinquentometro);
135	} else {
136		initCinquentometro();
137	}
138})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.