включить ослабление в основанном на времени движении - PullRequest
0 голосов
/ 11 марта 2019

Я намерен включить в свой проект ускорение, основанное на времени, и мне может потребоваться некоторая помощь для этого.

В настоящее время я использую простую формулу x пикселей в секунду.

...
speed: 100,
now: undefined,
delta: undefined,
then: undefined,
setDelta: function() {
    this.now = Date.now();
    this.delta = (this.now - this.then) / 1000;
    this.then = this.now;
},
...

var slice = this.speed * this.delta;
this.x += Math.cos(rad) * slice;
this.y += Math.sin(rad) * slice;

При этом мой объект движется со скоростью 100 пикселей в секунду. Анимация, однако, очень скучная, поэтому идея сделать ее более интересной, заставив ее начать медленно, ускоряться до половины расстояния, а затем снова начинать замедляться, пока не достигнет цели.

Я нашел этот список функций замедления для javascript click .

Я думаю, что тот, который кажется точным, будет иметь некоторый плавный синус, как этот:

easeInOutSin: function (t) {
    return (1 + Math.sin(Math.PI * t - Math.PI / 2)) / 2;
}

Однако проблема в том, что я не могу понять, как «связать» эту формулу с моим кодом (представленным выше).

По ссылке ребята утверждают, что t - это параметр от 0 до 1, и я думаю, что, вероятно, нужно изменить это speed. Может быть, кто-то может помочь.

это демонстрационный фрагмент для эксперимента:

let distance = (p) => Math.sqrt((p.x - p.dx) * (p.x - p.dx) + (p.y - p.dy) * (p.y - p.dy)),
	rftv = (p) => Math.atan2(p.dy - p.y, p.dx - p.x);

let cvs = document.createElement('canvas'),
	ctx = cvs.getContext('2d'),
	w = cvs.width = 700,
	h = cvs.height = 200,
	cx = w / 2,
	cy = h / 2;

let obj = {
	x: 100,
	y: cy,
	speed: 100,
	dx: 600,
	dy: cy,
	run: function() {
		if(!this.moving) { return; }
		let d = distance(this);
		if(d < 1) {
			this.end();
		}
		this.setDelta();
		var slice = this.speed * this.delta;
		let rad = rftv(this);
		this.x += Math.cos(rad) * slice;
		this.y += Math.sin(rad) * slice;
	},
	now: undefined,
	delta: undefined,
	then: undefined,
	setDelta: function() {
		this.now = Date.now();
		this.delta = (this.now - this.then) / 1000;
		this.then = this.now;
	},
	moving: false,
	start: function() {
		this._started_ = Date.now();
		this.then = Date.now();
		this.moving = true;
	},
	end: function() {
		this.moving = false;
		console.log( Date.now() - this._started_, 'should be close to 5000' );
	}
};

let render = () => {
	ctx.fillStyle = '#ccc';
	ctx.fillRect(0, 0, w, h);

	ctx.beginPath();
	ctx.arc(obj.x, obj.y, 10, 0, Math.PI * 2);
	ctx.closePath();
	ctx.strokeStyle = 'red';
	ctx.stroke();
	obj.run();

	requestAnimationFrame(render);
};

document.body.appendChild(cvs);
render();

obj.start();

1 Ответ

1 голос
/ 11 марта 2019

Выбранная вами функция замедления зависит от времени. Это означает, что он возвращает соотношение от 0 до 1 в зависимости от времени, которое также находится в диапазоне от 0 до 1. Это означает, что вы должны рассчитать соотношение прошедшего времени и общего времени анимации, которое вы хотите. Затем, чтобы вычислить позицию, вам нужно применить возвращенное отношение к общему расстоянию, которое вы хотите пройти (this.dx - this.startX), и добавить его в начальную позицию.

Обратите внимание, что в следующих примерах я проигнорировал ваши расчеты rad и this.then, я действительно не понял, что вы имели в виду под rad, и, как вы видите, замедление должно зависеть от общего пройденного расстояния. и общее время анимации. Так что понятия скорости тоже нет, или вместо этого вы должны применить его к общему расстоянию / времени анимации.

let distance = (p) => Math.sqrt((p.x - p.dx) * (p.x - p.dx) + (p.y - p.dy) * (p.y - p.dy)),
	rftv = (p) => Math.atan2(p.dy - p.y, p.dx - p.x),
  easeInOutSin = function (t) {
    return (1 + Math.sin(Math.PI * t - Math.PI / 2)) / 2;
  };

let cvs = document.createElement('canvas'),
	ctx = cvs.getContext('2d'),
	w = cvs.width = 700,
	h = cvs.height = 200,
	cx = w / 2,
	cy = h / 2;

let obj = {
	x: 100,
  startX: 100,
	y: cy,
	//speed: 100,
	dx: 600,
	dy: cy,
	run: function() {
		if(!this.moving) { return; }
		let d = distance(this);
		if(d < 1) {
			this.end();
		}
		this.setDelta();
		/*var slice = this.speed * this.delta;
		let rad = rftv(this);
		this.x += Math.cos(rad) * slice;*/
		this.x = this.startX + (this.delta * (this.dx - this.startX));
		//this.y += Math.sin(rad) * slice;
	},
	now: undefined,
	delta: undefined,
	//then: undefined,
	setDelta: function() {
		this.now = Date.now();
		this.delta = easeInOutSin( (this.now - this._started_) / 5000 ); //(this.now - this.then) / 1000;
		//this.then = this.now;
	},
	moving: false,
	start: function() {
		this._started_ = Date.now();
		this.then = Date.now();
		this.moving = true;
	},
	end: function() {
		this.moving = false;
		console.log( Date.now() - this._started_, 'should be close to 5000' );
	}
};

let render = () => {
	ctx.fillStyle = '#ccc';
	ctx.fillRect(0, 0, w, h);

	ctx.beginPath();
	ctx.arc(obj.x, obj.y, 10, 0, Math.PI * 2);
	ctx.closePath();
	ctx.strokeStyle = 'red';
	ctx.stroke();
	obj.run();

	if(obj.moving){ requestAnimationFrame(render); }
};

document.body.appendChild(cvs);

obj.start();

render();

Вот второй пример с более продвинутой функцией замедления, адаптированной из этого ответа , которая принимает 4 параметра: истекшее время, начальные и конечные значения и общее время анимации. Возвращаемым значением является непосредственно ваша позиция x. РЕДАКТИРОВАТЬ : фиксированные примененные параметры, должны быть 0 и общее расстояние, а затем вы добавляете его в исходное положение.

let distance = (p) => Math.sqrt((p.x - p.dx) * (p.x - p.dx) + (p.y - p.dy) * (p.y - p.dy)),
	rftv = (p) => Math.atan2(p.dy - p.y, p.dx - p.x),
    easeInOutSine = (t, startVal, endVal, totalTime) => (-endVal/2 * (Math.cos(Math.PI*t/totalTime) - 1) + startVal);

let cvs = document.createElement('canvas'),
	ctx = cvs.getContext('2d'),
	w = cvs.width = 700,
	h = cvs.height = 200,
	cx = w / 2,
	cy = h / 2;

let obj = {
	x: 100,
    startX: 100,
	y: cy,
	//speed: 100,
	dx: 600,
	dy: cy,
	run: function() {
		if(!this.moving) { return; }
		let d = distance(this);
		if(d < 1) {
			this.end();
		}
		this.setDelta();
		/*var slice = this.speed * this.delta;
		let rad = rftv(this);
		this.x += Math.cos(rad)  * slice;*/
		this.x = this.startX + this.delta;
		//this.y += Math.sin(rad) * slice;
	},
	now: undefined,
	delta: undefined,
	//then: undefined,
	setDelta: function() {
		this.now = Date.now();
		this.delta = easeInOutSine((this.now - this._started_), 0, (this.dx - this.startX), 5000);//(this.now - this.then) / 1000;
		//this.then = this.now;
	},
	moving: false,
	start: function() {
		this._started_ = Date.now();
		this.then = Date.now();
		this.moving = true;
	},
	end: function() {
		this.moving = false;
		console.log( Date.now() - this._started_, 'should be close to 5000' );
	}
};

let render = () => {
	ctx.fillStyle = '#ccc';
	ctx.fillRect(0, 0, w, h);

	ctx.beginPath();
	ctx.arc(obj.x, obj.y, 10, 0, Math.PI * 2);
	ctx.closePath();
	ctx.strokeStyle = 'red';
	ctx.stroke();
	obj.run();

	if(obj.moving){ requestAnimationFrame(render); }
};

document.body.appendChild(cvs);

obj.start();

render();

Надеюсь, вы лучше понимаете, как это работает, удачи!

Дополнительное примечание: Я также инвертировал obj.start(); и render(); и добавил условие к requestAnimationFrame, чтобы избежать бесконечного цикла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...