Карта метро Москвы. SVG-версия

    Продолжаю тему разработки динамической векторной (по координатам) карты для браузера dbcartajs. В новой версии (v2) я перевел отрисовку объектов с канваса на SVG. И переделал несколько примеров, в частности карту метро Москвы. В своем посте про канвасную версию карты я сравнивал ее с svg-версией из Википедии, сделав акцент на скорости загрузки, которая у канваса оказалась выше. Но воспроизведя карту через svg-обработчик в новой версии проекта, я понял, что скорость загрузки, пожалуй, единственное преимущество канваса перед SVG.

    image

    Во-первых, потребовалось меньшее количество кода для создания на svg.

    Во-вторых, простота и удобство создания кода. У svg логика строится на отдельных объектах, их свойствах и методах. Например, чтобы определить объект под курсором, нужно создать обработчик события для данного объекта (circle для станций метро), в котором можно поменять его свойства — цвет, масштаб (на картинке для значка станции «Ходынское поле» применен метод scale) и др. У канваса логика отрисовки построена на перерисовке всей карты. И для выделения отдельных объектов нужно перерисовывать и масштабировать всю карту. Кроме этого, канвас не хранит отрисованные объекты в памяти, их нужно отдельно сохранять в переменных (в dbcartajs в объекте mflood) и самому следить за ними (добавлять, удалять). SVG-изображение хранит отрисованные объекты в DOM-модели и к ним можно обращаться напрямую.

    В-третьих, возможностей в svg намного больше, чем в канвасе, например, анимация, фильтры для изображений.

    В-четвертых, совместимость с браузерами. У SVG она выше. C SVG я проблем не заметил. В канвасе некоторые свойства в Firefox 3.x и новых версиях работают по разному (например setDashLine, isPointInPath). Internet Explorer до 9 версии и Safari до 4 версии канвас вообще не поддерживали.

    И svg, и канвас могут манипулировать готовыми изображениями (png, jpeg). Правда, только канвас может обращаться к нему попиксельно. И это, как и скорость отрисовки, пожалуй, его главная особенность.

    В общем, кому интересно, смотрите примеры на egaxegax.github.io.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 20

      +5
      У канваса логика отрисовки построена на перерисовке всей карты.

      Это проблема конкретной вашей реализации, а не самого канваса.
        0
        В канвасе можно перерисовать отдельный фрагмент. Но перерисованный фрагмент наложится на изображение. Можно стереть часть изображения перед наложением фрагмента, но тогда появится пустые области. Кроме того, довольно сложно каждый раз вычислить область наложения особенно при выводе текста несколько раз в одной области. Поэтому я имел в виду перерисовку всей картинки как базовый вариант работы с канвасом в динамике.
          –3
          Кроме того, довольно сложно каждый раз вычислить область наложения особенно при выводе текста несколько раз в одной области.

          Ну, если правильная архитектура приложения, то ничего сложного нет.
            0
            При создании игр все эти проблемы давно давным решили…
            Дам совет — лучше карту рисовать в буфер и скролиннг реализовывать уже по этому буферу, а не рисовать каждый раз, с отключением текста. :)
              0
              Ждём статью)
                0
                Мне бы собраться с мыслями и про Вангеры написать (там к слову этот подход во всю, каждый объект сам за собой чистил).
          –1
          Первые 3 «во-первых» легко решаются библиотеками для canvas.
            0
            Какими, например? И как? У каждой библиотеки своя область применения.
              0
              Ну это естественно.
              LibCanvas, Fabric, Graphics2D, PaperJS хотя бы. Первое, что вспомнилось.
              Они общего назначения, есть и специализированные.

              Во-первых, потребовалось меньшее количество кода для создания на svg.

              Сравниваем:
              <rect x="10" y="10" width="200" height="200" fill="red"/>
              <path d="M10,10 L200,200 L400,10" fill="blue"/>
              

              Пример для canvas будет на Graphics2d, т.к. я её лучше всего знаю, да и там короче всех, пожалуй:
              var rect = ctx.rect(10, 10, 200, 200, 'red');
              // либо:
              var rect = ctx.rect({ x: 10, y: 10, width: 200, height: 200, fill: 'red' });
              
              var path = ctx.path([[10, 10], [200, 200], [400, 10]], 'blue');
              

              А многие библиотеки (Fabric, например) и SVG-пути поддерживают.

              Во-вторых, простота и удобство создания кода. У svg логика строится на отдельных объектах, их свойствах и методах. Например, чтобы определить объект под курсором, нужно создать обработчик события для данного объекта (circle для станций метро), в котором можно поменять его свойства — цвет, масштаб (на картинке для значка станции «Ходынское поле» применен метод scale) и др. У канваса логика отрисовки построена на перерисовке всей карты. И для выделения отдельных объектов нужно перерисовывать и масштабировать всю карту. Кроме этого, канвас не хранит отрисованные объекты в памяти, их нужно отдельно сохранять в переменных (в dbcartajs в объекте mflood) и самому следить за ними (добавлять, удалять). SVG-изображение хранит отрисованные объекты в DOM-модели и к ним можно обращаться напрямую.

              Та-даам.
              var station = ctx.circle({ cx: 50, cy: 50, radius: 10, fill: 'red', stroke: 'red 2px' });
              station.on('mouseover', 'animate', 'stroke', 'blue', 1000);
              station.on('mouseout', 'animate', 'stroke', 'red', 1000);
              

              Вот так ещё можно:
              station.on('mouseover', function(e){
                this.animate('stroke', 'blue', 1000);
                this.scale(1.3);
              });
              
              station.on('mouseout', function(e){
                this.animate({
                  stroke: 'blue',
                  scale: 1 / 1.3
                }, 1000);
              });
              


              В общем-то, на всех 4-х из мною перечисленных это спокойно реализуется.
                0
                Ах да, и забыл:
                В-третьих, возможностей в svg намного больше, чем в канвасе, например, анимация, фильтры для изображений.

                Анимацию в G2D я уже показал, в LibCanvas тоже есть, и в Fabric.
                Фильтры в Fabric:
                fabric.Image.fromURL('image.jpg', function(img) {
                  img.filters.push(new fabric.Image.filters.Grayscale());
                  img.applyFilters(canvas.renderAll.bind(canvas));
                  canvas.add(img);
                });
                

                Фильтры в g2d:
                var image = ctx.image('image.jpg', 10, 10);
                image.filter('pixel', function(r, g, b){
                 var mid = (r + g + b) / 3;
                 return [mid, mid, mid, 1];
                });
                

                Тут пример с Grayscale, встроенных в G2D пока нет.

                Да и вообще реализовать, в общем-то, несложно, в отличие от SVG есть прямой доступ к пикселям.
                  0
                  Приятно получить комментарий тоже от разработчика библиотеки для канваса. Только почему ни один пример не работает на http://graphics2d.js.org? Хотелось бы увидеть что-нибудь из вышеописанного в действии. В проекте graphics2d примеров тоже нет.
                    0
                    Всё пофиксил, благодарю. Добавил ещё один пример, теперь можно оценить соотношение прыгающих зайчиков и FPS :).

                    Сейчас очень многое изменяю, хочется сделать более общей и более функциональной (а ещё добавить WebGL-рендер), поэтому последние версии немного неюзабельны, а на сайте многое немного не работает. Думаю, к ноябрю закончу.
            +1
            А ещё SVG автоматически адаптирует своё качество в зависимости от плотности точек дисплея (например, на 4K-мониторе с системным масштабом 200%). Векторная графика есть векторная графика.
              0
              Для канвы это тоже делается на раз-два, см. функцию getRetinaRatio здесь.
                +1
                Ну да, можно выводить растровое изображение пропорционально бОльших размеров, вписывая его в пропорционально меньшую область (и надеясь на точное попадание каждого логического пиксела в пиксел физический — для нештриховой графики сейчас так и приходится делать), но зачем, если можно просто использовать векторную графику, именно для штриховых изображений подходящую идеально и адаптирующуюся под любую плотность точек автоматически и с гарантированным качеством? (Вопрос риторический.)
              +2
              На маломощном нетбуке наблюдаю обратную ситуацию — канвас версия очень сильно тормозит при масштабировании, очень инертная по задержкам (при наведении курсора на станцию) и т.п. SVG версия по сравнению с ней летает, скорость загрузки тоже немного выше чем Canvas версии.
              Linux Mint 17.2, amd64, Firefox, Atom D525
                0
                Да действительно, я уже и забыл, что в последних версиях канвасной карты метро я заменил отрисовку линий по координатам на фоновую картинку-скриншот с линиями. Отрисовываются только точки (прямоугольники) станций. Загружаться карта стала немного медленнее чем раньше (до v1.8). Зато намного быстрее стала работать на смартфоне особенно при перетаскивании и масштабировании. А SVG и правда быстрее.
                0
                А можете дополнить карту дополнительными элементами — ССВ и депо? Например отсюда — trackmap.ru
                  +1
                  Все можно. А почему бы самому не попробовать? На заказ я уже делал одну карту в своем блоге, но товарищ после этого почему-то прекратил переписку.
                  0
                  Монорельс не подписан.

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