История одного хабраспора



        Не так давно, просматривая глубокой ночью достаточно интересную заметку о игре на основе JS/Canvas (со своими ошибками и заблуждениями, которые были и у меня, что уж греха таить, понастальгировал всласть), я наткнулся на очередную порцию откровенно холиварных комментариев, после прочтения которых мир за окном стал серым и безрадостным, еда потеряла вкус, а любимый чай оказался несладким. И в тот момент то ли звезды сошлись, то ли срочных и важных багов и фич на вчера стало немного меньше, но я решил ввязаться в спор с достаточно резкими тезисами и вступиться за любимую технологию, которую так откровенно поливали непонятно чем. Так бы и осталось все это на уровне беспредметного перебрасывания пакетов с доводами через забор, если бы в ту же ветку не решил написать RussianSpy, и не об абстрактных попугаях, которых легче переписать в 3D, а о вполне конкретной задаче. И промелькнувшая фраза «Могу прислать ТЗ...» плавно намекнула на то, что вечер обещает быть интересным.


    Подготовка

    21.12.12, 2:29

        Конечно же в этот момент нужно было остановиться, накопать красивых демо из сети, бросить на вентилятор еще пару унций и успокоиться. Но мы не ищем легких путей…

        Дочитав последний комментарий, уже четко задевавший мое эго, не долго думая, отвечаю «присылайте ТЗ», и буквально через 5-10 минут получаю письмо в почтовом ящике Хабра с запросом контактов. Отвечаю, запрос контактов в Скайпе, приветствие, поехали. Получил архив с графикой. ТЗ оказалось достаточно простым, с одной стороны, но в то же время — интересным в качестве практики. Да и собственная гордость (подлая такая дама, ага) теперь бы мне просто не позволила ударить лицом в грязь.

        Если описывать задачу кратко — нужно было построить генератор гелиоцентрических планетарных систем с небольшим GUI, привязанным к планетам и их орбитам. В 2D. Описывалась она, по большому счету, кратко в самом диалоге, так что возникло еще несколько уточняющих вопросов. Но в общем, все было достаточно прозрачно. В 2:53 я уже во всю разглядывал то, что мне досталось и обдумывал, каким образом все это воплотить в жизнь.

    Техзадание
    Планеты вращаются вокруг звезды по часовой стрелке. Скорость вращения ближайшей  планеты — 40 сек на оборот. Время на оборот каждой последующей планеты на 20 секунд больше предыдущей. Состав системы случайный. При каждом обновлении планеты занимают случайное место на своей орбите, с которого и начинают вращение и картинка для планеты также случайна.

    При наведении мыши на планету она выделяется круглой рамкой, изображение орбиты также меняется так, как это указано в макете.

    При клике на планету выпадает меню. При наведении мыши на планету и при клике по ней анимация данной планеты останавливается, остальные планеты продолжают свое движение.

    При наведении мыши на орбиту — орбита подсвечивается, планета нет.

    Курсор мыши при наведении на планету или орбиту меняется на pointer.

    Должно работать: Opera 12+, IE9+, актуальные версии Chrome, FF и Safari под огрызок.

    21.12.12, 6:13

        В общем и целом реализация задачи к этому времени уже стала более или менее понятна и ясна. Практически первое, что я сделал — продумал, какие шаги мне нужно сделать для успешной отрисовки хотя-бы центральной звезды, и на этом остановился. Создал отдельный Virtualhost в своем dev-окружении, git-репозиторий, закомитил index.html и отправился в кровать. По пути в голове роились мысли на тему анимации, трансформаций с поворотами в offscreen-canvas, проблемы обсчета координат мыши и попутно еще тонна всяких сладостей. Из-за этого я достаточно долго не мог уснуть, но природа взяла свое.

    Процесс

    22.12.12, 21:00

        Посвятив пятницу всем незавершенным делам к своей принципиальной демо я вернулся в субботу вечером, после того, как семейство отошло ко сну и у меня появилось свобода думать, много курить и пить кофе бидонами.

        По опыту работы над FiveGUI понял, что в этом проекте нужно будет что-то подобное. Решил реализовать все через некое подобие AnimationController'а, в котором будет ссылка на основное панно рисования, и динамически собирать картинку в offscreen-canvas'ах его child-объектов. Немного покопавшись в Mozilla Developers Network и на HTML5Rocks, восстановив знания вокруг requestAnimationFrame принялся за базовую структуру проекта и объектов, которые я буду в итоге отрисовывать.

        Первым делом разбил весь проект в корне Vitrualhost'a на js/css/img, сложил всю доставшуюся в наследство графику в соответствующую папку и набросал пример структуры с ссылками на изображения. Во весь рост встала проблема предзагрузки этих самых изображений и основной контроллер обзавелся loader'ом графики. Реализовал я его, в принципе, достаточно топорно — просто сравнивая общее количество ссылок в структуре с counter'ом загруженных изображений, который обновлялся через замыкание в методе load каждого изображения.

    Общий концепт на тот момент
    //main.js
    var PlanetController = new Planets({}); // Контроллер отрисовки
    
    var resources = { //Изображения,
        stars: {
            "1": "/img/stars/1.png"
        }
        // Ресурсов на самом деле больше ^_^
    }
    
    for(var a in resources) {// Добавление ресурсов в контроллер
        for(var i in resources[a]) {
            PlanetController.pushResource(a+"-"+i, resources[a][i]);
        }
    }
    
    //Planets.js
    var Planets = function(){ /* ... */ } // "Конструктор" контроллера
    
    Planets.prototype.loadResources = function(cb) { // Загрузчик изображений
        var self = this;
        self.data.loadedResourcesCount = 0;
        self.data.resourcesCount = Helpers.objLength(self.resources);
        for(var resource in self.resources) {
            if(self.resources.hasOwnProperty(resource)) { // Жуткий костыль, который был обнаружен в самом конце, да. Не делайте так! Никогда!
                var tempImg     = new Image();
                tempImg.onload  = function() {
                    self.data.loadedResourcesCount++;
                    if(self.data.loadedResourcesCount == self.data.resourcesCount) {
                        self.startTimestamp = new Date();
                        self._flags['_resources_loaded'] = true;
                        cb();
                    }
                }
                tempImg.src = self.resources[resource];
                self.resources[resource] = tempImg;
            }
        }
    }
    



        Дальше нужно было каким-то образом после успешной загрузки ресурсов запустить сам проект на отрисовку. Я не стал заморачиваться с событийной моделью (что, в последствии, при доработке может сыграть со мной злую шутку) и решил это сделать через вызов callback'а при совпадении счетчиков, по факту — после загрузки последней картинки. Самое время, как я считал. Сам callback и все его окружение вынес в отдельный файл main.js, callback решил вызывать через call в области видимости window для того, чтобы иметь прямой доступ к своему контроллеру без геммороя (он к тому времени уже лежал именно в window).

        Подкрутил немного опечатки и явные баги, добился вызова callback'а и оповещения в консоли о том, что все изображения успешно загружены и двинулся дальше, в дебри объектов.

        Здесь наступила пора самого интересного. У меня уже появился файл Helpers.js, который предназначался для сервисных функций и всего расширяемого, что я предполагал использовать в проекте. Дальше оставалось только продумать структуру объектов.



        И она, как видно по изображению выше, родилась достаточно быстро. Каждый объект наследовался от основного родителя (в моем случае — объект Element) при помощи нехитрой комбинации через замену прототипа ( знаменитая функция Extend ). Как мне показалось — мое решение было наиболее логичным, исходя из строения любой «солнечной» системы. У нас есть основной родитель — Звезда, которая включает в себя некоторое количество Орбит. У Орбиты же есть детеныш Планета, которая уже включает в себя всплывающие окошки с информацией о себе (Popup — Float) и некоторыми действиями (Popup — Static — Options). Если присмотреться к схеме — у меня есть достаточно глобальная ошибка, которая может стать «фичей» — несколько планет на одной орбите. Я, если честно, не вдавался в подробности, аозможно ли такое, но, я думаю, таким образом можно будет достаточно быстро построить отношение между планетами на одной орбите и в будущем сделать подвид планеты «спутник». Но это в будущем.

        На текущий же момент базовая структура была успешно закомичена и следующим этапом была отрисовка. Хотя-бы центральной звезды.

        Тут я вспомнил свою оптимизацию портированной на JS TinyTower и как я мучался там с быстродействием отрисовки «по-горячему», поэтому, загодя, сделал в каждом объекте отрисовку на внутренний offscreen-canvas персонально для этого объекта, с последующей сборкой снизу вверх. Тут же возникло решение о подмене canvas'a для текущего объекта тем, который вернулся из его детеныша, если он у детеныша больше по размерам, чем текущий canvas родителя. Таким образом в конце цепочки всегда будет самое большое панно с уже отрисованными данными по всем планетам, звездам и орбитам. Получился такой себе многогранный аналог Z-buffer'а для видеокарт. И как показывает мой опыт — отрисовка в память сложных фигур, графики и прочего выполняется в разы быстрее.

        Отрисовка Звезды не заняла много времени. Через размер изображения Звезды и общего canvas'a подсчитал смещение и повесил картинку на свое место.

        С Орбитой было чуток сложнее. Поскольку у каждой орбиты был задан радиус — нужно было готовить под нее персональный canvas и рисвать дугой диаметром Pi*2 радиан. Вспоминая свою боль по конвертации радиан в градусы и обратно решил, что все же лучше перестроить мозги, чем городить огород c конвертом.

        С Планетами было интереснее всего. Мало того, что Планету нужно было позиционировать на Орбите, что и было реализовано чуть выше в объекте Орбиты, так еще и нужно было сделать задел на будущее вращение при вызове отрисовки.

        В общем и целом же рисование всего выглядело достаточно банально: В Объект-родитель добавлялись объекты-детки, после этого родитель пробегал по всем деткам, у каждого последовательно вызывал .draw(), и комбинировал все полученные результаты на свой canvas, после этого возвращал этот canvas своему собственному родителю. Таким образом .draw(), вызванный из контроллера, получал последнюю актуальную версию того, что должно было рисоваться, чистил главное панно и в одно действие отрисовывал новый кадр на экране.

        Все это аккуратно завернуто в кросс-браузерный requestAnimationFrame().

    На самом деле это выглядит так
    //this.dc - offscreen-canvas для текущего элемента
    //this.dctx - offscreen-canvas контекст для рисования
    
    //Planets.js
    
    Planets.prototype.draw = function(timestamp) {
        var self = this;
        self.frame++;
    
        self.cx.clearRect(0, 0, this.width, this.height); //Чистим кадр
        for(var a in self.elements) {
            if(self.elements.hasOwnProperty(a)) { // Все тот же ужасный костыль
                var drawData = this.elements[a].draw(timestamp);
    
                self.cx.save();
                self.cx.translate((this.width-drawData.width)/2, (this.height-drawData.height)/2); // переводим начало координат
                self.cx.drawImage(drawData, self.elements[a].getX(), self.elements[a].getY());
                self.cx.restore();
            }
        }
    
        requestAnimFrame(function(){self.draw(new Date());}); // Запрашиваем новый loop анимации для отрисовки
    }
    
    // Star.js
    
    Star.prototype.draw = function(timestamp) {
        var orbitData = null;
        var drawData = null;
    
        this.dctx.clearRect(0, 0, this.width, this.height);
        // Compose all orbits
    
        for(var a in this.orbits) {
            if(this.orbits.hasOwnProperty(a)) {
                drawData = this.orbits[a].draw(timestamp);
    
                // Замена текущего canvas'а большим из деточки, если нужно 
    
                if(orbitData == null) {
                    orbitData = drawData;
                    continue;
                }
    
                if(orbitData.width > drawData.width) {
                    orbitData.getContext("2d").drawImage(
                        drawData,
                        (orbitData.width - drawData.width)/2,
                        (orbitData.height - drawData.height)/2
                    );
                    continue;
                }
    
                drawData.getContext("2d").drawImage(
                    orbitData,
                    (drawData.width - orbitData.width)/2,
                    (drawData.height - orbitData.height)/2
                );
                orbitData = drawData;
            }
        }
    
        // Draw Star
    
        if(orbitData.width > this.width) {
            this.setWidth(orbitData.width);
            this.setHeight(orbitData.height);
        }
    
        this.dctx.drawImage(orbitData, 0, 0);
    
        this.dctx.drawImage(this.image,
            (this.dc.width - this.image.width) / 2,
            (this.dc.width - this.image.width) / 2
        );
        return this.dc;
    }
    
    //Orbit.js
    Orbit.prototype.draw = function(timestamp) {
    
        var drawData = null;
        this.dctx.clearRect(0, 0, this.width, this.height);
        this.dctx.strokeStyle = "rgba(85, 183, 242, .5)";
        this.dctx.lineWidth = 1;
    
        this.dctx.beginPath();
        this.dctx.arc(this.width/2, this.height/2, this.radius, 0, Math.PI*2, true);
        this.dctx.closePath();
        this.dctx.stroke();
    
        for(var a in this.planets) {
            if(this.planets.hasOwnProperty(a)) {
                drawData = this.planets[a].draw(timestamp);
                this.dctx.save();
    
                this.dctx.translate(this.width/2, this.height/2);
                this.dctx.rotate(this.planets[a].getPosition());
                this.dctx.drawImage(drawData,
                    (this.planets[a].width)/2*-1,
                    (this.height/2-this.planets[a].height)
                );
                this.dctx.restore();
            }
        }
    
        this.update();
        return this.dc;
    }
    
    //Planet.js
    Planet.prototype.draw = function(timestamp) {
        this.dctx.clearRect(0,0,this.dc.width, this.dc.height);
        this.dctx.drawImage(this.image, 0, 0);
        return this.dc;
    }
    
    


    Дальше пришло время анимации и событий…

    23.12.12, 02:00

        Мне повезло в том, что анимировать в моем примере нужно было только планеты, а точнее — их перемещение. Еще раз посетовав на нестабильный fps завел в базовом контроллере метку времени старта и начал передавать при каждом обновлении в draw() текущий timestamp начиная с главного родителя. Такой путь позвояет делать анимацию для всех объектов на одно и то же временное смещение, не обращая внимания на длительность выполнения операций. В Техзадании скорость планеты была указана угловой величиной, поэтому и у меня скорость планет начала выражаться как Math.PI*2/. Дополнив объект Планеты методом расчета текущей угловой позиции в Орбите добавил цепочку translate-rotate для отрисовки планеты, при этом угловое значение поворота брал уже непосредственно из объекта Планеты.

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

        Стек событий практически без новшеств я позаимствовал у самого себя, слегка подкорректировав под собственные нужды. В общем приближении — любой mouseevent на объекте body передавался в контроллер, и уже контроллер передавал его деткам, последовательно проверяя, не случилось ли блокировки.

        Первое событие, которое я реализовал — это «подсвечивание» орбиты при наведении. Дополнив Helpers.js математикой расчера расстояния между двумя точками я начал проверять, совпадает ли текущее расстояние от центра звезды до Mouse-pointer'а, и если совпадает с определенной погрешностью — увеличивал толщину линии орбиты и менял ей цвет.

        С Планетами, как обычно, все обстояло намного интереснее. С одной стороны — облегчало задачу то, что обработку события можно было производить только тогда, когда родитель — Орбита — обработал свое событие «hover», с другой стороны — Планета все-таки круглой формы. На помощь по старой доброй памяти пришел метод isPointInPath(). Нехитрой проверкой состояния родителя у Планеты я начал определять, нужно ли мне обрабатывать событие на Планете, отрисовывал тестовую прозрачную линию в виде дуги длинной Math.PI*2 (окружность, ага), оборачивал ее в блок beginPath() — endPath(), транслировал текущие координаты мыши и в родителе проверял, в нужном ли месте находится указатель мыши.

        Примерно на этом же этапе были добавлены всплывающие попапы с сервисной информацией. Раздобыл на просторах интерета списки планет, ников и альянсов. (Планеты на самом деле — названия небесных тел солнечной системы помимо реальных планет, Ники — реальные ники игроков NBA, а вот альянсы уже взял из генератора), добавил их в цикл генерации планет и практически сразу отрисовал первый попап при hover'е. Вышло клево, и я заморочился статическим попапом из ТЗ.

        Собственно, разница между статическим и динамическим попапом не только в том, что они по-разному выглядят, но еще и в том, что статический попап блокирует обработку событий для всего остального панно. Пришлось допиливать небольшой костыль в основной метод обработки событий с проверкой на существование попапа. Ну и в дополнение к уже имевшимся огрехам — ссылка на попап всегда должна присутствовать в контроллере, так как он существует выше всего в принципе (отрисовывается даже поверх звезды), но физически принадлежит планете. Вот такой диссонанс, с которым я промучался достаточно долго.

        Добавление «опций» в статический попап, обработка событий мыши при клике на Планету и опцию была выполнена практически в том же стиле, что и hover для остальных объектов, разве что добавились обработчики событий в виде callback'ов в каждой опции.

        Наконец-то можно было заняться фиксами и багами, отлаживать и показывать. Практически ве было завершено и, на удивление, после запуска в основных браузерах на моем огрызке особых проблем не возникло. Так что в 7:04 я, довольный собой, отправился в кровать.

    Заключение

    23.12.12, 21:16

        Отладив последние скользские моменты, явно выбивающися из ТЗ, добавив pointer и причесав все, что осталось, выбросил все на свой хостинг, попросил друзей посмотреть в Ослике, после их подтверждения, что все работает, кликается и вращается — отправил ссылку RussianSpy. Примерно через 10 минут поступила первая реакция, достаточно прохладная, как мне показалось, но тогда я оказался не прав, и мой оппонент скорее проверял, как все это сделано и почему же оно не тормозит и не падает. После 15 минут обсуждений технических моментов на душе стало тепло и хорошо, миру вернулись былые краски и райские птицы запели за окном. Александр увидел, что «нетормозящий и работающий» Canvas возможен и, более того, прямо перед его глазами. Конечно же были замечания по поводу «академичности» этого примера, был баг с ивентами в опере, но в общем и целом я смог добиться своего и показать, насколько хорошо это работает.
    Всегда приятно слышать такое в свой адрес
    [23.12.12, 21:40:28] RussianSpy: В общем ты меня впечатлил.

    [23.12.12, 21:48:57] RussianSpy: Знаешь — тем не менее тебе удалось продемонстрировать нетормозящее решение этой задачи
    [23.12.12, 21:49:18] RussianSpy: А значит ты в тему канваса копнул достаточно глубоко.

    Еще бы, думал я в этот момент. Уж с канвасом-то я найду общий язык.


        Обсудив некоторые моменты для этой статьи мы еще около часа обсуждали то, что я показал и в общем то, что у меня было по Canvas'у. Продемонстрировал FiveGUI, зарезанный порт TinyTower на JS, чем Сашу сильно заинтересовал и мы договорились не терять друг друга из виду.

        После этого можно было отправляться отдыхать довольным.

    Выводы


        Какие выводы я для себя из этого вынес? Много вcякого.
    • Прежде всего, что в суматохе ежедневных тасков не забыл основ программирования графики на Canvas'e и еще что-то умею.
    • Canvas — замечателньый инструмент, и при должном рвении и умении ничуть не хуже Flash по возможностям
    • Доказательство в стиле Хакатона — просто офигенный буст настроения!

    И напоследок...


    Исходники на Github
    Рабочее Demo

    Спасибо за внимание!
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 136

      0
      Спасибо. Но демо не открывается.
        0
        Прошу прощения, в спешке забыл ссылку на индекс, да.
          +8
          Подскажите, а нагрузка на CPU это вынужденная жертва?
          Обратил внимание в FF нагрузка при открытии увеличивалась в «двое», для чистоты открыл IE (1 вкладка с Вашим примером), от старенького P8400 IE съело 48%-50%.
            0
            Открыл на MBP в сафари демку и начал говорить по телефону. Через минуту разговора бук гудел как пылесос. При видеообработке и на тяжелых играх такое не всегда проявляется. Решение, конечно, не плохое, но трудно соотносящееся с реальностью…
              +1
              habrahabr.ru/post/163893/ посмотрите этот вариант)
                0
                Посмотрел, по ссылке гораздо более глючный вариант, бесспорно, но он и менее ресурсоемкий (пол часа страница открыта, нагрузка на процессор почти не выросла). Вариант kibizoidus куда лучше, но тяжел для рендеринга.
                  0
                  Что значит?
                  гораздо более глючный вариант
                    0
                    Подтормаживает
                      0
                      Какой браузер и как это проявляется?
                        0
                        Safari
                        Версия 6.0.2 (8536.26.17)
                        OS X 10.8.2

                        Раз в пару секунд картинка замирает на долю секунды.
          0
          Демо: File not found
            +3
            Поправил. Забыл ссылку на индекс, прошу прощения.
            +13
            У планет на внешней орбите окошко с описанием когда подходит к краю обрезается размером канваса:)
              +1
              Кстати, не хочу разводить холивар, но флеш уже приличное время умеет не «дёргаться» при движении, а у вас при наведении на планету её анимация, не смотря на её скорость, очень дёрганная и мерцающая. Это просто не учтено было при разработке или такая проблема присутствует?
                +4
                Это просто не учтено, да. Субпиксельное сглаживание, я за него просто забыл, прошу прощения. Постараюсь попозже поправить.
              0
              Дергаются в chrome на air.
                –26
                А, и забавная фича — меню останавливает планету. После отпускания планета едет дальше.
                В отрыве от того, что это типа модель, было бы ничего, но остановленный Марс это уже забавно.
                  +12
                  ТЗ же
                  При клике на планету выпадает меню. При наведении мыши на планету и при клике по ней анимация данной планеты останавливается, остальные планеты продолжают свое движение.
                    –41
                    Хреновое ТЗ же.
                    • UFO just landed and posted this here
                        0
                        Огаму видел? Там вообще планеты в виде таблицы описаны — и ничего)
                        • UFO just landed and posted this here
                            0
                            Везде нужен баланс. И вот где кончается реалистичность и начинается казуальность пусть решает гейм-дизайнер ;)
                            • UFO just landed and posted this here
                                0
                                Иногда в маразме вся суть ;)
                                • UFO just landed and posted this here
                      0
                      Можно немного поподробнее про «дерганье» — какая версия браузера?
                        +1
                        Тоже дёргаются. Safari 6. Повторить так: подвести к орбите, оставить мышу в покое, сидеть смотреть. Примерно каждые 5..10 секунд притормаживает.
                        +1
                        Там же подписаны названия планет. Нет там Марса.
                          +1
                          Очевидно же, что дело не в названии, а в божественности происходящего на экране.
                            0
                            Названия выбираются путем божественного рандома из списка ^_^
                      +10
                      Если проскролить страничку вниз, то орбиты и планеты перестают выделяться ровно под курсором, а выделяются с некоторым смещением.
                        +1
                        Аналогичная проблема, хром.
                          0
                          Нужно бы дополнить пример сдвигом при скролле. Сейчас что-нибудь придумаю. Спасибо.
                          0
                          Поправил. Попробуйте еще раз.
                            +2
                            Теперь все оки!
                            Хвалю, не слушай никого )
                          0
                          Круто, под FF, ubuntu — отжирает столько же сколько и простенький флеш.
                            0
                            Ага, тоже заметил: планеты двигаются рывками + загрузка 4 ядер на 50%. При переключении вкладки — на уровне 2-3%.
                              +1
                              Под Win7, с напрочь загруженным процессором (Vray рендеринг в 3dsmax, все 8 ядер забиты на 97-100% в диспетчере задач) работает на удивление гладко. Я, конечно, согласен, что тут не так много графики, но:
                              1 — Любые флеш банеры при такой нагрузке на процессор «тормозят»
                              2 — Вне зависимости от того, насколько прогружено видео в Youtube и какое качество видео выбрано — тормозит.
                              3 — Браузер в принципе меееедленно страницы открывает.

                              Не похвалы ради, просто на самом деле, при такой загруженности системы, делать что-либо параллельно не получается, даже видео посмотреть в интернете… приходится сидеть и тупить в монитор, а тут работает, будто система свободна.

                              Спасибо!
                              0
                              Сделай размер браузера Chrome тамк что бы не все орбиты влазили на экран.
                              Поводи мышкой.

                              В общем у меня орбиты подсвечиваются со смещением + 1 примерно.
                              Да и то… курсор то между орбит.
                                0
                                Может быт не помешает в ту ветвь комментариев добавить ссылку на эту статью? Довольно поучительно ведь получилось.

                                Хотелось бы уточнить детали:
                                var PlanetController = new Planets({
                                    fps: 5
                                });
                                

                                Я все правильно понял насчет 5 fps (при том, что на моей машине отнимает > 100 % процессорного времени)? (Каюсь код просмотрел по диагонали, сильно не вникал).
                                  0
                                  Нет. Этот параметр сейчас «для отвода глаз». Он не используется.
                                    0
                                    На слабенькой машине около 40 fps в Хроме, съедая одно ядро на ~90%.
                                    –4
                                    Довольно-таки простенькая анимация — можно было бы и на SVG реализовать.
                                      +2
                                      С удовольствием сравнил бы решение на svg и текущее на canvas.
                                        +2
                                        На SVG там все не так просто. Chrome и Safari имеют очень ограниченную поддержку SVG. Так например не работают обработчики событий внутри тегов use. Да и вообще там много оказалось всяких косяков. Если интересно могу рассказать подробнее
                                          +1
                                          Тьфу ты, да. Я и забыл, что в хроме плохо с SVG: сам же делал псевдо-3D на SVG, в огнелисе работало без заметных тормозов, а в хроме через одно место.
                                          +1
                                          Eddy_Em

                                          Так пишите ему ТЗ =))
                                            +1
                                            Да ладно: SVG — уже почти что устаревшая штука. Но при этом до сих пор не поддерживается одинаково всеми браузерами.
                                          +3
                                          «нетормозящее решение»? :) Intel Core i3-2100, последний FF, аддоны для верности отключил — загрузка процессора на полную
                                          image
                                            +1
                                            Не знаю даже. Хром. i5

                                              +5
                                              Старенький е8600, проц скачет от 2 до 7 процентов. Ubuntu, Chromium 22.0.1229.94.
                                                +1
                                                Celeron 575, ff17, linux: загрузка в 20%
                                                  +1
                                                  Celeron B815, debian, свежий iceweasel — по ощущениям что-то около 0,33 fps, и серьезные фризы в работе браузера. Печаль.
                                                  0
                                                  Когда курсор перемещается по орбитам горизонтально от Солнца — всё ок.
                                                  По вертикали всё очень грустно: курсор уже в «космосе» (сошёл с последней орбиты), а подсвечена только 8я орбита.
                                                  Mac OS X 10.7.5, Chrome 23.0.1271.101
                                                    0
                                                    Страница чуть промотана по вертикали, обработчик мыши этого не учитывает.
                                                      0
                                                      В последнем фиксе пару минут назад это исправлено, проверьте, пожалуйста, еще раз.
                                                        0
                                                        Нормализовалось в FF :) Если мышку пошевелить — 7-10%, если в покое — 4-5% процессора
                                                          +2
                                                          Теперь такой глюк:
                                                          image
                                                        +6
                                                        Вы молодец. Вот просто взять и потратить кучу свободного времени, что бы доказать кому-то, что технология работает — это достойно похвалы.
                                                        А вот с какой задачкой я столкнулся и интересно ваше мнение — надо сделать как можно больше интерактивных (реагирующих на 3 события мышки) изображений (учитывать прозрачные области как неактивные). В районе 3К одинаковых изображений я еще норм держусь (MBA, Chrome), но как сделать еще больше? 10К?
                                                          0
                                                          А Вы у каждой интерактив слушаете или используете bubbling?
                                                            0
                                                            Не совсем понял суть вопроса — в канвасе, как таковом нет событийной системы и объектов ее поддерживающих. То есть, при любом событии мышки я просто посылаю это событие обработчику канваса, который на основе полученных координат мышки решает, у какого из изображений (игровых объектов, например), отрисованных на канвасе вызываем событие-реакцию.
                                                              0
                                                              а, прошу прощения, не совсем правильно понимал событийную модель канваса)
                                                                0
                                                                Секундочку. Какоо размера canvas? Размеры изображений? Как расположены? Я сомневаюсь, что на одном экране влезет 3к изображений… В общем — лучше поподробнее рассказать, а еще лучше показать вашу задачу. Думаю — решение, все же, есть…
                                                                  0
                                                                  Соль в том, что бы сделать универсальное решение, по типу флеша. Что бы конечный пользователь-программист не задумывался, добавляя интерактивный объект к списку отображения. Из примеров — симуляция поверхости с терраморфингом, жидкости (в игре Крокодильчик Свомпи), или туча падающих снежинок, или просто все игровые объекты вместе с игровым полем, интерфейсом и партиклами.
                                                                  А вот 3К интерактивных изображений — ссылка
                                                                    +1
                                                                    Ну есть самые разные алгоритмы поиска. Оптимизировать надо) Тем же индексированием. Например, почитайте "Моделирование большого количества взаимодействующих друг с другом частиц". Там, конечно, про коллизии, но для мыши тоже отлично подойдёт. Я в своём примере на 10000 объектов просто анализирую в какую клетку кликнули и беру из неё объект. Это сделать легко, т.к. либа позволяет подключить свой класс для поиска элементов
                                                                      0
                                                                      Ну с анализом клетки или даже изометрии просто, а вот именно перекрывающие друг друга полупрозрачные пнгшки — уже сложнее. Направление я понял, спасибо. Стоит оптимизировать область поиска, но пока не могу прийти к выводу как именно.
                                                                        0
                                                                        Сначала находим все пнгшни по ректанглу. Эта оптимизация простая. Потом из них (а их будет не так много, ну допустим десяток-два) без оптимизаций отсекаем лишние.
                                                            –10
                                                            Пример как делать антипримеры на HTML5 :)
                                                              0
                                                              Можно узнать, почему антипример? Все-таки интересно.
                                                                –5
                                                                Багов многовато, абсолютно не показательно чем это круче чем флеш. Да живет, да работает, но с таким же успехом можно было бы и Java Applet написать — тоже работал бы.
                                                                  +3
                                                                  Из серьезных багов я вижу пока-что только скролл. Тем более вся эта работа — Proof of Concept, занявший несколько часов. И статья была о том, что при должно уровне вылизывания Canvas ничем не хуже флеша, нужно просто знать его ограничения и умело применять.
                                                                    –5
                                                                    Еще бажог — если открыть и закрыть GDT то попап на планете не открывается.
                                                                      0
                                                                      GDT? Можно расшифровку? Я еще не спал и слегка туплю ^_^
                                                                        –3
                                                                        Google Developer Toolbar :)
                                                                    +1
                                                                    Если уж на то пошло, можно было бы вообще в WebGL сделать ☺
                                                                    Тормозов бы не было совершенно. А планеты можно было бы сделать «объемными», натянув текстуры на псевдошарики.
                                                                      0
                                                                      Кстати да… Почему-то в холиварах про канвас и флеш всегда забывают про существование такого могучего зверя как WebGL.
                                                                        0
                                                                        Который пока что нельзя использовать в продакшне, потому что в IE не поддерживается вовсе, а в Опере по умолчанию отключен.
                                                                          +3
                                                                          Потому что пока что веб-разработчики зачем-то ориентируются на IE.
                                                                            0
                                                                            Пока им будут пользоваться клиенты — его будут поддерживать. Причём доля IE8 всё ещё ощутима и его приходится саппортить.
                                                                              +3
                                                                              Поддерживать это стоит лишь разработчикам всяких «социальных» сервисов (вроде gosuslugi), да IE-ориентированных сайтов. А если у вас нечто экспериментальное или же целевая аудитория — люди хотя бы немного умеющие работать на компьютере, можно про IE забыть.
                                                                                0
                                                                                IE в Windows 8 довольно таки юзабелен, даже желания не возникает ставить фокс или хром.
                                                                            0
                                                                            Потому что у флеша есть stage3D с коммерческими продуктами, которые уже зарабатывают миллионы.
                                                                    0
                                                                    А всё-таки, зачем тут несколько off-screen canvas? Смысл есть только чтобы закешировать разные элементы, но тут же ничего не кешируется.
                                                                      0
                                                                      1. Рисование в offscreen-канвасе значительно быстрее. Грубо говоря, рисунок в памяти обрабатывается лучше, чем рисунок на экране.
                                                                      2. Удобно при рисовании композитных объектов оперировать канвасами как изображениями.
                                                                        +1
                                                                        Вот чисто на глаз, мелкую планету рисуют на большом канвасе, чтобы этот большой канвас потом нарисовать на ещё большем. Имело бы смысл если бы там было очень много мелких деталей, и это можно было бы кешировать, а так это какая-то магия и надежда на неожиданный прирост производительности из-за offscreen рисования.

                                                                        Если не лень — замерь профайлером время и сравни с прорисовкой на 1 канвас. Если так уж хочется использовать offscreen — используй 1 offscreen canvas.
                                                                          0
                                                                          Грубо говоря, рисунок в памяти обрабатывается лучше, чем рисунок на экране.

                                                                          Насколько я понял это таки не имеет смысла — всё-равно браузер сначала отрисовывает в памяти, а только потом выводит на экран.
                                                                            0
                                                                            Не раз пытался проверить ваше утверждение о скрытом канвасе. Вот прямо сейчас скрытый канвас (back buffer) дает мне +30% FPS.
                                                                              0
                                                                              А можете показать пример? В моих приложение удаление back-buffer дало значительный прирост производительности. А потом дополнительные оптимизации, которые вообще не имели бы смысла, если бы использовал два холста.
                                                                                0
                                                                                Отписал в личку
                                                                                  0
                                                                                  Вижу)
                                                                        0
                                                                        В ИЕ9 не работает выделение. Да и с масштабированием проблема.
                                                                        Ну, и для сравнения загрузки процессора хотелось бы увидеть версию на флэше. Т.к. у меня эта анимация загружает процессор где-то на 50%.
                                                                          0
                                                                          Очень простенькая анимация, которая отжирает полностью одно ядро процессора. Что это по вашему доказывает?
                                                                            +5
                                                                            Что есть куда оптимизировать;)
                                                                            0
                                                                            Глядя на это дело подумалось, что по-хорошему, освещённость планет, а соответственно, и тени должны меняться вместе с изменением ориентации планет относительно солнца. Вот на такое было бы уже интересно посмотреть.

                                                                            Хотя понимаю, что топик не об этом.
                                                                              0
                                                                              Это идея. Спасибо.
                                                                              –5
                                                                              Рискую нарваться на минусы, но для сравнения: на AS3 решение будет в разы менее тормозным, плавным и красивым. Ко всему прочему и делать 20 минут от силы :-)
                                                                                +15
                                                                                Ко всему прочему и делать 20 минут от силы

                                                                                вперёд ;)
                                                                                  +7
                                                                                  Ждём анологичного поста про Флэш. ТЗ есть, исходники картинок есть, образец есть. :)
                                                                                      0
                                                                                      А вообще очень хотел бы почитать топик. Так, чтобы полный нуб мог повторить шаги и тоже сделать такое же. Я когда-то флешил, но это было давно и неправда.
                                                                                        0
                                                                                        А стоит ли? С либканвасом цель была другая, а то, что это и во флеше можно сделать, и так понятно. Разве что если использовать stage3d и прочую дребедень.
                                                                                        +1
                                                                                        Это вин :) В Yandex.Browser под макосью:

                                                                                        Flash версия сжирает ~70% CPU (скачет от 60 до 80)

                                                                                        JS версия сжирает не более 50% CPU (скачет от 30 до 50)
                                                                                    • UFO just landed and posted this here
                                                                                        0
                                                                                        Вы наверное имели ввиду простую анимацию в слоях и направляющих линиях без каких либо скриптов — это реально делать минут 30 с готовыми картинками.

                                                                                        Вот скрипт с нуля думаю подольше :)
                                                                                        +1
                                                                                        Тоже самое, ну точнее совсем не тоже, но на CSS codepen.io/juliangarnier/pen/idhuG
                                                                                          +1
                                                                                          Я вообще когда читал ту ветку комментариев, то думал, что там будет тихий ужас в задании, а задание оказалось простецким вообще. Надо будет и самому такое замутить.
                                                                                            +2
                                                                                            Я знал! Я знал, что ты не сможешь пройти мимо! Сенсей в моем топике, я рад! ^_^
                                                                                            Можем посоревноваться в оптимизации, хех.
                                                                                              +1
                                                                                              Ох, что ты со мной делаешь! Окей, я постараюсь намутить что-то до конца недели, но ничего не обещаю. Можешь кинуть на shocksilien@gmail.com ресурсы, пожалуйста?)

                                                                                              пс. ну как я мог пройти мимо?

                                                                                              ппс. но топик таки зачётный, спасибо)
                                                                                                +1
                                                                                                Ждем еще один вариант!
                                                                                                  0
                                                                                                  Уговорили)
                                                                                            0
                                                                                            Код можно форкнуть? Очень в тему :)
                                                                                              0
                                                                                              ТЗ странное. Зачем вместо известных каждому школьнику (по крайней мере в момент их преподавания) законов Кеплера выдумывать какие-то свои «двадцать секунд»?
                                                                                                +1
                                                                                                Требуется только видимость реалистичности, причём зрелищность и юзабельность куда важнее достоверности, особенно если достоверность потребует заметно больших вычислительных ресурсов. А то так можно дойти до, хотя бы, взаимного влияния планет на орбиты друг друга.
                                                                                                0
                                                                                                можно внеплановый парад планет устроить =)
                                                                                                  +1
                                                                                                  запилил за вечер на свг nin-jin.github.com.local/etc/planets/
                                                                                                  в отличие от канваса не грузит проц практически
                                                                                                  кто-нибудь знает как сделать чтобы в хроме не дрожали планетки и тексты?
                                                                                                    0
                                                                                                    Из ссылки нужно удалить .local :-)
                                                                                                      0
                                                                                                      А почему только две планеты?)

                                                                                                      кто-нибудь знает как сделать чтобы в хроме не дрожали планетки и тексты?

                                                                                                      Надо вырубить оптимизиацию. Не знаю, как это сделать в svg, но в canvas это можно сделать rotate'нув на 1 градус холст ;) попробуй что-то в этом духе.

                                                                                                      upd: хотя с текстами такое врядли проканает. их стоит отрисовать в Канвас и двигать картинку)
                                                                                                      0
                                                                                                      Позволю себе тоже вставить свои пять копеек. Под хромом и IE 10 идет довольно резво и ничего не грузит. Под Оперой безбожно тормозит.

                                                                                                      (Disclaimer: к середине дня запал как-то поугас, поэтому от первоначального ТЗ несколько отличается, но думаю не сильно критично)
                                                                                                        0
                                                                                                        1) Если горизонтальный скролл есть, то при наведении координаты мыши неверно определяются, не те орбиты подсвечиваются.
                                                                                                        2) Анимация рывками идёт.

                                                                                                        Но да, проц не грузит.

                                                                                                        p.s. в FF последнем тестил
                                                                                                          0
                                                                                                          Да, там косяков куча, признаю, но увы работа не волк, в лес не убегает, поэтому пока переключился на нее. Почему там иногда рывки, я так до сих пор и не разобрался. В FF действительно проблемы со скролами, в других браузерах такого вроде нет.
                                                                                                            0
                                                                                                            При чём fps всегда 59+.
                                                                                                            Иногда рисует по прошлой координате, потом уже по следующей, вот и дёрганья. Возможно что-то не то с округлением.
                                                                                                              0
                                                                                                              Похоже это какая-то общая проблема канвы. А так код конкретно этой карты здесь, а здесь код всего остального движка.
                                                                                                                0
                                                                                                                Почему? Разве libcanvas.github.com/games/solar/ идёт рывками?
                                                                                                                  0
                                                                                                                  Да. При чём не постоянно.
                                                                                                                  Порой может довольно продолжительный период плавно анимироваться, порой в течении относительно большого периода анимируется рывками.
                                                                                                                    0
                                                                                                                    FF? Может ли быть проблема в том, что паралельно что-то загружает проц?
                                                                                                                      0
                                                                                                                      FF, да.
                                                                                                                      При запущенном libcanvas.github.com/games/solar/ нагрузка на проц в районе 10% суммарная по всем процессам.
                                                                                                                        0
                                                                                                                        А ось? Просто скрипт gasizdat в фф у меня впринципе дёрганно работает, а libcanvas'овский — плавно.
                                                                                                                          0
                                                                                                                          Семёрка 64
                                                                                                                            0
                                                                                                                            А можно попросить включить/отключить аппаратное ускорение в настройках (Настройки-Дополнительно-Использовать аппаратное ускорение, если возможно) и сказать, изменится ли ситуация?
                                                                                                                              0
                                                                                                                              Да вроде не замечаю особой разницы от выключения аппаратного ускорения.

                                                                                                                              Я машину свою уже несколько суток не выключал, может дело в этом)
                                                                                                                                0
                                                                                                                                А просто перезапустить браузер?
                                                                                                                                  0
                                                                                                                                  Не помогло.
                                                                                                                                  Быть может у меня от недосыпа уже зрение шалит.

                                                                                                                                  Было б неплохо сделать возможность ведения лога. Скажем, чтобы текущий fps писался. Или хотя бы, чтобы минимальное значение fps выводилось.
                                                                                                                    0
                                                                                                                    Рывки еле заметны, но они есть) попробуйте вывести график FPS
                                                                                                          0
                                                                                                          При наведении на звезду тоже можно информацию выдавать, хоть и нет в ТЗ. А то она какая-то «неживая» получается. :)
                                                                                                            0
                                                                                                            Хотя с другой стороны, если звезда никак не используется, это к лучшему.

                                                                                                          Only users with full accounts can post comments. Log in, please.