Pull to refresh

Comments 21

Спасибо за пост. Есть некоторые замечания.

setInterval — это очень плохая идея для продакшена. Лучше — вложенные setTimeout.
Но с часами отдельная история: если хочется попадать в секунды, то нужно каждый раз подстраивать время таймаута, т.к. из-за внутреннего устройства JS (EventLoop), таймер выполняется не точно в назначенное время, а где-то около него.

И еще, очень хочется демку, чтобы сразу поиграть с ней.
Спасибо Вам за замечания.
Вы имеете ввиду то, что метод setInterval относительно медленный и из-за него часы могут сбиваться с точного времени?
Просто я помню, что setTimeout выполняет код один раз и, честно говоря, не знал, что вложенные setTimeout будут в целом работать быстрее одного setInterval.
Насчет демки, вот полный код: yadi.sk/d/YveCwtS5nFaFN
Если Вы сможете прокомментировать/исправить проблемные моменты в коде, то буду рад, если поделитесь им мне на почту im_prezz@mail.ru

Не быстрее, а точнее. В случае часов ничего критичного не случиться, можно просто делать интервал в 500мс. Но концептуально лучше использовать

setTimeout(функция, 1000 - (new Date()).getMilliseconds())

А в функции ставить новый таймаут и звать реквестАнимэйшенФрэйм. И уже в нём рисовать.
Дело не в скорости, а в том как работает EventLoop.
setInterval ставит задачу в него безусловно, отработала текущая итерация или нет.
В результате, если выполняемая задача начинает есть больше времени чем период интервала (по разным причинам), то задачи в очереди начнут накапливаться. В результате браузеру будет некогда заниматься другими задачами, он будет тратить все свое время на обслуживание этого setInterval.

Обойти эту проблему помогает использование setTimeout, который планирует лишь одну, следующую, итерацию вычислений. В конце который вы снова вызываете setTimeout. Таким образом задача сама себя ставит в очередь. И в итоге, если она начинает тормозить, то и планировать себя она начинает медленнее, т.е. тормозит только эта подвисшая задача.

При использовании setInterval браузер может «зафризиться», при рекурсивном setTimeout — добиться этого гораздо сложнее.

И еще раз заострю внимание: время таймаута для следующей итерации нужно вычислять каждый раз через объект Date, т.к. JS вам не гарантирует точность тайминга асинхронных вызовов. Т.е. на одной итерации у вас будет 1000ms, на другой, может 950ms, а на третей может быть 1050ms.

Возможно, в вашем случае с аналоговыми часами это не страшно — позицию стрелок вы вычисляете используя Date, но если бы вы просто делали цифровые часы, используя таймеры, то они убегали бы или отставали в зависимости от загрузки процессора и других факторов.
Ну зачем же каждую секунду делать document.getElementById("clock"), document.getElementById('myCanvas') и canvasHTML.getContext('2d')? А потом начинается «ой, HTML5 это очень непроизводительно» и «от снежинок на сайте вентилятор включается».

window.onload = function(){/*бла-бла-бла*/}
В большинстве случаев лучше просто ставить свой <script> перед </body>. window.onload нужен только если нужно дождаться загрузки всех картинок и других подобных ресурсов; у вас, вроде бы, картинок нет.
Почему Canvas, а не CSS Transform, которые во всех современных браузерах считаются за счет GPU? http://caniuse.com/#feat=transforms2d
При этом можно было бы выкинуть большую часть кода отвечающую за отрисовку. У меня стойкое ощущение, что статья валялась где-то в закромах последние лет 5 (или дольше, судя по window.onload), вы её нашли, сдули пыль и представили современному продвинутому сообществу.

И, кстати, не нужно писать, что новичкам пригодиться — нет ничего хуже, чем учить новичков устаревшими практиками. Спасибо.
Статью я написал два дня назад, всё от себя, без всякого копипаста. Сегодня опубликовали её.
Не претендую на хороший дизайн, не претендую на качественный и идеальный код.
В статье хотел описать как можно подробнее математическую логику задачи и привести рабочий(зрительной сносный) пример реализации.
Спасибо вам за ваши замечания, буду разбираться с CSS Transform.
Значит вам необходимо навёрстывать свои знания — веб очень динамичная среда, даже оставание на год крайне чувствительно для разработчика. Преобразование чисел времени в градусы слишком тривиальная задача, если вам нравится графика попробуйте поизучать SVG и D3.js, это довольно свежо и востребовательно.
В вопросе о web-виджетах нет однозначного ответа, что лучше использовать: Canvas или CSS Transforms.
Вопрос можно ставить на стадии проектирования. Тут предлагают реализацию. А почему сделано так, а не иначе — можно рассуждать бесконечно.
Мне тоже ближе CSS Transforms. Но лет 20 назад писал подобное на QBASIC. Там как раз для отрисовки использовал все формулы как автор один-в-один.
D3 и прочие рисующие библиотеки это, конечно, прекрасные технологии, очень сильно сокращающие время и трудоемкость разработки.
Но с ними та же проблема, что и с jQuery — они порождают толпы разработчиков, которые умеют библиотеку и не умеют косинусы.
В данной статье упор именно на математику, стоящую за движением стрелок, и в этом ее главная ценность.
-setInterval использовать НЕЛЬЗЯ НИКОГДА. Очень частая проблема — человек закрыл ноут, открыл, очередь из стоявших setInterval хлынула, ноут завис.
-для графики всегда стоит использовать requestAnimationFrame
-перерисовывать весь циферблат логикой не оптимально, правильней рисовать только стрелки, для «нахлестывающихся» картинок стоит либо сделать «двуслойный» прозрачный канвас (поддерживается не везде), либо перерисовывать только «задетые» изображения + рисовать белым поверх старых стрелок, либо изначально создать изображение с помощью канваса, а затем каждый раз делать «кладем нижний слой, кладем стрелки, кладем пимпочку»

и статья и правда не очень актуальная. такие писались где-то года 3 назад, сейчас же низкоуровневая работа с canvas редкость.
Ну, кому-то надо уметь и на низком уровне с канвасом работать. Тем более статья для новичков.
Новичку лучше начать с простой библиотеки, типа pixi.js.
Прекрасный официальный 101 по старой версии (API изменился ну от силы на 5%) — первая же ссылка в гугле
www.goodboydigital.com/pixi-js-tutorial-getting-started

Документацию они усложнили, да, ладно, не спорю. Раньше очень простой гайд был.
UFO just landed and posted this here
Цель-то не часы сделать (эка невидаль), а дать какие-то первичные навыки работы с канвасом. На практике часы ни на канвасе, ни на CSS сто лет никому не сдались:)
https://time.yandex.ru/ с вами не согласится, но, конечно, это единичный случай, да и часы там на SVG. А если автору нравится Canvas, то ему путь в WebGL или какую-то другую сложную пиксельную графику. Для остального есть более высокоуровневые инструменты.
Да причем тут WebGL? Начинающий поделился с начинающими туториалом соответствующего уровня.
По математике
var t_hour = 30*(d.getHours() + (1/60)*d.getMinutes());  //Определяем угол для часов


Что ж сразу и секунды не добавить? Да, будет практически не видно, но зато корректнее.

var t_sec = 6*d.getSeconds(); 
var t_min = 6*(d.getMinutes() + (1/60)*d.getSeconds()); // вы уже посчитали угол поворота секунд. 

// better

var t_min = 6 * d.getMinutes() + t_sec / 360; // 1 / 60 хороша с точки зрения понимания, но вот весь веб ленится делать казалось бы крошечные оптимизации, а потом 8 ядер не хватает 
// на всякий случай -- вместо двух "тяжелых" операций всего одна.


Если использовать requestAnimationFrame, мы можем получать текущее время с точностью до миллисекунд. Это позволит нам добавить easing и получить эффект «дерганья» секундной стрелки. Тут можно посмотреть код этих функций.

Если говорить про статью — выше уже написали, какие основные проблемы тут есть. Статьи «для начинающих» всегда хорошо, но не от самих же начинающих. Наилучшие статьи «от начинающих для начинающих» — это когда берут написанный профессионалами в области кусок кода и разбирают, почему сделано так или иначе. Конкретно в данной статье нет ничего нового, это даже не систематизированно в одном месте. Прочитав эту статью, новичок в любом случае полезет читать о канвасе что-то более подробное.

Ну а «математика», на которую вы ставите акцент, уж простите — 7-8 класс ей богу. Не тензорная алгебра уж точно. Если человек не может самостоятельно посчитать формулу как для текущей секунды получить угол поворота секундной стрелки — ему рано в программирование. (За исключением начальных классов, но в целом статья сложная для них).
Sign up to leave a comment.

Articles