Нет, речь в посте пойдет не о гипножабе, которая через монитор закодирует вас на похудение, или еще о чем-то таком. Речь пойдет о счетчике потраченных за компьютером калорий. Точнее, потраченных на сайтах. А также о некоторых tips-n-tricks с анимацией.
Мы давно хотели сделать нечто подобное, а тут подвернулась возможность и мы с радостью принялись за дело. И получился тренажерный зал онлайн LazyS. Конечно, никто не претендует на абсолютную точность и, безусловно, это фановая игрушка, но цифры взяты и не с потолка. К тому же, там довольно забавные тренажеры.
Как это работает
Человек в среднем в сутки тратит 1200 ккал. Это показатель не для спортсменов или работников физического труда, а как раз для человека среднестатистического (работающего в офисе). На активную фазу суток (т.е. на бодрствование), в качестве расчетной точки, мы выделили 1000 ккал.
С ростом пульса — увеличивается и расход энергии. Соответственно, дальше считать довольно просто: замеряем скорость движения, не влияющую на пульс, берем её за опорную, приводим коэффициент для бОльших скоростей, замеряем время и умножаем на средний расход калорий за это время.
Конечно, это все очень приблизительно, но для поверхностной оценки — вполне пойдет.
Где это работает
Во-первых, на самом сайте есть забавные тренажеры. Во-вторых, есть букмарклет, который лучше всего работает на ajax-driven сайтах (в частности, отлично получается на vk.com). Заходите и смотрите, сколько тратите энергии на движения мышкой).
Немного о коде
Расчет дистанции, пройденной курсором — достаточно тривиальная задача (привет, теорема Пифагора). А вот о чем было бы интересно рассказать, так это о реализации гири (или «офисных вертолетов»), которая, на мой взгляд, получилась очень интересной.
Гиря должна вращаться по кругу, захваченная мышкой. Конечно, она должна удерживаться, когда мышка покидает гирю. А еще нам нужно считать угловую скорость и рассчитывать тень под ней.
Аниматор jQuery по координатам гири тут не поможет, пришлось искать другое решение. И оно нашлось довольно интересное: в качестве основы для вращения гири мы будем использовать отдельный элемент (#center) и манипулировать его margin-bottom.
При захвате гири мышкой — начинаем фиксировать угол, образующийся между курсором и центром круга и записываем его значение в margin-bottom элемента #center.
var a = centerPos.left - e.pageX; var b = centerPos.top - e.pageY; var c = Math.sqrt( Math.pow(a,2)+Math.pow(b,2) ); var angle = Math.asin(b/c) * -1;
Одновременно с этим рассчитываем положение гири, исходя из полученного угла (простым прямоугольным треугольником, где радиус трэка — его гипотенуза):
var a2 = R * Math.cos(angle); var b2 = R * Math.sin(angle); girja.css({'top': b2, 'left': a2}); // гиря позиционирована абсолютно относительно контейнера с position:relative, поэтому компенсировать ничего не нужно
При отрыве мышки, или когда её плечо относительно центра становится менее 40 точек (чтобы нельзя было крутить вокруг центра вплотную и накручивать счетчик) в действие вступает аниматор jQuery, который анимирует margin-bottom элемента #center до значения pi/2 (в радианах). Вся соль в двух вещах:
- easeOutElastic easing, дающий красивый инерционный возврат
- step-функция аниматора.
Степ-функция как раз и анимирует саму гирю. На каждом кадре анимации мы получаем текущий угол и по нему выставляем координаты гири:
step: function(now, fx){ var a2 = R * Math.cos(now); var b2 = R * Math.sin(now); if (aUp < 0) { a2 = a2 * -1 }; girja.css({'top': b2, 'left': a2}); }
Отдельно про тень: ее хотелось сделать живой и красивой. После того, что вышло с расчетом координат гири, отрисовать тень должным образом не составило труда. Но есть одна хитрость:
shad.css({'left': a2, 'opacity': Math.pow(b2 / R,15)});
Горизонтальное смещение тени равно смещению гири. А вот прозрачность считается делением вертикального сдвига гири на радиус трека, где оба члена возведены в 15ю степень.
Зачем такое приведение? Во-первых, степень должна быть нечетной, чтобы сохранить знак (когда гиря поднимается выше центра — смещение становится отрицательным и прозрачность не станет нарастать). Во-вторых степени «регулируют скорость приближения к границе». В кавычках, потому что описание совсем не математическое, но как это правильнее сказать — не знаю.
Когда b2 и R равны, деление, независимо от степени, даст 1. А вот по мере уменьшения b2 (горизонтального сдвига гири), чем выше степень обоих членов — тем быстрее результат деления приблизится к 0 и тень пропадет.
Из этого всего в итоге получился такой код:
$('#karusel-machine').each(function(){ var girja = $('#girja'); var shad = $('#shadow'); var center = $('#center'); var angle = 0; var R = 193; var win = $(window); var bottomRad = (Math.PI / 2).toFixed(3); girja.on('mousedown', function(e){ center.stop(true, false); win.off('mouseup.girja'); e.preventDefault(); var centerPos = {left:center.offset().left, top: center.offset().top}; var baseAngle = parseFloat(center.css('margin-bottom')); var startAngle = baseAngle; var moveStart = new Date().getTime(); var moveEnd = 0; win.on('mousemove.girja', function(e){ var a = centerPos.left - e.pageX; var b = centerPos.top - e.pageY; var c = Math.sqrt( Math.pow(a,2)+Math.pow(b,2) ); if ( c < 40 ) { win.trigger('mouseup.girja', {pageX:e.pageX, pageY: e.pageY}); } var angle = Math.asin(b/c) * -1; var delta = Math.abs(angle - startAngle); moveEnd = new Date().getTime(); var t = moveEnd-moveStart; var V = delta/t; var k = V/0.005; if (k<1) k=1; var spentCals = calsPerMs*k*t; if (typeof(spentCals) == "number" && spentCals > 0) { window.calsSpent += spentCals; var kilocals = window.calsSpent / 1000; calsSpantInformer.text(kilocals.toFixed(2)); } center.css({'margin-bottom':angle}); startAngle = angle; moveStart = moveEnd; var a2 = R * Math.cos(angle); var b2 = R * Math.sin(angle); if (a > 0) { a2 = a2 * -1; } girja.css({'top': b2, 'left': a2}); shad.css({'left': a2, 'opacity': Math.pow((b2/R),15)}); }); win.on('mouseup.girja', function(e){ win.off('mouseup.girja'); var aUp = parseFloat(girja.css('left')); var bUp = parseFloat(girja.css('top')); win.off('mousemove.girja'); center.animate( {'margin-bottom': bottomRad }, { duration: bUp>0?2500:2000, easing: 'easeOutElastic', complete: function(){ center.stop(true, true).css({'margin-bottom': bottomRad }); win.off('mouseup.girja'); }, step: function(now, fx){ var a2 = R * Math.cos(now); var b2 = R * Math.sin(now); if (aUp < 0) { a2 = a2 * -1 }; girja.css({'top': b2, 'left': a2}); shad.css({'left': a2, 'opacity': Math.pow((b2/R),15)}); }, queue: false } ) }); }); });
Такими, достаточно нехитрыми способами, мы получили красивую и довольно живую анимацию для этого тренажера.
ссылка на оригинал статьи http://habrahabr.ru/post/155731/
Добавить комментарий