diff options
author | elioat <elioat@tilde.institute> | 2025-01-10 20:31:26 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-01-10 20:31:26 -0500 |
commit | 23a9b6fe9bd7fa07a92f42eb43ef91799de2986c (patch) | |
tree | de76bbdf54f9658ce57aa69ba2a0b451590b3691 /html | |
parent | bf01d51d4b849ce716eae0303a669841d4b914f3 (diff) | |
download | tour-23a9b6fe9bd7fa07a92f42eb43ef91799de2986c.tar.gz |
*
Diffstat (limited to 'html')
-rw-r--r-- | html/cards/index.html | 228 | ||||
-rw-r--r-- | html/cards/script.js | 183 |
2 files changed, 184 insertions, 227 deletions
diff --git a/html/cards/index.html b/html/cards/index.html index 537920b..4e38953 100644 --- a/html/cards/index.html +++ b/html/cards/index.html @@ -17,232 +17,6 @@ </head> <body> <canvas id="cards"></canvas> - <script> - // Constants - const CARD_WIDTH = 100; - const CARD_HEIGHT = 150; - const PADDING = 10; - const SUITS = ['❤️', '♦️', '♣️', '♠️']; - const VALUES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; - const CARD_BACK_COLOR = '#0066cc'; - const PATTERN_SIZE = 10; - - // Canvas setup - const canvas = document.getElementById('cards'); - const ctx = canvas.getContext('2d'); - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - - // Pure functions - const shuffle = array => [...array] - .reduce((acc, _, i) => { - const j = Math.floor(Math.random() * (i + 1)); - [acc[i], acc[j]] = [acc[j], acc[i]]; - return acc; - }, [...array]); - - const createDeck = () => - SUITS.flatMap(suit => VALUES.map(value => ({ suit, value }))); - - const createCard = (x, y, cardData) => ({ - x: x + 20, - y: y + 20, - card: cardData, - isFaceUp: false // Cards start face down - }); - - const isPointInCard = (x, y, card) => - x >= card.x && - x <= card.x + CARD_WIDTH && - y >= card.y && - y <= card.y + CARD_HEIGHT; - - // Rendering functions - const clearCanvas = () => { - ctx.fillStyle = 'beige'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - }; - - const renderCardBack = card => { - // Draw blue background - ctx.fillStyle = CARD_BACK_COLOR; - ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); - - // Draw checkered pattern - ctx.strokeStyle = '#003366'; - for (let i = 0; i < CARD_WIDTH; i += PATTERN_SIZE) { - for (let j = 0; j < CARD_HEIGHT; j += PATTERN_SIZE) { - if ((i + j) % (PATTERN_SIZE * 2) === 0) { - ctx.fillStyle = '#0055aa'; - ctx.fillRect(card.x + i, card.y + j, PATTERN_SIZE, PATTERN_SIZE); - } - } - } - - // Draw border - ctx.strokeStyle = 'black'; - ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); - }; - - const renderCardFront = card => { - ctx.fillStyle = 'white'; - ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); - ctx.fillStyle = 'black'; - ctx.font = '20px Arial'; - ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); - ctx.fillText(card.card.value, card.x + 10, card.y + 30); - ctx.fillText(card.card.suit, card.x + 10, card.y + 60); - }; - - const renderCard = card => { - if (card.isFaceUp) { - renderCardFront(card); - } else { - renderCardBack(card); - } - }; - - const renderAllCards = cards => { - clearCanvas(); - cards.forEach(renderCard); - }; - - // State management - const gameState = { - cards: [], - draggingCard: null, - deck: shuffle(createDeck()), - stackPosition: { - x: 0, - y: 0 - } - }; - - // Add function to get top card - const getTopStackCard = () => { - return gameState.cards.find(card => - card.x === gameState.stackPosition.x && - card.y === gameState.stackPosition.y - ); - }; - - // Event handlers - const handleMouseMove = e => { - if (!gameState.draggingCard) return; - - const rect = canvas.getBoundingClientRect(); - gameState.draggingCard.x = e.clientX - rect.left; - gameState.draggingCard.y = e.clientY - rect.top; - - renderAllCards(gameState.cards); - }; - - const handleMouseUp = () => { - gameState.draggingCard = null; - document.removeEventListener('mousemove', handleMouseMove); - document.removeEventListener('mouseup', handleMouseUp); - }; - - // Add this function to distribute cards evenly with overlap - const distributeCards = () => { - const totalCards = gameState.cards.length; - const cols = Math.floor((canvas.width - 20) / (CARD_WIDTH / 2)); // Allow overlap by halving the width and accounting for border - const rows = Math.ceil(totalCards / cols); // Calculate number of rows - - const cardWidthWithPadding = CARD_WIDTH / 2; // Allow overlap - const cardHeightWithPadding = CARD_HEIGHT / 2; // Allow overlap - - // Calculate starting positions - const startX = 20 + (canvas.width - (cols * cardWidthWithPadding)) / 2; // Start from 20 pixels from the left - const startY = 20 + (canvas.height - (rows * cardHeightWithPadding)) / 2; // Start from 20 pixels from the top - - // Distribute cards - gameState.cards.forEach((card, index) => { - const row = Math.floor(index / cols); - const col = index % cols; - card.x = startX + col * cardWidthWithPadding + (Math.random() * 20 - 10); // Random offset for overlap - card.y = startY + row * cardHeightWithPadding + (Math.random() * 20 - 10); // Random offset for overlap - card.isFaceUp = true; // Flip cards face up when distributed - }); - - renderAllCards(gameState.cards); - }; - - // Update handleMouseDown to include double-click detection - let lastClickTime = 0; - const handleMouseDown = e => { - const rect = canvas.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - // Handle right click for card flipping - if (e.button === 2) { // Right click - e.preventDefault(); - const clickedCard = gameState.cards.find(card => - isPointInCard(x, y, card) && - (card.x !== gameState.stackPosition.x || card.y !== gameState.stackPosition.y) - ); - if (clickedCard) { - clickedCard.isFaceUp = !clickedCard.isFaceUp; - renderAllCards(gameState.cards); - } - return; - } - - // Check for double-click - const currentTime = new Date().getTime(); - if (currentTime - lastClickTime < 300) { // 300 ms for double-click - distributeCards(); // Show all cards when double-clicked - lastClickTime = 0; // Reset last click time - return; - } - lastClickTime = currentTime; - - // Find any clicked card - const clickedCard = gameState.cards.find(card => isPointInCard(x, y, card)); - - if (clickedCard) { - // If it's a card in the stack, handle drawing it - if (clickedCard.x === gameState.stackPosition.x && - clickedCard.y === gameState.stackPosition.y) { - clickedCard.isFaceUp = true; - } - - // Allow dragging regardless of where the card is - gameState.draggingCard = clickedCard; - document.addEventListener('mousemove', handleMouseMove); - document.addEventListener('mouseup', handleMouseUp); - } - }; - - // Initialize game - const initializeGame = () => { - // Create a full deck of cards - gameState.deck = shuffle(createDeck()); // Ensure we have a full deck - gameState.cards = gameState.deck.map(cardData => createCard(0, 0, cardData)); // Create cards with initial position (0, 0) - - // Draw cards in a pile with their reverse side showing - gameState.cards.forEach((card, index) => { - card.x = 20; // Pile at 20 pixels from the left - card.y = 20 + index * 5; // Stack cards with a slight offset for visibility - }); - - clearCanvas(); - renderAllCards(gameState.cards); - - // Add event listeners - canvas.addEventListener('mousedown', handleMouseDown); - canvas.addEventListener('contextmenu', e => e.preventDefault()); - }; - - // Start the game - initializeGame(); - - // Clean up on window unload - window.addEventListener('unload', () => { - canvas.removeEventListener('mousedown', handleMouseDown); - canvas.removeEventListener('contextmenu', e => e.preventDefault()); - }); - </script> + <script src="script.js"></script> </body> </html> \ No newline at end of file diff --git a/html/cards/script.js b/html/cards/script.js new file mode 100644 index 0000000..64c145b --- /dev/null +++ b/html/cards/script.js @@ -0,0 +1,183 @@ +// Constants +const CARD_WIDTH = 100; +const CARD_HEIGHT = 150; +const PADDING = 10; +const SUITS = ['❤️', '♦️', '♣️', '♠️']; +const VALUES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; +const CARD_BACK_COLOR = '#0066cc'; +const PATTERN_SIZE = 10; + +// Canvas setup +const canvas = document.getElementById('cards'); +const ctx = canvas.getContext('2d'); +canvas.width = window.innerWidth; +canvas.height = window.innerHeight; + +// Pure functions +const shuffle = array => [...array] + .reduce((acc, _, i) => { + const j = Math.floor(Math.random() * (i + 1)); + [acc[i], acc[j]] = [acc[j], acc[i]]; + return acc; + }, [...array]); + +const createDeck = () => + SUITS.flatMap(suit => VALUES.map(value => ({ suit, value }))); + +const createCard = (x, y, cardData) => ({ + x: x + 20, + y: y + 20, + card: cardData, + isFaceUp: false // Cards start face down +}); + +// Function to check if a point is within a card +const isPointInCard = (x, y, card) => + x >= card.x && + x <= card.x + CARD_WIDTH && + y >= card.y && + y <= card.y + CARD_HEIGHT; + +// Rendering functions +const clearCanvas = () => { + ctx.fillStyle = 'beige'; + ctx.fillRect(0, 0, canvas.width, canvas.height); +}; + +const renderCardBack = card => { + // Draw blue background + ctx.fillStyle = CARD_BACK_COLOR; + ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + + // Draw checkered pattern + ctx.strokeStyle = '#003366'; + for (let i = 0; i < CARD_WIDTH; i += PATTERN_SIZE) { + for (let j = 0; j < CARD_HEIGHT; j += PATTERN_SIZE) { + if ((i + j) % (PATTERN_SIZE * 2) === 0) { + ctx.fillStyle = '#0055aa'; + ctx.fillRect(card.x + i, card.y + j, PATTERN_SIZE, PATTERN_SIZE); + } + } + } + + // Draw border + ctx.strokeStyle = 'black'; + ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); +}; + +const renderCardFront = card => { + ctx.fillStyle = 'white'; + ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + ctx.fillStyle = 'black'; + ctx.font = '20px Arial'; + ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + ctx.fillText(card.card.value, card.x + 10, card.y + 30); + ctx.fillText(card.card.suit, card.x + 10, card.y + 60); +}; + +const renderCard = card => { + if (card.isFaceUp) { + renderCardFront(card); + } else { + renderCardBack(card); + } +}; + +const renderAllCards = cards => { + clearCanvas(); + cards.forEach(renderCard); +}; + +// State management +const gameState = { + cards: [], + draggingCard: null, + deck: shuffle(createDeck()), + stackPosition: { + x: 0, + y: 0 + } +}; + +// Event handlers +const handleMouseMove = e => { + if (!gameState.draggingCard) return; + + const rect = canvas.getBoundingClientRect(); + gameState.draggingCard.x = e.clientX - rect.left; + gameState.draggingCard.y = e.clientY - rect.top; + + renderAllCards(gameState.cards); +}; + +const handleMouseUp = () => { + gameState.draggingCard = null; + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); +}; + +// Define handleMouseDown before using it +const handleMouseDown = e => { + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + // Handle right click for card flipping + if (e.button === 2) { // Right click + e.preventDefault(); + const clickedCard = gameState.cards.find(card => + isPointInCard(x, y, card) && + (card.x !== gameState.stackPosition.x || card.y !== gameState.stackPosition.y) + ); + if (clickedCard) { + clickedCard.isFaceUp = !clickedCard.isFaceUp; + renderAllCards(gameState.cards); + } + return; + } + + // Find the top-most clicked card + const clickedCard = gameState.cards.slice().reverse().find(card => isPointInCard(x, y, card)); + + if (clickedCard) { + // If it's a card in the stack, handle drawing it + if (clickedCard.x === gameState.stackPosition.x && + clickedCard.y === gameState.stackPosition.y) { + clickedCard.isFaceUp = true; + } + + // Allow dragging regardless of where the card is + gameState.draggingCard = clickedCard; + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + } +}; + +// Initialize game +const initializeGame = () => { + // Create a full deck of cards + gameState.deck = shuffle(createDeck()); + gameState.cards = gameState.deck.map(cardData => createCard(0, 0, cardData)); + + // Draw cards in a pile with their reverse side showing + gameState.cards.forEach((card, index) => { + card.x = 20; // Pile at 20 pixels from the left + card.y = 20 + index * 5; // Stack cards with a slight offset for visibility + }); + + clearCanvas(); + renderAllCards(gameState.cards); + + // Add event listeners + canvas.addEventListener('mousedown', handleMouseDown); + canvas.addEventListener('contextmenu', e => e.preventDefault()); +}; + +// Start the game +initializeGame(); + +// Clean up on window unload +window.addEventListener('unload', () => { + canvas.removeEventListener('mousedown', handleMouseDown); + canvas.removeEventListener('contextmenu', e => e.preventDefault()); +}); \ No newline at end of file |