Parear Imagens
O aprendiz conecta pares de imagens que compartilham relação semântica, causal ou sequencial. Desenvolve reconhecimento visual e associação de conceitos.
Experimente
Conecte: Causa e Efeito
Selecione um item da esquerda e seu par da direita.
Causa
Efeito
Código-fonte
Dados de exemplo
Cada par tem uma imagem e uma legenda dos dois lados; o id do par é o gabarito — mesmo id nos dois lados forma a resposta certa.
| 1 |
<?php
|
| 2 |
$instrucao = 'Conecte cada causa ao efeito correspondente:';
|
| 3 |
$pares = [
|
| 4 |
['id' => 'p1', 'esqImagem' => 'semente.jpg', 'esqLegenda' => 'Semente plantada', 'dirImagem' => 'muda.jpg', 'dirLegenda' => 'Muda brotando'],
|
| 5 |
['id' => 'p2', 'esqImagem' => 'nuvem.jpg', 'esqLegenda' => 'Nuvem de chuva', 'dirImagem' => 'rua-molhada.jpg', 'dirLegenda' => 'Rua molhada'],
|
| 6 |
['id' => 'p3', 'esqImagem' => 'lagarta.jpg', 'esqLegenda' => 'Lagarta na folha', 'dirImagem' => 'borboleta.jpg', 'dirLegenda' => 'Borboleta colorida'],
|
| 7 |
];
|
| 8 |
$paresDir = $pares;
|
| 9 |
shuffle($paresDir);
|
| 10 |
?>
|
Estrutura da questão
Duas colunas de cartões com imagem — clicar um de cada lado forma um par; a cor do cartão mostra qual par é qual.
| 1 |
<div class="questao" data-tipo="parear-imagens">
|
| 2 |
|
| 3 |
<p class="questao-instrucao"><?= htmlspecialchars($instrucao) ?></p>
|
| 4 |
|
| 5 |
<div class="colunas">
|
| 6 |
<div class="coluna">
|
| 7 |
<p class="coluna-titulo">Causa</p>
|
| 8 |
<?php foreach ($pares as $i => $par): ?>
|
| 9 |
<button type="button" class="cartao cartao-esq" data-par-id="<?= $par['id'] ?>" data-cor="<?= $i % 6 ?>" data-estado="padrao">
|
| 10 |
<img src="<?= htmlspecialchars($par['esqImagem']) ?>" alt="<?= htmlspecialchars($par['esqLegenda']) ?>">
|
| 11 |
<span><?= htmlspecialchars($par['esqLegenda']) ?></span>
|
| 12 |
</button>
|
| 13 |
<?php endforeach; ?>
|
| 14 |
</div>
|
| 15 |
|
| 16 |
<div class="coluna">
|
| 17 |
<p class="coluna-titulo">Efeito</p>
|
| 18 |
<?php foreach ($paresDir as $par): ?>
|
| 19 |
<button type="button" class="cartao cartao-dir" data-par-id="<?= $par['id'] ?>" data-estado="padrao">
|
| 20 |
<img src="<?= htmlspecialchars($par['dirImagem']) ?>" alt="<?= htmlspecialchars($par['dirLegenda']) ?>">
|
| 21 |
<span><?= htmlspecialchars($par['dirLegenda']) ?></span>
|
| 22 |
</button>
|
| 23 |
<?php endforeach; ?>
|
| 24 |
</div>
|
| 25 |
</div>
|
Lista de pares conectados
Caixa vazia, preenchida pelo JavaScript a cada par formado; cada linha herda a cor do par e pode ser desfeita antes de verificar.
| 1 |
<div class="pares-lista" hidden></div>
|
| 2 |
<template class="par-template">
|
| 3 |
<div class="par">
|
| 4 |
<span class="par-esquerda"></span>
|
| 5 |
<span class="par-seta" aria-hidden="true">→</span>
|
| 6 |
<span class="par-direita"></span>
|
| 7 |
<button type="button" class="par-remover" aria-label="Remover esta correspondência">×</button>
|
| 8 |
</div>
|
| 9 |
</template>
|
Ações da questão
Verificar, o painel de resultado e o botão de reiniciar — os dois últimos nascem ocultos.
| 1 |
<button type="button" class="questao-verificar">Verificar resposta</button>
|
| 2 |
|
| 3 |
<div class="questao-painel" hidden>
|
| 4 |
<p class="questao-painel-mensagem"></p>
|
| 5 |
</div>
|
| 6 |
|
| 7 |
<button type="button" class="questao-reset" hidden>Tentar novamente</button>
|
| 8 |
|
| 9 |
</div>
|
Duas colunas
Causas à esquerda, efeitos à direita, lado a lado.
| 1 |
.colunas {
|
| 2 |
display: grid;
|
| 3 |
grid-template-columns: 1fr 1fr;
|
| 4 |
gap: 16px;
|
| 5 |
margin-bottom: 20px;
|
| 6 |
}
|
| 7 |
|
| 8 |
.coluna-titulo {
|
| 9 |
margin: 0 0 8px;
|
| 10 |
font-size: 11px;
|
| 11 |
font-weight: 700;
|
| 12 |
letter-spacing: 0.05em;
|
| 13 |
text-transform: uppercase;
|
| 14 |
color: #64748b;
|
| 15 |
}
|
Cartão com imagem
Foto em cima, legenda colada embaixo — sem respiro entre as duas, pra dar impressão de continuidade.
| 1 |
.cartao {
|
| 2 |
display: block;
|
| 3 |
width: 100%;
|
| 4 |
margin-bottom: 8px;
|
| 5 |
border: 1px solid #cbd5e1;
|
| 6 |
border-radius: 12px;
|
| 7 |
overflow: hidden;
|
| 8 |
background: #f8fafc;
|
| 9 |
font: inherit;
|
| 10 |
text-align: left;
|
| 11 |
cursor: pointer;
|
| 12 |
}
|
| 13 |
|
| 14 |
.cartao img {
|
| 15 |
display: block;
|
| 16 |
width: 100%;
|
| 17 |
aspect-ratio: 1 / 1;
|
| 18 |
object-fit: cover;
|
| 19 |
}
|
| 20 |
|
| 21 |
.cartao span {
|
| 22 |
display: block;
|
| 23 |
padding: 8px 10px;
|
| 24 |
font-size: 14px;
|
| 25 |
font-weight: 500;
|
| 26 |
text-align: center;
|
| 27 |
}
|
Cartão selecionado
Realce neutro enquanto aguarda o clique no efeito correspondente.
| 1 |
.cartao[data-estado="selecionado"] {
|
| 2 |
outline: 2px solid #6366f1;
|
| 3 |
background: rgba(99, 102, 241, 0.1);
|
| 4 |
}
|
Cor por par
Cada cartão da esquerda tem uma cor fixa (data-cor, de 0 a 5); o cartão da direita herda a cor do da esquerda quando o par se forma, então as duas fotos de uma dupla sempre combinam — e nunca com as de outro par.
| 1 |
.cartao[data-estado="associado"][data-cor="0"] { border-color: #f59e0b; background: rgba(245, 158, 11, 0.1); }
|
| 2 |
.cartao[data-estado="associado"][data-cor="1"] { border-color: #8b5cf6; background: rgba(139, 92, 246, 0.1); }
|
| 3 |
.cartao[data-estado="associado"][data-cor="2"] { border-color: #06b6d4; background: rgba(6, 182, 212, 0.1); }
|
| 4 |
.cartao[data-estado="associado"][data-cor="3"] { border-color: #d946ef; background: rgba(217, 70, 239, 0.1); }
|
| 5 |
.cartao[data-estado="associado"][data-cor="4"] { border-color: #14b8a6; background: rgba(20, 184, 166, 0.1); }
|
| 6 |
.cartao[data-estado="associado"][data-cor="5"] { border-color: #f97316; background: rgba(249, 115, 22, 0.1); }
|
Resultado da verificação
Verde para quem acertou o par certo, vermelho para quem ficou sem par ou casou com o errado.
| 1 |
.cartao[data-estado="correta"] { border-color: #22c55e; background: rgba(34, 197, 94, 0.1); }
|
| 2 |
.cartao[data-estado="errada"] { border-color: #ef4444; background: rgba(239, 68, 68, 0.1); }
|
Lista de pares conectados
Caixa com uma linha por par formado, cada linha na mesma cor da dupla nas colunas acima.
| 1 |
.pares-lista {
|
| 2 |
margin-bottom: 20px;
|
| 3 |
padding: 16px;
|
| 4 |
border: 1px solid #cbd5e1;
|
| 5 |
border-radius: 12px;
|
| 6 |
background: #f8fafc;
|
| 7 |
}
|
| 8 |
|
| 9 |
.par {
|
| 10 |
display: flex;
|
| 11 |
align-items: center;
|
| 12 |
justify-content: space-between;
|
| 13 |
gap: 12px;
|
| 14 |
padding: 10px 12px;
|
| 15 |
margin-bottom: 8px;
|
| 16 |
border: 1px solid #cbd5e1;
|
| 17 |
border-radius: 8px;
|
| 18 |
font-size: 14px;
|
| 19 |
}
|
| 20 |
|
| 21 |
.par:last-child {
|
| 22 |
margin-bottom: 0;
|
| 23 |
}
|
| 24 |
|
| 25 |
.par[data-cor="0"] { border-color: #f59e0b; background: rgba(245, 158, 11, 0.08); }
|
| 26 |
.par[data-cor="1"] { border-color: #8b5cf6; background: rgba(139, 92, 246, 0.08); }
|
| 27 |
.par[data-cor="2"] { border-color: #06b6d4; background: rgba(6, 182, 212, 0.08); }
|
| 28 |
.par[data-cor="3"] { border-color: #d946ef; background: rgba(217, 70, 239, 0.08); }
|
| 29 |
.par[data-cor="4"] { border-color: #14b8a6; background: rgba(20, 184, 166, 0.08); }
|
| 30 |
.par[data-cor="5"] { border-color: #f97316; background: rgba(249, 115, 22, 0.08); }
|
| 31 |
|
| 32 |
.par-seta {
|
| 33 |
color: #6366f1;
|
| 34 |
font-weight: 700;
|
| 35 |
}
|
Remover um par
Botão pequeno e circular, ativo só antes de verificar.
| 1 |
.par-remover {
|
| 2 |
flex-shrink: 0;
|
| 3 |
width: 24px;
|
| 4 |
height: 24px;
|
| 5 |
border: none;
|
| 6 |
border-radius: 999px;
|
| 7 |
background: #e2e8f0;
|
| 8 |
color: #475569;
|
| 9 |
font-size: 14px;
|
| 10 |
line-height: 1;
|
| 11 |
cursor: pointer;
|
| 12 |
}
|
Ações
Botões de largura total, um de cada vez na tela.
| 1 |
.questao-verificar,
|
| 2 |
.questao-reset {
|
| 3 |
display: block;
|
| 4 |
width: 100%;
|
| 5 |
padding: 12px 16px;
|
| 6 |
border: none;
|
| 7 |
border-radius: 10px;
|
| 8 |
font: inherit;
|
| 9 |
font-weight: 600;
|
| 10 |
cursor: pointer;
|
| 11 |
}
|
| 12 |
|
| 13 |
.questao-verificar {
|
| 14 |
background: #4f46e5;
|
| 15 |
color: #fff;
|
| 16 |
}
|
| 17 |
|
| 18 |
.questao-reset {
|
| 19 |
background: #e5e7eb;
|
| 20 |
color: #1f2937;
|
| 21 |
}
|
Estado oculto
O atributo hidden nativo tem a mesma especificidade da regra de exibição acima — sem isto, "Tentar novamente" apareceria antes de verificar.
| 1 |
.questao-verificar[hidden],
|
| 2 |
.questao-reset[hidden] {
|
| 3 |
display: none;
|
| 4 |
}
|
Painel de resultado
Caixa de destaque com a contagem de acertos, abaixo das colunas.
| 1 |
.questao-painel {
|
| 2 |
margin: 16px 0;
|
| 3 |
padding: 16px;
|
| 4 |
border-radius: 12px;
|
| 5 |
background: #1e3a8a;
|
| 6 |
color: #dbeafe;
|
| 7 |
font-weight: 600;
|
| 8 |
}
|
Encontrar os elementos da questão
Localiza as duas colunas, a lista de pares, o modelo de linha e as ações.
| 1 |
document.addEventListener('DOMContentLoaded', function () {
|
| 2 |
document.querySelectorAll('.questao[data-tipo="parear-imagens"]').forEach(function (questao) {
|
| 3 |
var esquerda = questao.querySelectorAll('.cartao-esq');
|
| 4 |
var direita = questao.querySelectorAll('.cartao-dir');
|
| 5 |
var lista = questao.querySelector('.pares-lista');
|
| 6 |
var template = questao.querySelector('.par-template');
|
| 7 |
var verificar = questao.querySelector('.questao-verificar');
|
| 8 |
var painel = questao.querySelector('.questao-painel');
|
| 9 |
var reset = questao.querySelector('.questao-reset');
|
| 10 |
// O cartão da esquerda selecionado, aguardando o clique no par da direita.
|
| 11 |
var selecionado = null;
|
| 12 |
|
| 13 |
function itemDireita(parId) {
|
| 14 |
return questao.querySelector('.cartao-dir[data-par-id="' + parId + '"]');
|
| 15 |
}
|
| 16 |
|
| 17 |
function legenda(cartao) {
|
| 18 |
return cartao.querySelector('span').textContent.trim();
|
| 19 |
}
|
| 20 |
|
| 21 |
function limparSelecao() {
|
| 22 |
if (selecionado) selecionado.dataset.estado = selecionado.dataset.associado ? 'associado' : 'padrao';
|
| 23 |
selecionado = null;
|
| 24 |
}
|
Lista de pares conectados
Cria ou atualiza a linha do par (na mesma cor do cartão da esquerda) e permite desfazer com o × — só antes de verificar.
| 1 |
function entradaPar(itemEsq) {
|
| 2 |
return lista.querySelector('.par[data-par-id="' + itemEsq.dataset.parId + '"]');
|
| 3 |
}
|
| 4 |
|
| 5 |
function criarOuAtualizarPar(itemEsq, itemDir) {
|
| 6 |
var entrada = entradaPar(itemEsq);
|
| 7 |
if (!entrada) {
|
| 8 |
var fragmento = template.content.cloneNode(true);
|
| 9 |
entrada = fragmento.querySelector('.par');
|
| 10 |
entrada.dataset.parId = itemEsq.dataset.parId;
|
| 11 |
lista.appendChild(fragmento);
|
| 12 |
}
|
| 13 |
entrada.dataset.cor = itemEsq.dataset.cor;
|
| 14 |
entrada.querySelector('.par-esquerda').textContent = legenda(itemEsq);
|
| 15 |
entrada.querySelector('.par-direita').textContent = legenda(itemDir);
|
| 16 |
lista.hidden = false;
|
| 17 |
}
|
| 18 |
|
| 19 |
function removerEntradaPar(itemEsq) {
|
| 20 |
var entrada = entradaPar(itemEsq);
|
| 21 |
if (entrada) entrada.remove();
|
| 22 |
lista.hidden = !lista.children.length;
|
| 23 |
}
|
| 24 |
|
| 25 |
lista.addEventListener('click', function (evento) {
|
| 26 |
var botao = evento.target.closest('.par-remover');
|
| 27 |
if (!botao || verificar.disabled) return;
|
| 28 |
var entrada = botao.closest('.par');
|
| 29 |
var itemEsq = questao.querySelector('.cartao-esq[data-par-id="' + entrada.dataset.parId + '"]');
|
| 30 |
var itemDir = itemEsq.dataset.associado ? itemDireita(itemEsq.dataset.associado) : null;
|
| 31 |
delete itemEsq.dataset.associado;
|
| 32 |
itemEsq.dataset.estado = 'padrao';
|
| 33 |
if (itemDir) { itemDir.dataset.estado = 'padrao'; delete itemDir.dataset.cor; }
|
| 34 |
removerEntradaPar(itemEsq);
|
| 35 |
});
|
Selecionar um cartão da esquerda
Clique alterna a seleção; clicar em outro cartão troca, sem mexer no par que ele já tinha formado.
| 1 |
esquerda.forEach(function (item) {
|
| 2 |
item.addEventListener('click', function () {
|
| 3 |
if (verificar.disabled) return;
|
| 4 |
if (selecionado === item) { limparSelecao(); return; }
|
| 5 |
limparSelecao();
|
| 6 |
selecionado = item;
|
| 7 |
item.dataset.estado = 'selecionado';
|
| 8 |
});
|
| 9 |
});
|
Associar ao efeito
Só reage com um cartão da esquerda selecionado; o cartão da direita herda a cor do par, e a lista ganha (ou atualiza) uma linha.
| 1 |
direita.forEach(function (item) {
|
| 2 |
item.addEventListener('click', function () {
|
| 3 |
if (!selecionado || verificar.disabled) return;
|
| 4 |
|
| 5 |
var anterior = Array.prototype.find.call(esquerda, function (e) {
|
| 6 |
return e.dataset.associado === item.dataset.parId;
|
| 7 |
});
|
| 8 |
var antigaDireita = selecionado.dataset.associado
|
| 9 |
? itemDireita(selecionado.dataset.associado)
|
| 10 |
: null;
|
| 11 |
|
| 12 |
if (anterior && anterior !== selecionado) {
|
| 13 |
delete anterior.dataset.associado;
|
| 14 |
anterior.dataset.estado = 'padrao';
|
| 15 |
removerEntradaPar(anterior);
|
| 16 |
}
|
| 17 |
if (antigaDireita && antigaDireita !== item) { antigaDireita.dataset.estado = 'padrao'; delete antigaDireita.dataset.cor; }
|
| 18 |
|
| 19 |
selecionado.dataset.associado = item.dataset.parId;
|
| 20 |
selecionado.dataset.estado = 'associado';
|
| 21 |
item.dataset.estado = 'associado';
|
| 22 |
item.dataset.cor = selecionado.dataset.cor;
|
| 23 |
criarOuAtualizarPar(selecionado, item);
|
| 24 |
selecionado = null;
|
| 25 |
});
|
| 26 |
});
|
Verificar
Um par está certo quando o par-id associado do cartão da esquerda bate com o próprio par-id.
| 1 |
verificar.addEventListener('click', function () {
|
| 2 |
var acertos = 0;
|
| 3 |
esquerda.forEach(function (item) {
|
| 4 |
var correto = item.dataset.associado === item.dataset.parId;
|
| 5 |
item.dataset.estado = correto ? 'correta' : 'errada';
|
| 6 |
if (correto) acertos++;
|
| 7 |
});
|
| 8 |
|
| 9 |
painel.querySelector('.questao-painel-mensagem').textContent = acertos === esquerda.length
|
| 10 |
? 'Correto! Todos os pares conectados.'
|
| 11 |
: acertos + ' de ' + esquerda.length + ' pares corretos.';
|
| 12 |
painel.hidden = false;
|
| 13 |
|
| 14 |
lista.querySelectorAll('.par-remover').forEach(function (b) { b.hidden = true; });
|
| 15 |
|
| 16 |
verificar.hidden = true;
|
| 17 |
reset.hidden = false;
|
| 18 |
});
|
Reiniciar
Desfaz todas as associações, esvazia a lista de pares e devolve o estado inicial.
| 1 |
reset.addEventListener('click', function () {
|
| 2 |
selecionado = null;
|
| 3 |
esquerda.forEach(function (item) {
|
| 4 |
delete item.dataset.associado;
|
| 5 |
item.dataset.estado = 'padrao';
|
| 6 |
});
|
| 7 |
direita.forEach(function (item) {
|
| 8 |
item.dataset.estado = 'padrao';
|
| 9 |
delete item.dataset.cor;
|
| 10 |
});
|
| 11 |
lista.textContent = '';
|
| 12 |
lista.hidden = true;
|
| 13 |
painel.hidden = true;
|
| 14 |
verificar.hidden = false;
|
| 15 |
reset.hidden = true;
|
| 16 |
});
|