Escenario: Estoy programando un módulo para dibujar una baraja de cartas en un lienzo como si fueran sostenidas por una persona.
Editar:
He limpiado la pregunta lo mejor que he podido para que sea más clara.
Lo que busco ahora :
Las coordenadas X,Y de cada estrella que representan el centro de cada carta, así como su rotación desde ese punto para formar un semicírculo.
Para que sea más sencillo que antes, ahora también se puede asumir:
- El ángulo total que cubren las tarjetas es 90º .
- Hay 5 tarjetas .
- La carta del medio siempre estará en X = 0
- Cada tarjeta final estará en una rotación de 30º (el lado izquierdo será -30º)
- El lienzo tiene un tamaño fijo. Nota: He puesto 200 como altura, aunque es un número algo arbitrario y ni siquiera sé si ayuda o no.
También debo mencionar que el dibujo no está exactamente a escala, lo hice lo mejor que pude con una herramienta primitiva para dibujar formas geométricas
Solución:
La fuente de mi solución se puede encontrar aquí: https://github.com/tcrosen/playing-cards
Puede encontrar una demostración en funcionamiento aquí: http://tcrosen.github.com/playing-cards/demo.html
function drawHand(el, originX, originY, numberOfCards, cardWidth, cardHeight, showOrigin) {
// The angle off the origin that each card will be referenced from
// The +1 is added because we want the first card to be positioned above the origin
var angle = 180 / (numberOfCards + 1);
// How far each card will be from the origin of the hand.
// This is proportional to the size of the card so that larger cards avoid too much overlap
var radius = cardWidth * 1.2;
// Through trial & error I determined a small hand (3-5 cards) looks most realistic
// when the end cards are at a rotation of 30 degrees (90 - 5 * 12). However when larger hands are created
// the end cards must be rotated at a larger angle in order to be "held" properly. Anything that would
// calculate to an angle > 30 (6 cards or more) is simply capped at 45 degrees.
var endRotation = 12 * numberOfCards > 60 ? 45 : 90 - 12 * numberOfCards;
// Find an equal angle to split the cards across the entire hand
var rotationIncrement = endRotation * 2 / (numberOfCards + 1);
// If the user wants to see the origin for debugging/design purposes, show an X there
if (showOrigin) {
$(el).append($('<span>X</span>').css('color', 'red').css('position', 'absolute').css('top', originY + 'px').css('left', originX + 'px'));
}
// Loop through each card
// *Note: I start at 1 (instead of 0) in order to avoid multiplying by 0 and ending up with flat angles.
// If you are using an array of cards (eventual scenario) you would need to account for the 0 index
for (var i = 1; i <= numberOfCards; i++) {
// Set the card rotation - always rotate from the end point so use the card number as a multiplier
var rotation = endRotation - rotationIncrement * i;
// The X,Y coordinates of each card.
// Note that the origin X,Y is added to each coordinate as a hand would almost never be generated from 0,0
// on an HTML canvas.
var x = radius * Math.cos(toRadians(angle * i)) + originX;
var y = radius * Math.sin(toRadians(-angle * i)) + originY;
// This next algorithm is used to push the cards "up" by a larger amount the further you get from the middle of the hand.
// This is done because a higher number of cards will start to push down and form a sharper circle.
// By moving them up it evens out the semi-circle to appear like something that would be more realistically held by a human.
// And as usual, this value is affected by existing variables to always position the hand based on its previous metrics.
y = y - Math.abs(Math.round(numberOfCards / 2) - i) * rotationIncrement;
// HTML positions elements relative to the top left corner, but the CSS3 "rotation" property uses the center.
// So I cut the values in half and use the center to position the cards.
// *Note: I realize both this and the previous line could have been included in the first X,Y calculation. They are separated for clarity.
x = x - cardWidth / 2;
y = y - cardHeight / 2;
// Create the card using my jQuery plugin
var $card = $('<div></div>').card({
width: cardWidth,
text: i,
rotation: rotation,
top: y,
left: x
});
// Draw it in the parent element
$(el).append($card);
}
}
// Helper function to convert to radians from degrees since Javascript's Math library defaults to radians.
function toRadians(degrees) {
return degrees * Math.PI / 180;
}
Aquí hay algunas capturas de pantalla de los resultados en acción con diferentes valores: