🎯 1.000.000 de Pixels
Disponíveis: -
Vendidos: -
Preço por pixel: R$ 1,00
Arraste para selecionar blocos (mín. 10×10 pixels). Solte para configurar a compra.
Nenhuma seleção.
/** * PROTÓTIPO: compra de blocos 10×10 dentro de um canvas 1000×1000 (1.000.000 pixels). * Cada bloco = 100 pixels. Grade de 100 × 100 blocos. * Os dados são salvos em localStorage para fins de demonstração. */ const PRICE_PER_PIXEL = 1.0; // R$ 1,00 por pixel (ajuste depois) const BLOCK_SIZE = 10; // 10×10 pixels por bloco const GRID = 100; // 100×100 blocos const TOTAL_BLOCKS = GRID * GRID; // 10.000 blocos const TOTAL_PIXELS = 1000000; // DOM const canvas = document.getElementById('board'); const ctx = canvas.getContext('2d'); const selectionInfo = document.getElementById('selectionInfo'); const ownerEl = document.getElementById('owner'); const linkEl = document.getElementById('link'); const colorEl = document.getElementById('color'); const buyBtn = document.getElementById('buyBtn'); const blocksCountEl = document.getElementById('blocksCount'); const pixelsCountEl = document.getElementById('pixelsCount'); const totalPriceEl = document.getElementById('totalPrice'); const availableEl = document.getElementById('available'); const soldEl = document.getElementById('sold'); const kpiMoneyEl = document.getElementById('kpiMoney'); // Estado let occupied = loadOccupied(); // Uint8Array(10000) let purchases = loadPurchases(); // array de {x,y,w,h,color,owner,link} let isDragging = false; let start = null; let current = null; let selectionBlocks = []; // lista de índices de bloco selecionados // Funções de persistência em localStorage function saveOccupied() { const b64 = btoa(String.fromCharCode(...occupied)); localStorage.setItem('mp_occupied', b64); } function loadOccupied() { const b64 = localStorage.getItem('mp_occupied'); if (!b64) return new Uint8Array(TOTAL_BLOCKS); const bin = atob(b64); const arr = new Uint8Array(bin.length); for (let i=0;iacc+(v?1:0),0); const soldPixels = soldBlocks * BLOCK_SIZE * BLOCK_SIZE; const availPixels = TOTAL_PIXELS - soldPixels; availableEl.textContent = availPixels.toLocaleString('pt-BR'); soldEl.textContent = soldPixels.toLocaleString('pt-BR'); const totalMoney = soldPixels * PRICE_PER_PIXEL; kpiMoneyEl.textContent = totalMoney.toLocaleString('pt-BR', {style:'currency', currency:'BRL'}); } // Desenho base (grade leve) function drawGrid() { ctx.clearRect(0,0,canvas.width, canvas.height); // fundo ctx.fillStyle = '#0b0b0c'; ctx.fillRect(0,0,canvas.width, canvas.height); // desenhar compras salvas purchases.forEach(p => { ctx.fillStyle = p.color; ctx.fillRect(p.x, p.y, p.w, p.h); }); // linhas principais a cada 100px ctx.strokeStyle = 'rgba(255,255,255,0.04)'; ctx.lineWidth = 1; for (let x=0; x<=1000; x+=100){ ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,1000); ctx.stroke(); } for (let y=0; y<=1000; y+=100){ ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(1000,y); ctx.stroke(); } } function blockIndex(bx, by){ return by*GRID + bx; } function rectToBlocks(x,y,w,h){ const bx1 = Math.floor(x / BLOCK_SIZE); const by1 = Math.floor(y / BLOCK_SIZE); const bx2 = Math.ceil((x+w) / BLOCK_SIZE)-1; const by2 = Math.ceil((y+h) / BLOCK_SIZE)-1; const list = []; for (let by=by1; by<=by2; by++){ for (let bx=bx1; bx<=bx2; bx++){ if (bx<0||by<0||bx>=GRID||by>=GRID) continue; list.push(blockIndex(bx,by)); } } return list; } function blocksToRect(blocks){ if (!blocks.length) return null; const coords = blocks.map(i=>({bx:i%GRID, by:Math.floor(i/GRID)})); const minBx = Math.min(...coords.map(c=>c.bx)); const maxBx = Math.max(...coords.map(c=>c.bx)); const minBy = Math.min(...coords.map(c=>c.by)); const maxBy = Math.max(...coords.map(c=>c.by)); return { x: minBx*BLOCK_SIZE, y: minBy*BLOCK_SIZE, w: (maxBx-minBx+1)*BLOCK_SIZE, h: (maxBy-minBy+1)*BLOCK_SIZE }; } // Interação canvas.addEventListener('mousedown', (e)=>{ const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const x = Math.floor((e.clientX - rect.left) * scaleX); const y = Math.floor((e.clientY - rect.top) * scaleY); start = {x,y}; isDragging = true; }); window.addEventListener('mousemove', (e)=>{ if (!isDragging) return; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const x = Math.floor((e.clientX - rect.left) * scaleX); const y = Math.floor((e.clientY - rect.top) * scaleY); current = {x,y}; drawGrid(); // desenhar seleção alinhada aos blocos const x0 = Math.min(start.x, current.x); const y0 = Math.min(start.y, current.y); const x1 = Math.max(start.x, current.x); const y1 = Math.max(start.y, current.y); const sel = { x: Math.floor(x0 / BLOCK_SIZE) * BLOCK_SIZE, y: Math.floor(y0 / BLOCK_SIZE) * BLOCK_SIZE, w: (Math.ceil(x1 / BLOCK_SIZE) * BLOCK_SIZE) - Math.floor(x0 / BLOCK_SIZE) * BLOCK_SIZE, h: (Math.ceil(y1 / BLOCK_SIZE) * BLOCK_SIZE) - Math.floor(y0 / BLOCK_SIZE) * BLOCK_SIZE }; // pintar overlay ctx.fillStyle = 'rgba(16,185,129,0.25)'; ctx.fillRect(sel.x, sel.y, sel.w, sel.h); ctx.strokeStyle = 'rgba(16,185,129,0.9)'; ctx.lineWidth = 2; ctx.strokeRect(sel.x+0.5, sel.y+0.5, sel.w-1, sel.h-1); selectionBlocks = rectToBlocks(sel.x, sel.y, sel.w, sel.h) .filter(i => !occupied[i]); // apenas livres const blocks = selectionBlocks.length; const pixels = blocks * BLOCK_SIZE * BLOCK_SIZE; const total = pixels * PRICE_PER_PIXEL; selectionInfo.textContent = `Selecionados: ${blocks} blocos (${pixels.toLocaleString('pt-BR')} px) — valor R$ ${fmtBRL(total)}`; blocksCountEl.textContent = blocks; pixelsCountEl.textContent = pixels.toLocaleString('pt-BR'); totalPriceEl.textContent = fmtBRL(total); buyBtn.disabled = blocks === 0; }); window.addEventListener('mouseup', ()=>{ isDragging = false; }); buyBtn.addEventListener('click', ()=>{ const owner = ownerEl.value.trim(); const link = linkEl.value.trim(); const color = colorEl.value; if (!selectionBlocks.length){ alert('Selecione blocos livres no mural.'); return; } if (!owner){ alert('Informe o nome/empresa.'); return; } if (!/^https?:\/\//i.test(link)){ alert('Informe uma URL válida começando com http:// ou https://'); return; } // Criar compra única (retângulo que cobre os blocos) const rect = blocksToRect(selectionBlocks); // Marcar ocupação bloco a bloco selectionBlocks.forEach(i => occupied[i]=1); purchases.push({ x: rect.x, y: rect.y, w: rect.w, h: rect.h, color, owner, link }); saveOccupied(); savePurchases(); // redesenhar drawGrid(); updateHeaderStats(); // limpar UI selectionBlocks = []; blocksCountEl.textContent = '0'; pixelsCountEl.textContent = '0'; totalPriceEl.textContent = '0,00'; buyBtn.disabled = true; selectionInfo.textContent = 'Seleção aplicada! (demo)'; }); // Tornar blocos clicáveis para abrir link (em produção) canvas.addEventListener('click', (e)=>{ const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; const x = Math.floor((e.clientX - rect.left) * scaleX); const y = Math.floor((e.clientY - rect.top) * scaleY); // verificar se clicou dentro de alguma compra for (const p of purchases){ if (x >= p.x && x < p.x+p.w && y >= p.y && y < p.y+p.h){ window.open(p.link, '_blank'); break; } } }); // Inicialização drawGrid(); updateHeaderStats();