Field/FieldPlacePrivate.js

// ПРИВАТНЫЕ ФУНКЦИИ РАЗМЕЩЕНИЯ КАРТ

/**
* Считает отступы сверху и слева
* @param {number} requiredActiveWidth необходимая ширина для размещения карт
* @param {number} areaActiveWidth     реальная ширина для размещения карт
* @param {number} areaWidth           ширина поля
* @param {number} areaHeight          высота поля
* @param {number} cardWidth           ширина карты
* @param {number} cardHeight          высота карты
*
* @return {object} Возвращает отступы `{top, left}`
*/
Field.prototype._calculateMargin = function(requiredActiveWidth, areaActiveWidth, areaWidth, areaHeight, cardWidth, cardHeight){
	var offset = this.style.margin + this.style.padding;
	var leftMargin = cardWidth/2 + offset;
	var topMargin = 0;
	// Если ширина карт меньше ширины поля, устанавливаем отступ
	// По умолчанию отступ слева уже указан
	if(requiredActiveWidth != areaActiveWidth){
		switch(this.style.horizontalAlign){
		case 'center':
			leftMargin = (areaWidth - requiredActiveWidth)/2;
			break;
		case 'right':
			leftMargin = areaWidth - requiredActiveWidth - cardWidth/2 - offset;
			break;
		case 'centerLeft':
			leftMargin += this.area.width/2 - this.style.padding - this.style.margin - this.style.minActiveSpace/2 - skinManager.skin.width/2;
			break;
		}
	}

	// Отступ сверху
	switch(this.style.verticalAlign){

	// Выравнивание по верхнему краю
	case 'top':
		topMargin = offset + cardHeight/2;
		break;

	// Выравнивание по нижнему краю
	case 'bottom':
		topMargin = areaHeight - offset - cardHeight/2;
		break;

	// Выравнивание по центру
	default:
		topMargin = areaHeight/2;
		break;
	}
	return {
		top: topMargin,
		left: leftMargin
	};
};

/**
* Создает массив задержек.
* @param {boolean} noDelay все задержки равны нулю
*
* @return {array} Возращает массив задержек.
*/
Field.prototype._createDelayArray = function(noDelay){
	var delayArray = [];

	for(var i = 0; i < this.cards.length; i++){

		var localDelay = noDelay ? 0 : this._delays[this.cards[i].id]; 

		delayArray.push(localDelay);
	}
	return delayArray;
};

/**
* Считает отступ между картами.
* @param {number} activeWidth Активная ширина поля.
*
* @return {number} Возвращает отступ между картами.
*/
Field.prototype._calculateCardSpacing = function(activeWidth){
	var cardSpacing = 0;

	if(this.cards.length > 1){
		cardSpacing = activeWidth/(this.cards.length-1);
	}
	if(this.style.forcedSpace){
		if(cardSpacing < this.style.forcedSpace && this.cards.length > 1){
			console.warn("Field", this.id, "wants to space cards out by", this.style.forcedSpace + "px", "but only has", cardSpacing + "px", "available per card\n", this);
		}
		cardSpacing = Math.min(cardSpacing, this.style.forcedSpace);
	}
	return cardSpacing;
};

Field.prototype._calculateShift = function(cardWidth, cardSpacing, areaActiveWidth){
	if(this.focusedCard && cardWidth*(this.cards.length - 1) > areaActiveWidth){		
		return cardWidth*(1 + this.style.scaleDiff/2) - cardSpacing;
	}
	return 0;
};

/**
* Рассчитывает позицию для карты в соотвествии с индексом и перемещает карту в эту позицию.
* @param {Card}            card         карта
* @param {number}          index        индекс карты в поле
* @param {number}          topMargin    отступ сверху
* @param {number}          leftMargin   отступ слева
* @param {number}          cardSpacing  отступ от предыдущей карты
* @param {number}          angle        угол поворота
* @param {number}          shift        сдвиг от выделенной карты
* @param {number}          focusedIndex индекс выделенной карты в поле
* @param {number[]}        delayArray   массив задержек карт
* @param {number}          delayIndex   индекс задержки карты
* @param {BRING_TO_TOP_ON} bringToTopOn когда поднимать карту на передний план
*/
Field.prototype._moveCard = function(
	card, index, topMargin, leftMargin, cardSpacing, angle, shift, focusedIndex,
	delayArray, delayIndex, bringToTopOn
){

	// Задержка
	var delay = delayArray[index];
	if(delay === null || delay === undefined){
		delay = this.delayTime*delayIndex;
	}

	// Сдвиг текущей карты
	if(this.focusedCard && index != focusedIndex){
		shift = (index < focusedIndex) ? -shift : shift;	
	}
	else {
		shift = 0;
	}

	// Масштаб карты
	if(this.focusedCard && index == focusedIndex){
		card.setScale(1 + this.style.scaleDiff);
	}
	else{
		card.setScale(1);
	}

	// Устанавливаем сдвиг для козыря в колоде
	if(this.type == 'DECK' && card.suit !== null){
		leftMargin += skinManager.skin.trumpOffset;
	}

	// Сдвиг поднятых карт
	var bottomMargin = card.raised ? this.style.raisedOffset : 0;
	if(this.style.flipped && this.style.axis == 'horizontal' || !this.style.flipped && this.style.axis == 'vertical'){
		bottomMargin = -bottomMargin;
	}

	// Горизонтальная позиция состоит из сдвига слева, сдвига по отношению
	// к предыдущим картам, позиции базы поля и сдвига от курсора
	var x = leftMargin + cardSpacing*index + shift;

	// Вертикальная позиция состоит из двига сверху - сдвиг снизу
	var y = topMargin - bottomMargin;

	// Абсолютная позиция 
	if(this.style.axis == 'vertical'){
		var temp = x;
		x = y + this.x;
		y = temp + this.y;
	}
	else{
		x += this.x;
		y += this.y;
	}

	this._startCardMovers(card, angle, x, y, topMargin - bottomMargin, delay, bringToTopOn);
	this._fixCardDraggability(card);
};

/**
* Запускает твины карты.
* @param  {Card} card         
* @param  {number} angle        
* @param  {number} x            
* @param  {number} y           
* @param  {number} margin      
* @param  {number} delay       
* @param  {BRING_TO_TOP_ON} bringToTopOn
*/
Field.prototype._startCardMovers = function(card, angle, x, y, margin, delay, bringToTopOn){
	// Запускаем поворот карты
	this._rotateCard(card, angle, delay);

	// Запускаем перемещение карты
	if(cardControl.card != card){
		card.moveTo(x, y, this.moveTime, delay, false, true, bringToTopOn);
	}
	else{
		card.setBasePreserving(x, y);
	}
};

/**
* Выставляет перетаскиваемость карты для тех случаев, когда карта была перемещена
* без использования presetField метода.
* @param {Card} card
*/
Field.prototype._fixCardDraggability = function(card){

	if(this.style.draggable){
		if(!card.draggable){
			card.setDraggability(true);
		}
	}
	else if(card.draggable){
		card.setDraggability(false);
	}
};

/**
* Поворачивает карту и считает корректированную позицию по оси y.
* @param {Card}   card  карта
* @param {number} angle угол
* @param {number} x     позиция по x
* @param {number} y     позиция по y
* @param {number} delay время задержки
*
* @return {number} Возвращает откорректированную позицию по оси y.
*/
Field.prototype._rotateCard = function(card, angle, delay){


	// Берем сохраненный угол, если поле со случайными углами
	if(this.style.randomAngle){
		angle = this._angles[card.id] || 0;
	}
	// Поворачиваем карту, если она на дне колоды
	else if(this.type == 'DECK' && card.suit !== null){
		angle = Math.abs(angle - 90);
	}	

	card.rotateTo(angle, this.moveTime, delay);
};