<h1>Tangram Memory Game</h1>
<p>Click cards and find matching tangrams</p>
<div class="board" id="tangramgrid"></div>
<svg style="display:none" viewBox="0 0 100 100">
<defs>
<polygon id="tangram1" points="0 0, 50 50, 0 100" />
<polygon id="tangram2" points="50 50, 75 25, 100 50, 75 75" />
<polygon id="tangram3" points="0 100, 25 75, 75 75, 50 100"/>
</defs>
</svg>
*, *:before, *:after {
box-sizing: border-box;
}
body {
padding: 1rem;
min-height: 100vh;
border: 1rem solid;
background: #926ea9;
color: wheat;
text-align: center;
}
h1 {
margin: 0;
font-weight: 400;
font-family: "Ribeye Marrow", sans-serif;
}
p {
font-family: Montserrat, sans-serif;
font-weight: 300;
margin: 0 0 1.5rem;
}
.t svg {
display: block
}
.t use {
transition: .5s;
}
.t:not(.revealed) use {
fill: darken(#926ea9, 10);
}
use {
stroke: wheat;
}
.revealed use {
stroke: transparent;
}
.board {
max-width: 75vh;
margin: 0 auto;
display: grid;
grid-gap: .5rem;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 1fr;
}
.t {
border: 1px solid;
}
.revealed {
background: white;
border-color: white;
}
.t {
.bt-1 {transform: translate(0,0);}
.bt-2 {transform: translate(0,0) scale(-1,1) rotate(90deg);}
.st-1 {transform: translate(25px,75px) scale(.5,.5) rotate(-90deg);}
.mt {transform: translate(100px,49.5px) scale(.7142,.7142) rotate(45deg);}
.st-2 {transform: translate(100px,50px) scale(.5,.5) rotate(-180deg);}
.sq {}
.rh {}
}
.tcat.revealed {
svg {transform: translate(0,8px);}
.bt-1 {
transform: translate(12.5px,10px);
fill: #3d3d3d;
}
.bt-2 {
transform: translate(-8.5px,130px) scale(-1,1) rotate(135deg);
fill: #666666;
}
.st-1 {
transform: translate(25px,0px) scale(.5,.5) rotate(180deg);
}
.st-2 {
transform: translate(-25px,-50px) scale(.5,.5) rotate(0deg);
}
.st-1, .st-2 {
fill: #FFE6C5;
}
.mt {
transform: translate(12.5px,12.5px) scale(-.7142,.7142) rotate(0deg);
}
.mt, .rh {
fill: #CCCCCC;
}
.sq {
transform: translate(-75px,-50px);
fill: #303030;
}
.rh {
transform: translate(133px,200px) scale(1,-1) rotate(45deg);
}
}
.tsquirrel.revealed {
svg {transform: translate(0,12px);}
.bt-1 {
transform: translate(12.5px,-12.5px);
fill: #6B3010;
}
.bt-2 {
transform: translate(-8.5px,108.5px) scale(-1,1) rotate(135deg);
fill: #7F381C;
}
.st-1 {
transform: translate(12.5px,87.5px) scale(.5,.5) rotate(180deg);
}
.st-2 {
transform: translate(97.5px,108.5px) scale(.5,.5) rotate(135deg);
}
.mt {
transform: translate(12.5px,-35px) scale(.7142,.7142) rotate(45deg);
}
.sq {
transform: translate(22.5px,-16.5px);
fill: #E6E6E6;
}
.rh {
transform: translate(172.5px,33.25px) scale(1,1) rotate(90deg);
fill: #7F381C;
}
.st-1, .st-2, .mt {
fill: #8C6239;
}
}
.theart.revealed {
.bt-1 {
transform: translate(-25px,50px) rotate(-90deg);
fill: #D4145A;
}
.bt-2 {
transform: translate(25px,50px) scale(-1,1) rotate(90deg);
fill: #c1272d;
}
.st-1 {
transform: translate(50px,25px) scale(.5,.5) rotate(-90deg);
fill: #9e005d;
}
.st-2 {
transform: translate(25px,50px) scale(.5,.5) rotate(0deg);
fill: darken(#ed1c24, 6);
}
.mt {
transform: translate(-25px,50px) scale(.7142,.7142) rotate(-45deg);
fill: #ed1c24;
}
.sq {
transform: translate(-25px,50px) ;
fill: lighten(#c1272d, 1);
}
.rh {
transform: translate(50px,125px) scale(1,-1) rotate(0deg) ;
fill: #D4145A;
}
}
.trocket.revealed {
.bt-1 {
transform: translate(75px,75px) rotate(-180deg);
fill: #ED1C24
}
.bt-2 {
transform: translate(75px,75px) scale(-1,1) rotate(180deg);
fill: #C1272D;
}
.st-1 {
transform: translate(-37.5px,62.5px) scale(.5,.5) rotate(-90deg);
fill: #FBB03B;
}
.st-2 {
transform: translate(37.5px,37.5px) scale(.5,.5) rotate(90deg);
fill: #BDCCD4;
}
.mt {
transform: translate(125px,25px) scale(-.7142,.7142) rotate(-135deg);
fill: #E6E6E6;
}
.sq {
transform: translate(-37.5px,12.5px) ;
fill: #4D4D4D;
}
.rh {
transform: translate(137px,137px) scale(1,-1) rotate(90deg);
fill: #FBB03B;
}
}
.tbird.revealed {
.bt-1 {
transform: translate(72.5px,-25px) rotate(45deg);
fill: #D5ABD8;
}
.bt-2 {
transform: translate(72.5px,45.5px) scale(1,1) rotate(-135deg);
fill: #D889DB;
}
.st-1 {
transform: translate(1.5px,45.5px) scale(.5,.5) rotate(-45deg);
fill: #E6E6E6;
}
.st-2 {
transform: translate(2px,-25px) scale(.5,.5) rotate(45deg);
fill: #FBB03B;
}
.mt {
transform: translate(2px,-26px) scale(.7142,.7142) rotate(0deg);
fill: #D29E98;
}
.sq {
transform: translate(36.5px,-25px) rotate(45deg);
fill: #E6E6E6;
}
.rh {
transform: translate(107px,10px) scale(1,1) rotate(45deg);
fill: #F2B1B7;
}
}
.thome.revealed {
.bt-1 {
transform: translate(25px,66.5px) rotate(-90deg);
fill: #D54D1D;
}
.bt-2 {
transform: translate(0px,116.5px) scale(1,1) rotate(-90deg);
fill: #F8CC88;
}
.st-1 {
transform: translate(100px,66.5px) scale(.5,.5) rotate(90deg);
fill: #FBE098;
}
.st-2 {
transform: translate(100px,116.5px) scale(.5,.5) rotate(180deg);
fill: #F8CC88;
}
.mt {
transform: translate(50px,66.5px) scale(-.7142,.7142) rotate(-45deg);
fill: #FBE098;
}
.sq {
transform: translate(25px,-75px) rotate(45deg);
fill: #E6E6E6;
}
.rh {
transform: translate(61px,137px) scale(1,-1) rotate(45deg);
fill: #E9624E;
}
}
.tlotus.revealed {
.bt-1 {
transform:translate(50px,85.5px) rotate(135deg);
fill: #CDDC39;
}
.bt-2 {
transform:translate(121.5px,14.5px) scale(1,1) rotate(45deg);
fill: #8BC34A;
}
.st-1{
transform: translate(50px,50px) scale(.5,.5) rotate(135deg);
fill: #d889db;
}
.mt {
transform: translate(86px,50px) scale(.7142,.7142) rotate(90deg);
fill: #4CAF50;
}
.st-2 {
transform: translate(50px,50px) scale(-.5,.5) rotate(135deg);
fill: #f2b1b7;
}
.sq {
transform: translate(-25px,-25px);
fill: #de5e87;
}
.rh {
transform: translate(121px,14.5px) rotate(45deg);
fill: #009688;
}
}
.tboat.revealed {
.bt-1 {
transform: translate(50px, 12.5px) rotate(45deg);
fill: #eeeeee;
}
.bt-2 {
transform: translate(50px, 0) scale(1, 1) rotate(0deg);
fill: #dddddd;
}
.st-1 {
transform: translate(50px, 100px) scale(0.5, 0.5) rotate(-90deg);
fill: #ededed;
}
.mt {
transform: translate(100px, 100px) scale(0.7142, 0.7142) rotate(90deg);
fill: #795548;
}
.st-2 {
transform: translate(85px, 0px) scale(0.5, 0.5) rotate(135deg);
fill: #d27373;
}
.sq {
transform: translate(25px, 25px);
fill: #c7c4c4;
}
.rh {
transform:translate(64px,29.5px) rotate(45deg);
fill: darken(#795548,10);
}
}
.t {
transition: opacity .75s .75s, background .5s;
cursor: pointer;
opacity: 1;
}
.hidden {
cursor: auto;
opacity: .5;
}
View Compiled
const classes = [
'tcat',
'tsquirrel',
'theart',
'trocket',
'tbird',
'thome',
'tlotus',
'tboat'
];
const tangram = `
<div class="t">
<svg viewbox="-50 -50 200 200" >
<use class="bt-1" xlink:href="#tangram1" transform="translate(0,0)"/>
<use class="bt-2 red" xlink:href="#tangram1" transform="translate(0,0) scale(-1,1) rotate(90 0 0)"/>
<use class="st-1" xlink:href="#tangram1" transform="translate(25,75) scale(.5,.5) rotate(-90 0 0)"/>
<use class="mt" xlink:href="#tangram1" transform="translate(100,49.5) scale(.7142,.7142) rotate(45 0 0)"/>
<use class="st-2" xlink:href="#tangram1" transform="translate(100,50) scale(.5,.5) rotate(-180 0 0)"/>
<use class="sq" xlink:href="#tangram2"/>
<use class="rh" xlink:href="#tangram3"/>
</svg>
</div>
`
class MemoryGame {
constructor (selector) {
this.selector = selector
this.init()
}
init () {
this.randomize()
this.buildGrid()
this.revealed = []
}
randomize () {
this.classes = this.shuffle(classes.concat(classes))
}
shuffle (array) {
array.sort( function(a, b) {return 0.5 - Math.random()} );
return array;
}
buildGrid () {
let html = ''
for (let i = 0; i<this.classes.length; i++ ) {
html += tangram;
}
this.selector.innerHTML = html;
this.cards = this.selector.querySelectorAll('.t')
this.classes.forEach ( (el, i) => {
const card = this.cards[i]
card.classList.add(el)
card.setAttribute( 'data-class', el)
this.addCardListeners(card)
})
}
checkMatch () {
return this.revealed.length === 2 && this.revealed[0].getAttribute('data-class') === this.revealed[1].getAttribute('data-class')
}
addCardListeners (el) {
el.addEventListener( 'mouseenter', (e) => {
if (el.classList.contains('revealed')){
return
}
if ( this.revealed.length < 2 ) {
return
}
Array.prototype.slice.call(this.revealed).forEach( (item) => {
item.classList.remove('revealed')
})
this.revealed = []
})
el.addEventListener( 'click', (e) => {
if (el.classList.contains('revealed')) {
return
}
el.classList.add('revealed');
this.revealed.push(el)
if ( this.checkMatch() ) {
Array.prototype.slice.call(this.revealed).forEach( (item) => {
item.classList.add('hidden')
this.revealed = []
})
}
}, false );
}
}
let memory = new MemoryGame(document.getElementById("tangramgrid"));
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.