<div id="app">
<transition v-on:enter="enter" v-bind:css="false" appear>
<div class="butter-cheese-eggs" v-show="true">
<div v-for="(block, index) in grid" @click="select(index)">
<block :figure.sync="block.figure" />
</div>
</div>
</transition>
<transition v-on:enter="enterWin" v-bind:css="false">
<win v-show="winner" :click-handler="restart"></win>
</transition>
</div>
<template id="block">
<div class="block">
<transition v-on:enter="enter" v-bind:css="false">
<span v-show="figure > -1">{{ fig }}</span>
</transition>
</div>
</template>
<template id="win">
<div class="win">
<h2>Win</h2>
<!--<button @click="clickHandler">Play again</button>-->
</div>
</template>
@import url('https://fonts.googleapis.com/css?family=Permanent+Marker');
* {
box-sizing: border-box;
}
#app {
display: flex;
overflow: hidden;
position: relative;
width: 100vw;
height: 100vh;
background-image: linear-gradient(red, orange);
}
.butter-cheese-eggs {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-wrap: wrap;
width: 300px;
height: 300px;
> div {
flex: 0 0 auto;
width: 100px;
height: 100px;
.block {
cursor: pointer;
display: table-cell;
width: 100px;
height: 100px;
font: bold 50px/0 'Comic Sans MS', sans-serif;
vertical-align: middle;
text-align: center;
}
&:nth-child(1),
&:nth-child(2),
&:nth-child(3),
&:nth-child(4),
&:nth-child(5),
&:nth-child(6) {
border-bottom: 3px solid #fff;
}
&:nth-child(2),
&:nth-child(5),
&:nth-child(8) {
border-left: 3px solid #fff;
border-right: 3px solid #fff;
}
}
span {
display: block;
}
}
.win {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(-10deg);
transform-origin: 50% 50%;
transform-origin: center;
width: 300px;
height: 300px;
margin-top: -60px;
margin-left: -20px;
padding-top: 140px;
font-family: 'Permanent Marker', cursive;
text-align: center;
h2 {
display: block;
margin: 0 0 10px;
font-size: 100px;
text-shadow: 0px 0px 50px white;
text-align: center;
}
button {
cursor: pointer;
transition: all 200ms cubic-bezier(0.175, 0.885, 0.320, 1.275);
outline: none;
display: inline-block;
padding: 15px;
background-color: darkred;
border: none;
border-radius: 10px;
font-family: 'Permanent Marker', cursive;
font-size: 20px;
color: white;
&:hover {
transform: scale(1.1);
}
}
}
View Compiled
let state = {
grid: _.map(_.range(0, 9), (index) => {
return { index, figure: -1 }
}),
myTurn: false
}
const appState = _.cloneDeep(state)
const block = Vue.component('block', {
name: 'block',
template: '#block',
props: {
figure: {
type: Number,
default: -1
}
},
computed: {
fig () {
return this.figure === 0 ? 'O' : 'X'
}
},
data () {
return {
selected: false
}
},
methods: {
enter (el, done) {
TweenMax.from(el, 1, {
autoAlpha: 0,
scale: 0,
ease: Elastic.easeOut.config(1.25, 0.5),
onComplete: done
})
}
}
})
const win = Vue.component('win', {
name: 'win',
template: '#win',
props: {
clickHandler: {
type: Function,
default: null
}
}
})
const app = new Vue({
name: 'app',
el: '#app',
data() {
return state
},
components: {
block
},
computed: {
winner () {
const wins = ['012', '036', '345', '147', '258', '678','048', '246']
const grid = this.grid
const player = this.myTurn ? 0 : 1
const moves = _.reduce(this.grid, (result, value, index) => {
if (value.figure === player) {
result.push(index)
}
return result
}, [])
return !!_.find(wins, win => {
const combination = _.map(win.split(''), n => parseInt(n));
console.log('combination', combination, moves)
return _.difference(combination, moves).length === 0;
})
}
},
methods: {
select (index) {
const {figure} = this.grid[index]
if (figure > -1) {
return;
}
this.grid[index].figure = this.myTurn ? 1 : 0
this.myTurn = !this.myTurn
},
restart () {
this.grid = appState.grid
this.myTurn = appState.myTurn
},
enter (el, done) {
TweenMax.from(el, 1, {
autoAlpha: 0,
scale: 0,
ease: Elastic.easeOut.config(1.25, 0.5)
})
},
enterWin (el) {
TweenMax.from(el, 1, {
autoAlpha: 0,
scale: 0,
ease: Elastic.easeOut.config(1.25, 0.5)
})
}
}
})
View Compiled
This Pen doesn't use any external CSS resources.