Card/CardControl/CardControlCard.js

// АПДЕЙТ И ТАЙМЕР НАЖАТИЯ КОНТРОЛИРУЕМОЙ КАРТЫ

/**
* Обновление позиции карты и хвоста.
*/
CardControl.prototype._updateCard = function(){
	if(!this.card){
		return false;
	}

	// Ресетим контроллер, если карта была спрятана\удалена
	if(!this.card.sprite.visible || this.card.mover){
		this.reset('card hidden or moving');
		return false;
	}

	// Возвращаем карту по нажатию правой кнопки или если она была перевернута
	if(this.pointer.rightButton.isDown || !this.card.draggable || !this.pointer.withinGame){
		// Если у карты включена физика, кидаем ее, иначе - возвращаем
		if(cardManager.physicsEnabled && this.card.sprite.body){
			this.cardThrow();
		}
		else{
			this.cardReturn();
		}
		return false;
	}

	var curTime = Date.now();

	this._updateCardPosition(curTime);
	this._updateCardAngle(curTime);
	var fields = this._cardOnValidField();
	if(fields){
		this.card.setScale(1 + fields[0].style.scaleDiff);
		if(~fields[0].validCards.indexOf(this.card)){
			fieldManager.popOutField(fields[0]);
		}
		else{
			fieldManager.resetPopOut();
		}
	}
	else{
		this.card.setScale(1.1);
		fieldManager.resetPopOut();
	}
	return true;
};

/**
* Устанавливаем позицию карты и плавно передивгаем ее к курсору.
* @param {number} curTime текущее время
*/
CardControl.prototype._updateCardPosition = function(curTime){
	var sTime, sP, mP;
	sTime = this.cardShiftEndTime - curTime;
	if(sTime > 0){
		sP = {
			x: Math.round(this.cardShiftPosition.x / (this.cardShiftDuration/game.speed)* sTime), 
			y: Math.round(this.cardShiftPosition.y / (this.cardShiftDuration/game.speed)* sTime)
		};
	}
	else{
		sP = {x:0, y:0};
	}
	mP = {
		x: this.pointer.x - this.card.x,
		y: this.pointer.y - this.card.y
	};
	this.card.setRelativePosition(mP.x - sP.x, mP.y - sP.y);
};

/**
* Устанавливает угол в зависимости от инерции карты.
* @param {number} curTime текущее время
*/
CardControl.prototype._updateCardAngle = function(curTime){

	var maxAngle = this.cardMaxMoveAngle;

	this._saveCardInertia(curTime, 300);
	
	// Вычисляем угол из средней длины вектора инерции
	var totalDistance = 0;
	for(var i = 0; i < this._inertiaHistory.length; i++){
		totalDistance += this._inertiaHistory[i][1];
	}
	var angle = totalDistance / this._inertiaHistory.length / 1.25;
	if(angle !== 0){
			angle -= angle > 0 ? Math.min(angle, this.cardMoveThreshold) : Math.max(angle, -this.cardMoveThreshold);
	}
	if(angle > maxAngle) {
	  angle = maxAngle;
	}
	if(angle < -maxAngle) {
	  angle = -maxAngle;
	}
	this.card.setAngle(angle);

	this.cardLastX = this.card.x + this.card.sprite.x;
	this.cardLastY = this.card.y + this.card.sprite.y;
};

/**
* Сохраняет текущее время и позицию карты.
* @param {number} curTime текущее время
* @param {number} maxTime позиции, запомненные больше этого времени назад, будут удалены
* @see {@link https://github.com/KyleU/solitaire.gg/blob/bf67e1622048bc32abfeef2848f74f220daa384e/app/assets/javascripts/card/CardInput.js#L53|Источник кода}
*/
CardControl.prototype._saveCardInertia = function(curTime, maxTime){
	var curX = this.card.x + this.card.sprite.x,
		curY = this.card.y + this.card.sprite.y,
		distance = {
			x: curX - this.cardLastX,
			y: curY - this.cardLastY
		};

	while(this._inertiaHistory.length && curTime - this._inertiaHistory[0][0] > maxTime) {
		this._inertiaHistory.shift();
	}
	this._inertiaHistory.push([curTime, distance.x, distance.y]);
};