Cripto Dashboard em Javascript

Introdução
Neste tutorial, você aprenderá a criar um Cripto Dashboard utilizando apenas HTML, CSS e JavaScript, sem a necessidade de bibliotecas externas.
Este guia prático ensina como visualizar informações sobre a cotação de criptomoedas de forma simples e eficiente. Utilizaremos a API do Mercado Bitcoin para consultar e exibir os dados no dashboard.
Cripto Dashboard em Javascript
Primeiro passo você deverá criar a estrutura da página, para isso crie um arquivo html contendo o seguinte código :
<html lang="pt-BR"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css"> <title>Cripto DashBoard</title></head><body> <script> </script></body></html>
Logo após crio a estrutura do conversor:
<div class="container"> <div class="header"> <div class="title-container"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" stroke="currentColor" fill="currentColor" width="1em" height="1em" viewBox="0 0 30 30" alt="Ícone de Criptomoeda" > <path d="M 15 3 C 8.373 3 3 8.373 3 15 C 3 21.627 8.373 27 15 27 C 21.627 27 27 21.627 27 15 C 27 8.373 21.627 3 15 3 z M 14 8 L 16 8 L 16 10.007812 C 17.86 10.050812 18.970703 10.984141 18.970703 12.494141 C 18.970703 13.554141 18.188109 14.476906 17.162109 14.628906 L 17.162109 14.753906 C 18.486109 14.850906 19.449219 15.849672 19.449219 17.138672 C 19.449219 18.888672 18.129 19.995047 16 19.998047 L 16 22 L 14 22 L 14 20 L 11.5 20 L 11.5 10 L 14 10 L 14 8 z M 13.59375 11.601562 L 13.59375 14.144531 L 15.166016 14.144531 C 16.296016 14.144531 16.912109 13.680953 16.912109 12.876953 C 16.912109 12.079953 16.337844 11.601563 15.339844 11.601562 L 13.59375 11.601562 z M 13.59375 15.558594 L 13.59375 18.398438 L 15.457031 18.398438 C 16.663031 18.398438 17.314453 17.892031 17.314453 16.957031 C 17.314453 16.042031 16.641203 15.558594 15.408203 15.558594 L 13.59375 15.558594 z"> </path> </svg> <h2><span>C</span>ripto <span>D</span>ashboard</h2> </div> <div class="search-input"> <input type="text" id="filter-input" placeholder="Selecione uma Cripto Moeda. ex: Bitcoin" /> <div class="select-dropdown"> <ul id="optionsList"> <li>Bitcoin</li> <li>Ethereum</li> <li>Ripple</li> </ul> </div> <button id="searchButton"> <svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" alt="Ícone de search"> <path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"> </path> </svg> </button> </div> </div> <div class="dashBoard" id="dashBoard"> <div id="loading" style="display: none;">Carregando...</div> <div class="card"> <div class="css-text-mask mask-one">Bitcoin</div> <p>Ativo Selecionado</p> </div> <div class="card"> <div class="css-text-mask mask-two">BTC</div> <p>Ticker</p> </div> <div class="card"> <div class="css-text-mask mask-three">385M</div> <p>Última cotacação em (R$)</p> </div> <div class="card"> <div class="css-text-mask mask-three">Cripto</div> <p>Segmento </p> </div> <div class="card"> <div class="css-text-mask mask-four">+0,82%</div> <p>Variação em 24H</p> </div> <div class="card"> <div class="css-text-mask mask-one">6,9B</div> <p>Capitalização de mercado</p> </div> </div> </div> <div id="loadingModal" class="modal"> <div class="modal-content"> <div class="loader"></div> <p>Carregando...</p> </div> </div> <script src="main.js"></script>
No código acima crio a interface do DashBoard Cripto.
Adicionei um campo para entrada de um número e um botão para executar a consulta a api de cotação da moeda e por fim adicionei uma área para exibir o resultado retornado pela api.
Depois adiciono o css responsável pela estilização da página :
*,*:after,*:before { margin: 0; padding: 0; border: 0; font-size: 100%; vertical-align: baseline; text-decoration: none;}:root { --white: #ffffff; --dark: rgb(40, 44, 51); --green: rgb(166, 247, 80); --green2: rgb(142, 211, 69); --gray: rgb(85, 89, 95); --orange: rgb(255, 216, 98); --red: #f46663; --contorno: rgba(255, 255, 255, 0.7);}body { height: 100vh; width: 100vw; display: flex; align-items: center; justify-content: center; background-color: #121f32; color: var(--white); font-family: basic-sans, sans-serif !important;}.container { width: calc(100% - 2rem); padding: 1rem; display: flex; flex-direction: column; align-items: center; justify-content: center;}.header { width: calc(80% - 2rem); padding: 1rem; display: flex; flex-direction: row; align-items: center; justify-content: space-between;}.title-container { display: flex; flex-direction: row; align-items: end; gap: 0.5rem;}.title-container > svg { width: 5rem; height: 5rem; fill: var(--dark); stroke: var(--green);}.title-container h2 { font-size: 3rem; line-height: 4rem; font-weight: 700; font-style: normal; color: var(--white);}.title-container h2 > span { font-size: 3.8rem; line-height: 4rem; color: var(--green);}.search-input { min-width: 30%; display: flex; gap: 0.5rem; align-items: center; position: relative; padding: 0.5rem; background: var(--dark); border: 1px solid var(--gray); border-radius: 4px;}.search-input input { width: 100%; font-size: 1rem; color: var(--green); position: relative; outline: none; padding: 0.5rem 1rem; background: var(--dark); border: 1px solid transparent;}.search-input .select-dropdown { position: absolute; top: 50px; left: 0; background: var(--dark); border: 1px solid var(--gray); color: var(--green); width: 250px; z-index: 1; display: none;}.search-input .select-dropdown ul { list-style-type: none; padding: 0; margin: 0;}.search-input .select-dropdown li { padding: 0.5rem; cursor: pointer;}.search-input .select-dropdown li:hover { background-color: var(--green); color: var(--dark);}.search-input button { padding: 0.8rem; display: flex; align-items: center; justify-content: center; border-radius: 4px; text-decoration: none; background-color: var(--green);}.search-input button:hover { background-color: var(--green2); scale: 0.9;}.search-input button > svg { font-size: 1rem; fill: var(--gray);}.dashBoard { width: calc(80% - 2rem); padding: 1rem; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;}.card { display: flex; flex-direction: column; gap: 0.3rem; padding: 0.5rem;}.card p { font-size: 0.9rem; line-height: 1rem; font-weight: 600; letter-spacing: 0.0605rem; color: var(--orange);}.css-text-mask { text-align: left; font-family: basic-sans, sans-serif; font-size: 5rem; line-height: 5rem; font-weight: 800; background-position: 0px 0px; animation: animatedBackground 60s linear infinite normal; background-clip: text; -webkit-text-fill-color: #0000; background-size: 50%; -webkit-text-stroke-width: 1px; -webkit-text-stroke-color: #ffffff;}.css-text-mask { white-space: nowrap; overflow: hidden; text-overflow: ellipsis;}.mask-one { background-image: url(cripto-azul.jpg);}.mask-two { background-image: url(cripto-dourado.jpg);}.mask-three { background-image: url(cripto-verde.webp); text-transform: lowercase;}.mask-four { background-image: url(cripto-roxo.webp);}.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); justify-content: center; align-items: center;}.modal-content { padding: 1rem; display: flex; flex-direction: column; align-items: center; gap: 0.4rem; border: 2px solid var(--gray); border-radius: 4px; background: var(--dark); box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); color: var(--green); font-size: 0.8rem; font-weight: 600; letter-spacing: 0.125rem;}.loader { border: 8px solid var(--gray); border-top: 8px solid var(--green); border-radius: 50%; width: 3rem; height: 3rem; animation: loader-spin 1s linear infinite;}@keyframes loader-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); }}@keyframes animatedBackground { 0% { background-position: 0 0; } 100% { background-position: 500px 0; }}@keyframes float { 0% { transform: translateY(0); } 50% { transform: translateY(-10px); /* Move para cima */ } 100% { transform: translateY(0); }}@media (max-width: 1800px) { .css-text-mask { font-size: 4.2rem; }}@media (max-width: 1020px) { .css-text-mask { font-size: 3rem; }}@media (max-width: 768px) { .css-text-mask { font-size: 1.2rem; }}@media (max-width: 480px) { .css-text-mask { font-size: 1rem; }}
Por ultimo adiciono o script que irá obter a cotação da moeda:
const input = document.getElementById("filter-input");const dropdown = document.querySelector(".select-dropdown");const optionsList = document.getElementById("optionsList");const searchButton = document.getElementById("searchButton");const dashBoard = document.getElementById("dashBoard");const loadingModal = document.getElementById("loadingModal");let selectedCripto = null;const optionsCripto = [ { label: "Bitcoin", value: "BTC" }, { label: "Ethereum", value: "ETH" }, { label: "Litecoin", value: "LTC" }, { label: "Ripple", value: "XRP" }, { label: "Cardano", value: "ADA" },];function filterOptions() { const filter = input.value.toLowerCase(); const options = optionsList.getElementsByTagName("li"); let hasVisibleOption = false; Array.from(options).forEach((option) => { const optionText = option.textContent || option.innerText; if (optionText.toLowerCase().includes(filter)) { option.style.display = ""; hasVisibleOption = true; } else { option.style.display = "none"; } }); toggleDropdown(hasVisibleOption);}function selectOption(optionText, optionValue) { input.value = optionText; selectedCripto = optionValue; toggleDropdown(false);}function toggleDropdown(show) { dropdown.style.display = show ? "block" : "none";}// Eventos// Adiciona o evento global de clique para fechar o dropdown ao clicar foradocument.addEventListener("click", function (event) { if (!input.contains(event.target) && !dropdown.contains(event.target)) { toggleDropdown(false); // Fecha o dropdown }});// Adiciona evento para filtrar opçõesinput.addEventListener("input", filterOptions);// Exibe o dropdown ao focar no inputinput.addEventListener("focus", () => toggleDropdown(true));// Adiciona evento de clique para cada opção da listaoptionsList.addEventListener("click", (event) => { if (event.target.tagName === "LI") { const optionText = event.target.textContent || event.target.innerText; const optionValue = event.target.dataset.value; selectOption(optionText, optionValue); }});searchButton.addEventListener("click", async () => { showLoading(); if (selectedCripto) { const criptoInfo = await getCotacao(selectedCripto); updateCards(criptoInfo); } else { alert("Selecione uma criptomoeda antes de buscar a cotação."); } hideLoading();});async function loadOptions() { try { const optionsHTML = optionsCripto .map((option) => `<li data-value="${option.value}">${option.label}</li>`) .join(""); optionsList.innerHTML = optionsHTML; } catch (error) { console.error("Erro ao carregar opções:", error); }}loadOptions();async function fetchData(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`Erro ao buscar dados: ${response.status}`); } return await response.json();}async function getCotacao(cripto) { try { const [criptoInfoData, criptoCotacaoData] = await Promise.all([ fetchData( `https://api.mercadobitcoin.net/api/v4/symbols?symbols=${cripto}-BRL` ), fetchData( `https://api.mercadobitcoin.net/api/v4/tickers?symbols=${cripto}-BRL` ), ]); // Extração e formatação dos dados const { "base-currency": symbol, description, type } = criptoInfoData; const { open, last, vol } = criptoCotacaoData[0]; const variation = ((parseFloat(last) - parseFloat(open)) / parseFloat(open)) * 100; const criptoInfo = { symbol: symbol[0], description: description[0], type: type[0], lastValue: last, variation: variation, volume: vol, }; // Exibe o resultado no console console.log("Informações da Criptomoeda:", criptoInfo); return criptoInfo; } catch (error) { console.error("Erro ao buscar dados da criptomoeda:", error); return null; }}const updateCards = (criptoInfo) => { const cardData = [ { label: "Ativo Selecionado", value: criptoInfo.description, maskClass: "mask-one", }, { label: "Ticker", value: criptoInfo.symbol, maskClass: "mask-two" }, { label: "Última cotação em (R$)", value: formatNumber(criptoInfo.lastValue), maskClass: "mask-three", }, { label: "Segmento", value: criptoInfo.type, maskClass: "mask-three" }, { label: "Variação em 24H", value: formatPercent(criptoInfo.variation), maskClass: "mask-four", }, { label: "Volume Movimentado", value: formatNumber(criptoInfo.volume), maskClass: "mask-one", }, ]; const dashBoardCards = cardData .map((data) => createCard(data.label, data.value, data.maskClass)) .join(""); dashBoard.innerHTML = dashBoardCards;};const formatNumber = (number) => { number = Number(number); if (number >= 1e9) { return (number / 1e9).toFixed(0) + "B"; // Bilhões } else if (number >= 1e6) { return (number / 1e6).toFixed(0) + "M"; // Milhões } else if (number >= 1e3) { return (number / 1e3).toFixed(0) + "K"; // Milhar } return formatNumberPTBR(number);};const formatPercent = (number) => { return `${formatNumberPTBR(number)}%`;};const formatNumberPTBR = (number) => { const formatter = new Intl.NumberFormat("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2, }); return formatter.format(number);};const createCard = (label, value, maskClass) => { return ` <div class="card"> <div class="css-text-mask ${maskClass}">${value}</div> <p>${label}</p> </div> `;};const showLoading = () => { loadingModal.style.display = "flex";};const hideLoading = () => { loadingModal.style.display = "none";};
Você pode visualizar o componente no link abaixo:
Deixo aqui um vídeo curto do processo de criação do DashBoard:
Conclusão
Com esses simples passos, você consegue de forma rápida criar um Cripto Dashboard, usando apenas HTML e CSS. Caso necessite você pode personalizar estilizar conforme a sua necessidade.
Código fonte do tutorial do Dashboard em Javascript:
Referências


This really cleared things up for me.
Que bom, que te ajudou!