-
-
Save holgersindbaek/b8f134306148d12995dadf5e28d2d5b7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { playSound } from '~/utils/playSound' | |
import { TweenMax, TimelineMax, Power2, Sine } from 'gsap' | |
export function handleNewGameInitialized() { | |
// Create initial variables | |
const animationTimeline = new TimelineMax({ | |
paused: true, | |
onComplete: () => { | |
// // Call action on complete | |
// this.props.handleResetCardPositions() | |
// dispatch(gameActions.setCardPositions({ test: "test" })) | |
// // Set new state | |
// this.setState({ deck: this.props.deck, board: this.props.board }) | |
} | |
}) | |
// Move cards to stock | |
if (is.not.equal(this.state.board.stock.count(), 52)) { | |
// Initial variables | |
const stockPosition = this.containerPosition('#stock') | |
// Animate cards to stock | |
animationTimeline.add([TweenMax.to('.card_component', Number.toSeconds(480), { | |
top: stockPosition.top, | |
left: stockPosition.left, | |
ease: Power2.easeOut, | |
onStart: () => { | |
// Set shadow on front and back | |
$('.card_component').find('.front, .back').css({ 'box-shadow': 'inset 0px 0px 0px 1px rgba(0,0,0,0.65), 0px 0px 8px 0px rgba(0,0,0,0)' }) | |
} | |
}), TweenMax.to('.card_component.face_up .back', Number.toSeconds(80), { | |
rotationY: -90, | |
ease: Sine.easeOut | |
}), TweenMax.to('.card_component.face_up .front', Number.toSeconds(80), { | |
rotationY: 0, | |
delay: Number.toSeconds(80), | |
ease: Sine.easeIn, | |
onComplete: () => { | |
// Loop through face up cards | |
$('.card_component.face_up').each((index, element) => { | |
// // Set face up state on card | |
// this.refs[`card_${$(element).data('suit')}_${$(element).data('rank')}`].setState({ faceUp: false }) | |
}) | |
} | |
})]) | |
} | |
// Move cards to tableau | |
Object.forEach(this.tableaus(), (tableau, tableauKey) => { | |
// Initial variables | |
const tableauPosition = this.containerPosition(`#${tableauKey.underscore()}`) | |
const tableauCardMargin = this.tableauCardMargin(tableau.count()) | |
const tableauIndex = Object.keys(this.tableaus()).findIndex(tableauKey) | |
const targets = tableau.map((card) => `#card_${card.suit}_${card.rank}`) | |
const duration = Number.toSeconds(200 - (20 * (6 - tableauIndex))) | |
const delay = -Number.toSeconds(20 * tableauIndex) | |
animationTimeline.to(targets, duration, { | |
top: (index) => tableauPosition.top + (index * tableauCardMargin), | |
left: () => tableauPosition.left, | |
delay: delay, | |
ease: Power2.easeOut, | |
onStart: () => { | |
// Loop through cards | |
tableau.forEach((card, cardIndex) => { | |
// Set z index on card | |
$(`#card_${card.suit}_${card.rank}`).css({ 'z-index': 2000 + cardIndex }) | |
// Set shadow on front and back | |
$(`#card_${card.suit}_${card.rank}`).find('.front, .back').css({ 'box-shadow': 'inset 0px 0px 0px 1px rgba(0,0,0,0.65), 0px 0px 8px 0px rgba(0,0,0,0.1)' }) | |
}) | |
// Play deal sound | |
if (is.equal(tableauIndex, 0)) { playSound('deal') } | |
}, | |
onComplete: () => { | |
// Loop through cards | |
tableau.forEach((card, cardIndex) => { | |
// Set z index on card | |
$(`#card_${card.suit}_${card.rank}`).css({ 'z-index': 1000 + cardIndex }) | |
}) | |
} | |
}) | |
}) | |
// Turn first card in tableau | |
Object.forEach(this.tableaus(), (tableau) => { | |
// Initial variables | |
const card = tableau.last() | |
// Add turn to animation | |
animationTimeline.to(`#card_${card.suit}_${card.rank} .front`, Number.toSeconds(80), { | |
rotationY: 90, | |
delay: -Number.toSeconds(80), | |
ease: Sine.easeOut | |
}).to(`#card_${card.suit}_${card.rank} .back`, Number.toSeconds(80), { | |
rotationY: 0, | |
ease: Sine.easeIn, | |
onComplete: () => { | |
// // Set face up state on card | |
// this.refs[`card_${card.suit}_${card.rank}`].setState({ faceUp: true }) | |
} | |
}) | |
}) | |
// Run animations | |
animationTimeline.play() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Interact from 'interactjs' | |
import { React } from '~/utils/import' | |
import { playSound } from '~/utils/playSound' | |
import { TweenMax, TimelineMax, Power2, Sine } from 'gsap' | |
import PropTypes from 'prop-types' | |
import CardComponent from '../../components/CardComponent' | |
import { handleNewGameInitialized } from './animationHelper' | |
import './index.scss' | |
class BoardComponent extends React.Component { | |
static propTypes = { | |
deck: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
board: PropTypes.shape({ | |
stock: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
waste: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
foundationClover: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
foundationHeart: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
foundationSpade: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
foundationDiamond: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauOne: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauTwo: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauThree: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauFour: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauFive: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauSix: PropTypes.arrayOf(React.PropTypes.shape).isRequired, | |
tableauSeven: PropTypes.arrayOf(React.PropTypes.shape).isRequired | |
}) | |
} | |
constructor(props) { | |
// Set props | |
super(props) | |
// Set initial state | |
this.state = { | |
deck: props.board.deck, | |
board: { | |
stock: props.board.stock, | |
waste: props.board.waste, | |
foundationClover: props.board.foundationClover, | |
foundationHeart: props.board.foundationHeart, | |
foundationSpade: props.board.foundationSpade, | |
foundationDiamond: props.board.foundationDiamond, | |
tableauOne: props.board.tableauOne, | |
tableauTwo: props.board.tableauTwo, | |
tableauThree: props.board.tableauThree, | |
tableauFour: props.board.tableauFour, | |
tableauFive: props.board.tableauFive, | |
tableauSix: props.board.tableauSix, | |
tableauSeven: props.board.tableauSeven | |
} | |
} | |
// Bind functions | |
this.handleNewGameInitialized = handleNewGameInitialized.bind(this) | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// Lifecycle | |
///////////////////////////////////////////////////////////////////////// | |
componentDidMount() { | |
// Position initial cards | |
this.positionCardsOnBoard() | |
// Initialize drag and drop | |
this.initializeInteract() | |
// Listen for when new game is initialized | |
emitter.on('initializeNewGame', this.handleNewGameInitialized) | |
// Listen for height change | |
let handleHeightChangeTimer | |
$(window).resize(() => { | |
if (is.existy(handleHeightChangeTimer)) { handleHeightChangeTimer.cancel() } | |
handleHeightChangeTimer = this.updateTableauCardMargins.delay(1000) | |
}) | |
// Listen for tap on stock | |
$('#card_container').on('click', '.card_component[data-container="stock"]', this.handleStockTap) | |
} | |
shouldComponentUpdate() { | |
return false | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// Main | |
///////////////////////////////////////////////////////////////////////// | |
handleStockTap = () => { | |
log('handleStockTap') | |
} | |
initializeInteract = () => { | |
// Enable dragging on card | |
Interact('.card_component.face_up') | |
.draggable({ max: Infinity }) | |
.on('dragstart', (event) => { | |
// Save values on interaction | |
event.interaction.x = parseInt(event.target.getAttribute('data-x'), 10) || parseInt($(event.target).css('left'), 10) || 0 | |
event.interaction.y = parseInt(event.target.getAttribute('data-y'), 10) || parseInt($(event.target).css('top'), 10) || 0 | |
}) | |
.on('dragmove', (event) => { | |
// Set new values on interaction | |
event.interaction.x += event.dx | |
event.interaction.y += event.dy | |
// Move card | |
event.target.style.left = `${event.interaction.x}px` | |
event.target.style.top = `${event.interaction.y}px` | |
}) | |
.on('dragend', (event) => { | |
// Save values on target | |
event.target.setAttribute('data-x', event.interaction.x) | |
event.target.setAttribute('data-y', event.interaction.y) | |
}) | |
// Enable dropping of cards | |
Interact('.tableau', '.card_component') | |
.dropzone({ overlap: 0.60 }) | |
.on('dropactivate', (event) => { | |
// Set class on drop areas | |
$(event.target).addClass('drop_active') | |
}) | |
.on('dragenter', (event) => { | |
// Set class on drop area and card | |
$(event.target).addClass('drop_target') | |
$(event.relatedTarget).addClass('can_drop') | |
}) | |
.on('dragleave', (event) => { | |
// Remove class on drop area and card | |
$(event.target).removeClass('drop_target') | |
$(event.relatedTarget).removeClass('can_drop') | |
}) | |
.on('drop', (event) => { | |
}) | |
.on('dropdeactivate', (event) => { | |
// Remove class on drop area and card | |
$(event.target).removeClass('drop_target') | |
$(event.relatedTarget).removeClass('can_drop') | |
}) | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// Helpers | |
///////////////////////////////////////////////////////////////////////// | |
positionCardsOnBoard = () => { | |
// Loop through stock | |
this.props.board.stock.forEach((card, cardIndex) => { | |
// Set position of card | |
$(`#card_${card.suit}_${card.rank}`).css({ 'z-index': 1000 + cardIndex, top: this.containerPosition('#stock').top, left: this.containerPosition('#stock').left }) | |
// Set data on card | |
$(`#card_${card.suit}_${card.rank}`).attr('data-container', 'stock') | |
// Remove shadow from card | |
$(`#card_${card.suit}_${card.rank}`).find('.front, .back').css({ 'box-shadow': 'inset 0px 0px 0px 1px rgba(0,0,0,0.65), 0px 0px 8px 0px rgba(0,0,0,0)' }) | |
}) | |
// Set shadow on stock | |
$('.stock_shadow').css({ 'box-shadow': `0px 0px 16px 0px rgba(0, 0, 0, ${(this.props.board.stock.count() / 52) * 0.6})` }) | |
// Loop through waste | |
this.props.board.waste.forEach((card, cardIndex) => { | |
// Set position of card | |
$(`#card_${card.suit}_${card.rank}`).css({ 'z-index': 1000 + cardIndex, top: this.containerPosition('#waste').top, left: this.containerPosition('#waste').left + (32 * cardIndex) }) | |
// Set data on card | |
$(`#card_${card.suit}_${card.rank}`).attr('data-container', 'waste') | |
// Add shadow to card | |
$(`#card_${card.suit}_${card.rank}`).find('.front, .back').css({ 'box-shadow': 'inset 0px 0px 0px 1px rgba(0,0,0,0.65), 0px 0px 8px 0px rgba(0,0,0,0.1)' }) | |
}) | |
// Loop through foundation | |
Object.forEach(this.foundations(), (foundation, foundationKey) => { | |
// Initial variables | |
const foundationPosition = this.containerPosition(`#${foundationKey.underscore()}`) | |
// Loop through cards and move them to position | |
foundation.forEach((card, cardIndex) => { | |
// Set position on card | |
$(`#card_${card.suit}_${card.rank}`).css({ 'z-index': 1000 + cardIndex, top: foundationPosition.top, left: foundationPosition.left }) | |
// Set data on card | |
$(`#card_${card.suit}_${card.rank}`).attr('data-container', foundationKey.underscore()) | |
// Add shadow to card | |
$(`#card_${card.suit}_${card.rank}`).find('.front, .back').css({ 'box-shadow': 'inset 0px 0px 0px 1px rgba(0,0,0,0.65), 0px 0px 8px 0px rgba(0,0,0,0.1)' }) | |
}) | |
}) | |
// Loop through tableau | |
Object.forEach(this.tableaus(), (tableau, tableauKey) => { | |
// Initial variables | |
const tableauPosition = this.containerPosition(`#${tableauKey.underscore()}`) | |
const tableauCardMargin = this.tableauCardMargin(tableau.count()) | |
// Loop through cards and move them to position | |
tableau.forEach((card, cardIndex) => { | |
// Set position on card | |
$(`#card_${card.suit}_${card.rank}`).css({ 'z-index': 1000 + cardIndex, top: tableauPosition.top + (tableauCardMargin * cardIndex), left: tableauPosition.left }) | |
// Set data on card | |
$(`#card_${card.suit}_${card.rank}`).attr('data-container', tableauKey.underscore()) | |
// Add shadow to card | |
$(`#card_${card.suit}_${card.rank}`).find('.front, .back').css({ 'box-shadow': 'inset 0px 0px 0px 1px rgba(0,0,0,0.65), 0px 0px 8px 0px rgba(0,0,0,0.1)' }) | |
}) | |
}) | |
// Change state to props | |
this.state.deck = this.props.deck | |
this.state.board = this.props.board | |
} | |
containerPosition = (container) => { | |
return { top: $(container).position().top + $(container).parent().position().top, left: $(container).position().left + $(container).parent().position().left } | |
} | |
tableauCardMargin = (cardAmount) => { | |
// Get height of tableau | |
const cardHeight = 142 | |
const calculatedHeight = (margin) => cardHeight + ((cardAmount - 1) * margin) | |
const tableauHeight = $("#bottom_row").height() | |
// Calculate correct margin | |
if (calculatedHeight(32) < tableauHeight) { | |
return 32 | |
} else if (calculatedHeight(24) < tableauHeight) { | |
return 24 | |
} else if (calculatedHeight(16) < tableauHeight) { | |
return 16 | |
} else if (calculatedHeight(8) < tableauHeight) { | |
return 8 | |
} else { | |
return 8 | |
} | |
} | |
tableaus = () => { | |
return { tableauOne: this.props.board.tableauOne, tableauTwo: this.props.board.tableauTwo, tableauThree: this.props.board.tableauThree, tableauFour: this.props.board.tableauFour, tableauFive: this.props.board.tableauFive, tableauSix: this.props.board.tableauSix, tableauSeven: this.props.board.tableauSeven } | |
} | |
foundations = () => { | |
return { foundationClover: this.props.board.foundationClover, foundationHeart: this.props.board.foundationHeart, foundationSpade: this.props.board.foundationSpade, foundationDiamond: this.props.board.foundationDiamond } | |
} | |
///////////////////////////////////////////////////////////////////////// | |
// Render method | |
///////////////////////////////////////////////////////////////////////// | |
render() { | |
return ( | |
<div className="center-container board_component"> | |
<div className="center-row"> | |
<div className="container"> | |
<div className="row" id="bottom_row"> | |
<div className="col-1"> | |
<div className="tableau one" id="tableau_one" /> | |
</div> | |
<div className="col-1"> | |
<div className="tableau two" id="tableau_two" /> | |
</div> | |
<div className="col-1"> | |
<div className="tableau three" id="tableau_three" /> | |
</div> | |
<div className="col-1"> | |
<div className="tableau four" id="tableau_four" /> | |
</div> | |
<div className="col-1"> | |
<div className="tableau five" id="tableau_five" /> | |
</div> | |
<div className="col-1"> | |
<div className="tableau six" id="tableau_six" /> | |
</div> | |
<div className="col-1"> | |
<div className="tableau seven" id="tableau_seven" /> | |
</div> | |
</div> | |
<div className="row" id="top_row"> | |
<div className="col-1"> | |
<div className="stock_shadow" /> | |
<div className="stock" id="stock" /> | |
</div> | |
<div className="col-2"> | |
<div className="waste" id="waste" /> | |
</div> | |
<div className="col-1"> | |
<div className="foundation_background clover" style={{ backgroundImage: `url(${assetPath('medium_clover_foundation', 'png')})` }} /> | |
<div className="foundation clover" id="foundation_clover" /> | |
</div> | |
<div className="col-1"> | |
<div className="foundation_background heart" style={{ backgroundImage: `url(${assetPath('medium_heart_foundation', 'png')})` }} /> | |
<div className="foundation heart" id="foundation_heart" /> | |
</div> | |
<div className="col-1"> | |
<div className="foundation_background spade" style={{ backgroundImage: `url(${assetPath('medium_spade_foundation', 'png')})` }} /> | |
<div className="foundation spade" id="foundation_spade" /> | |
</div> | |
<div className="col-1"> | |
<div className="foundation_background diamond" style={{ backgroundImage: `url(${assetPath('medium_diamond_foundation', 'png')})` }} /> | |
<div className="foundation diamond" id="foundation_diamond" /> | |
</div> | |
</div> | |
<div className="card_container" id="card_container"> | |
<For each="card" of={this.props.board.stock}> | |
<CardComponent card={card} key={`${card.suit}_${card.rank}`} ref={`card_${card.suit}_${card.rank}`} /> | |
</For> | |
</div> | |
</div> | |
</div> | |
</div> | |
) | |
} | |
} | |
export default BoardComponent |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment