Leaflet: Расстояние между плитками при дробном масштабировании в браузерах Webkit

Созданный на 30 июн. 2015  ·  96Комментарии  ·  Источник: Leaflet/Leaflet

screen shot 2015-06-30 at 14 39 12

Как показано на скриншоте, между плитками есть небольшое пространство. Такое случается со мной при дробном увеличении в Safari, Chrome и Opera. Похоже, это было исправлено в # 2377, но позже удалено в 3ccbe5b в пользу лучшей производительности.

Я попытался повторно реализовать исправление, чтобы сам увидеть влияние на производительность, и это было довольно плохо, к тому же это даже не решило проблему для Chrome.

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

Изменить: я установил jsFiddle, который показывает проблему: http://jsfiddle.net/Eschon/pw9jcjus/1/

bug in progress

Самый полезный комментарий

Проблема очень заметна, особенно на более темных картах или изображениях (в моем случае). Я думаю, что сглаживание в браузере вызывает пробелы между плитками из-за _fractional_ преобразования во время масштабирования, как упоминал Иван.

Пока не будет найдено лучшее решение, я буду использовать этот обходной путь (или взломать), чтобы увеличить плитки на 1 пиксель с побочным эффектом, что они перекрываются на 1 пиксель. Кроме того, плитки будут немного увеличены и масштабированы (на 1 пиксель).

/* 
 * Workaround for 1px lines appearing in some browsers due to fractional transforms
 * and resulting anti-aliasing.
 * https://github.com/Leaflet/Leaflet/issues/3575
 */
(function(){
    var originalInitTile = L.GridLayer.prototype._initTile
    L.GridLayer.include({
        _initTile: function (tile) {
            originalInitTile.call(this, tile);

            var tileSize = this.getTileSize();

            tile.style.width = tileSize.x + 1 + 'px';
            tile.style.height = tileSize.y + 1 + 'px';
        }
    });
})()

Все 96 Комментарий

Без понятия. :( Хотя я видел проблему только в Safari, - Chrome / Opera работали нормально.

Есть ли у кого-нибудь идеи, как это можно исправить?

У меня есть идея, но она связана со словами «радикальный» и «экспериментальный»:

https://github.com/IvanSanchez/Leaflet.gl

Одна из вещей, которые я обнаружил, заключается в том, что текстуры нужно фиксировать. то есть, если эта строка будет удалена: https://github.com/IvanSanchez/Leaflet.gl/blob/master/src/core/GlUtil.js#L74 появятся осколки («промежутки между плитками») (поскольку текстура обтекает (показан треугольник и половина пикселя с другой стороны плитки). Итак, мое обоснованное предположение состоит в том, что Webkit сглаживает половину пикселя по краю каждого изображения.

На данный момент треугольники тайлового слоя образуют полную сетку, которая визуализируется без осколков ... но на данный момент слова «экспериментальный» будут повсюду.

@mourner Эффект сильнее в Safari, но он также виден в Chrome.
screen shot 2015-06-30 at 15 42 07

@IvanSanchez Вау, хорошая работа, но я не думаю, что смогу ее использовать (пока).

@Eschon да, наверное, я этого не заметил, потому что на экране Retina это еще менее заметно.

В некоторых случаях это, похоже, влияет и на Firefox.
Я не мог воспроизвести его в jsFiddle, но если вы посмотрите на этот сайт
У меня такие же пробелы в Firefox 39 и 41.0a2

Изменить: похоже, проблема с фреймами. Если я открываю карту непосредственно не воспроизводит.

Я сомневаюсь, что это будет возможно в версии 1.0 без большого количества взломов. Отодвинуть отставание в конец.

Та же проблема возникает и в последней версии Chrome. Я хочу, чтобы вы нашли возможное исправление, так как карты OpenStreet плохо отображаются с этими линиями. Благодарю.

Проблема очень заметна, особенно на более темных картах или изображениях (в моем случае). Я думаю, что сглаживание в браузере вызывает пробелы между плитками из-за _fractional_ преобразования во время масштабирования, как упоминал Иван.

Пока не будет найдено лучшее решение, я буду использовать этот обходной путь (или взломать), чтобы увеличить плитки на 1 пиксель с побочным эффектом, что они перекрываются на 1 пиксель. Кроме того, плитки будут немного увеличены и масштабированы (на 1 пиксель).

/* 
 * Workaround for 1px lines appearing in some browsers due to fractional transforms
 * and resulting anti-aliasing.
 * https://github.com/Leaflet/Leaflet/issues/3575
 */
(function(){
    var originalInitTile = L.GridLayer.prototype._initTile
    L.GridLayer.include({
        _initTile: function (tile) {
            originalInitTile.call(this, tile);

            var tileSize = this.getTileSize();

            tile.style.width = tileSize.x + 1 + 'px';
            tile.style.height = tileSize.y + 1 + 'px';
        }
    });
})()

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

Сделал jsbin с тёмными тайлами cartodb, линии видны даже на экране retina:
http://jsbin.com/ratoli/5/edit?html , js, вывод

Safari / Chrome глючит, FF ОК для меня.

Для этого мы должны сделать отчет об ошибке Chrome без листовок, чтобы он был исправлен в будущих версиях Chrome.

@hyperknot Я думаю, что уже есть отчет об ошибке Chrome, но я не могу его найти прямо сейчас.

Я использую бета-версию leafletjs версия : chrome, safari, firefox) на osx El Capitan. когда я редактирую свойство изображения transform css на 1 пиксель, отредактированная плитка выравнивается с плиткой рядом с ней.

// ex.
 transform: translate3d(1556px, -81px, 0px);
 // to
 transform: translate3d(1555px, -80px, 0px);

Иногда эта ошибка не появляется (разница в 1 пиксель) !! Не уверен, почему :( но я предполагаю, что это связано со стилем контейнера карты.

Изменить: скажем, плитки правильно выровнены при рендеринге и не показывают разницы в 1 пиксель, разрыв будет виден при перемещении карты. Поведение похоже на вспышку границы в 1 пиксель вокруг плитки.

Я нашел обходной путь css, который избавляет меня от проблемы в Safari 9.1 на OS X El Capitan:

.leaflet-tile-container img {
    box-shadow: 0 0 1px rgba(0, 0, 0, 0.05);
}

Однако я не могу поверить в это, я взял это из следующего ответа Stackoverflow, который также намекает на то, почему это помогает: http://stackoverflow.com/a/17822836/138103

Использование box-shadow решает проблему более прямым образом: оно увеличивает размеры перерисовки поля, заставляя WebKit перерисовывать лишние пиксели. Известные последствия для производительности box-shadow в мобильных браузерах напрямую связаны с используемым радиусом размытия, поэтому тень в один пиксель не должна иметь практически никакого эффекта.

Возможно, это можно использовать как правильное исправление, хотя, вероятно, было бы хорошо, если бы другие попробовали это в первую очередь. Однако у меня это работает и, похоже, не вызывает проблем в других браузерах.

@href Может быть, вы могли бы сделать запрос на

@href @IvanSanchez Вы уверены, что это не повлияет на производительность рендеринга? Я думаю, мы должны профилировать, как добавление тени блока с прозрачностью альфа влияет, например, на мобильные устройства, оно может иметь видимый эффект на производительность или даже, возможно, отключать аппаратное ускорение.

@IvanSanchez готово.

Вы уверены, что это не повлияет на производительность рендеринга? Я думаю, мы должны профилировать, как добавление box-shadow с альфа-прозрачностью влияет, например, на мобильные устройства, может иметь видимый эффект на производительность или даже, возможно, отключать аппаратное ускорение для панорамирования.

Я совершенно не уверен, каков эффект от этого. Судя по ответу Stackoverflow, это, похоже, не имеет эффекта, но это просто доверие чьему-либо мнению по косвенно связанной проблеме. Безусловно, было бы отличной идеей, если бы кто-то более знающий мог изучить это и описать мой обходной путь.

Мне просто повезло, когда я погуглил и хотел, чтобы об этом узнали другие.

@hyperknot @href Я с радостью

@IvanSanchez @href Кроме того, было бы неплохо ограничить этот взлом только затронутыми браузерами, а не применять его глобально.

@IvanSanchez есть ли рекомендуемый рабочий процесс для профилирования производительности рендеринга Leaflet?

Кроме того, было бы неплохо ограничить этот взлом только затронутыми браузерами, а не применять его глобально.

Я лично вижу это только в Safari, так что есть смысл ограничить его для сафари. Однако были упоминания о людях, которые видели это в Chrome. Кто-нибудь до сих пор видит эту ошибку в других браузерах webkit?

Довольно тривиально добавить класс CSS на карту только для выбранных браузеров в конструкторе L.Map .

@href Проблема присутствует в моем Chromium 49

@hyperknot Я не знаю, как лучше всего

@href Кроме того, это исправление в моем Chromium 49 .leaflet-container { background: black } :

fractional-space

С img.leaflet-tile.leaflet-tile-loaded { box-shadow: 0 0 1px rgba(0, 0, 0, 0.05); } :

fractional-space1

: crying_cat_face:

Это странно. Для меня проблема не возникает в Chromium 49 (OS X El Capitan), и тень от коробки, похоже, не имеет никакого влияния в любом случае.

Сделал обновленную площадку: http://playground-leaflet.rhcloud.com/yid/1/edit?html , вывод

Я буду отправлять сообщения об ошибках без листовок командам Webkit и Blink.

Хорошо, успешно сделал минимальный пример без листовок, воспроизводящий проблему:
http://playground-leaflet.rhcloud.com/say/1/edit?html , вывод

Отчет об ошибке Chromium отправлен: https://bugs.chromium.org/p/chromium/issues/detail?id=600120

Отправлена ​​ошибка Webkit: https://bugs.webkit.org/show_bug.cgi?id=156130

Версия без img: http://playground-leaflet.rhcloud.com/foqi/1/edit?html , вывод

Некоторые обновления из отчета об ошибке Chromium, от [email protected]

Эй, просто хочу дать вам короткую информацию. Я активно работаю над этим вопросом.

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

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

Исправление будет состоять из двух частей:

  1. Первая часть, которая позволяет нашей растровой системе растрировать с любой общей матрицей. Эта часть почти готова. У меня есть WIP, но в нем все еще есть ошибка, приводящая к снижению производительности. Я рассчитываю закончить его в течение нескольких дней.
    https://codereview.chromium.org/2075873002/
  2. Вторая часть, которая позволяет нашему управлению мозаикой управлять набором текстур, который имеет различную трансляцию растра. Изначально я собирался использовать общую матрицу, но оказалось, что вычисление покрытия тайлов становится очень трудным. Вместо этого я сделаю более простую версию, которая поддерживает только перевод и масштабирование. Я полагаю, для этого потребуется еще неделя работы.

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

https://bugs.chromium.org/p/chromium/issues/detail?id=600120

Репортеру ошибок:

Загруженный вами тестовый пример сработал в Chromium, что мы по-другому обрабатываем привязку к пикселям. Если вы добавите содержимое к плиткам, вы больше не увидите швов на заднем плане. Мы легко можем починить край корпуса, однако в содержимом плитки все же могут быть швы.

Это сложная проблема, и единственное известное идеальное решение - использовать FSAA, чего не делают никакие браузеры из-за затрат процессора / памяти. Каждый поставщик разрабатывает собственную эвристику, чтобы часть содержимого выглядела лучше, за счет чего часть содержимого выглядела неправильно.

Например: http://jsbin.com/wayapiz/

Chrome рисует с правильной геометрией, но края расплываются, как застрявшая свинья.
Firefox рисует первый прямоугольник как 49x49, второй как 49x50.
Edge рисует первый прямоугольник размером 50x50, второй - как 50x49. Оба с алиасингом.

Также, если вы добавите поворот к контейнеру, вся реализация приведет к размытию краев.

Я рекомендую обходной путь - добавить некоторое перекрытие между плитками. Знаете, как сделать плакат из бумаги формата А4. Идея состоит в том, чтобы каждый тайл перерисовывал хотя бы один пиксель после всего масштабирования, чтобы каждый физический пиксель гарантированно перекрывался хотя бы одним непрозрачным пикселем. Ширина перекрытия зависит от минимального масштаба, который вы хотите поддерживать. Например, если вы хотите уменьшить размер до 1/4, добавьте 4 пикселя перекрытия.

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

Репро, загруженное репортером, было не очень хорошим воспроизведением, потому что оно запускает оптимизированный путь кода в cc. Когда мы обнаружили слой полностью сплошного цвета, мы рисуем его как SolidColorLayerImpl, который не имеет концепции масштаба содержимого. Вот упрощенное воспроизведение: http://jsbin.com/hodoxew/

Черный ящик будет отображаться как SolidColorLayerImpl размера (100, 100), который будет генерировать один сплошной четырехцветный квадратик размера (100, 100), который будет проецироваться на экран с помощью преобразования рисования в (99,5, 99,5). При сглаживании крайний правый столбец и нижний ряд пикселей будут отображаться полупрозрачными.

С другой стороны, если мы добавим что-то к слою, чтобы заставить cc думать, что слой не является сплошным цветом, теперь проблема возникает в двух случаях:

  1. Последняя плитка сплошного цвета, например: http://jsbin.com/zexawa/

Размер содержимого будет округлен в большую сторону, поэтому, хотя размер масштабированного слоя составляет всего (600, 300) * 0,995 = (597, 298,5), он будет перерисован как (597, 299).

  1. Плитка списка не сплошного цвета, например: http://jsbin.com/mewaguf/

Размер содержимого будет округлен в большую сторону. Опять же, фактическое масштабированное содержимое покрывает только (600, 300) * 0,995 = (597, 298,5), сгенерированные квадраты будут покрывать (597 299). Последняя строка пикселей должна быть полупрозрачной из-за сглаживания, но на самом деле мы заполняем ее цветом фона слоя, который является цветом фона элемента, создавшего слой.

На фоне не будет швов, однако фон может растекаться по краям, когда предполагается, что содержимое закрывает его. Например: http://jsbin.com/vigufo/

Другой тестовый пример с использованием не сплошных цветных плиток:
http://playground-leaflet.rhcloud.com/giqo/edit?output

@hyperknot фантастическое обновление, но эти две цитаты, похоже, противоречат друг другу (в одном человек говорит, что работает над исправлением, а следующий говорит, что хорошее исправление невозможно). Так что, ждать исправления или просто согласиться с тем, что так будет всегда?

@mourner, извините, я не включил полный контекст, и вы, конечно, правы. Итак, исходный отчет об ошибке был:
https://bugs.chromium.org/p/chromium/issues/detail?id=600120

В какой-то момент это слилось с другой проблемой:
https://bugs.chromium.org/p/chromium/issues/detail?id=521364

В этом другом вопросе мы получили оригинальный длинный ответ с решением. Я спросил, может ли он проверить, действительно ли дело с Leaflet исправлено его работой, и он ответил:

Мне очень жаль, что выпуск листовок - это на самом деле другая проблема. Я повторно открою выпуск 600120 и поделюсь своим анализом.

Итак, ошибка Leaflet теперь повторно открыта, и мы получили второй ответ в первой ошибке Leaflet. Кстати, последнее обновление прямо сейчас:

Я взглянул на карту Google для мобильных устройств, чтобы узнать, как они с этим справились. Оказывается, они тоже страдают от этой проблемы, но они сделали некоторые уловки с пользовательским интерфейсом, чтобы сделать ее менее очевидной.

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

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

Кстати, похоже, что мобильные Google Maps (я предполагаю, что веб-сайт) используют точно такую ​​же технику, что и мы.

@mourner @IvanSanchez - какой-то серьезный вопрос с возможным решением с использованием will-change:
https://bugs.chromium.org/p/chromium/issues/detail?id=600120#c15

Насколько важна текущая населенность? Я вижу, что каждая плитка помещается в свой собственный слой (translate3d), и эти слои независимо масштабируются некоторым дробным значением.

Пока мы масштабируем каждый фрагмент карты независимо (либо с помощью слоев, либо с помощью drawImageRect + дробное CTM), для дробных значений масштаба мы получаем края, которые не выровнены по пикселям. Это вызывает единственную проблему сглаживания совпадающих краев, когда дискретный AA вызывает размытие цвета фона.

Я думаю, что это могло бы сработать, вместо этого растеризовать карту с нефракционным масштабом (скажем, 1.0), а затем атомарно уменьшить масштаб:

1) избегайте наложения отдельных листов (используйте "transform: translate ()" вместо "transform: translate3d ()")
2) принудительное создание слоев контейнера (всей карты) (используйте "transform: scale3d ()" в контейнере)
3) принудительная растеризация слоя контейнера с масштабом == 1.0

С этими изменениями изображения фрагментов карты должны быть растрированы с масштабом 1.0 (=> края выровнены по пикселям => нет артефактов стыковки), затем масштаб всего слоя карты должен быть уменьшен атомарно.

К сожалению, №3 нетривиален, и единственный способ, о котором я могу думать, это

  • начните с scale (1) и will-change: transform (подробности см. https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/Ufwrhx_iZ7U)
  • сбросить масштаб слоя до желаемого значения после первого прохода растра

например http://jsbin.com/yaqeru/10/embed?output

chrishtr / danakj правильно ли я понял семантику растрового масштаба слоя, и есть ли лучший способ зафиксировать масштаб растеризации слоя на определенном значении?

Вы можете помочь с ответом?

Я знаю, что исправление находится в стадии разработки (спасибо всем, кто настоял на этом!), Но пока что ...

https://gist.github.com/ericsoco/5712076f69f9068b11d41262b9e93666

import leaflet from 'leaflet';
// ...
patchMapTileGapBug();
// ...
function patchMapTileGapBug () {

    // Workaround for 1px lines appearing in some browsers due to fractional transforms
    // and resulting anti-aliasing. adapted from <strong i="9">@cmulders</strong>' solution:
    // https://github.com/Leaflet/Leaflet/issues/3575#issuecomment-150544739
    let originalInitTile = leaflet.GridLayer.prototype._initTile;
    if (originalInitTile.isPatched) return;

    leaflet.GridLayer.include({
        _initTile: function (tile) {
            originalInitTile.call(this, tile);

            var tileSize = this.getTileSize();

            tile.style.width = tileSize.x + 1 + 'px';
            tile.style.height = tileSize.y + 1 + 'px';
        }
    });

    leaflet.GridLayer.prototype._initTile.isPatched = true;

}

Я рад, что люди этим занимаются. В случае, если это полезно для кого-то в качестве менее чем идеального обходного пути: если вы готовы пожертвовать инерцией, установка intertia:false в инициализации карты устраняет пробелы и, возможно, zoomAnimation:false

Я думаю, что нашел (слишком сложное) решение для этого, я разместил его на https://github.com/Leaflet/Leaflet.TileLayer.NoGap - комментарии приветствуются, так как я не уверен в производительности этого на старых компьютерах / телефонах.

cc @Eschon @hyperknot

Кстати, ошибка также возникает при использовании Chrome на экране с высоким разрешением (2560x1140), без необходимости использовать дробное масштабирование листовки или масштабирование браузера.

Извините за задержку с ответом.

Я только что проверил ваше решение. Демо-версия у меня работала нормально, поэтому я попытался добавить исправление в свое приложение. Поскольку я все еще использовал старую версию Leaflet для 1.0-dev, я обновил ее до 1.0.0-rc3 с http://leafletjs.com/download.html

Первое , что я заметил, что там , кажется, ошибка здесь . Это показывает ошибку, что карта undefined, поэтому я изменил ее на this._map .

После этого он работал, но на моем рабочем компьютере он очень медленный. Особенно в Chrome, но Firefox тоже работал медленно.

Удивительно, но это не оказало большого влияния на телефоны, которые я тестировал.
Firefox на Android выглядит так же, как и раньше, Chrome работает немного медленнее, но не так уж плохо.
Safari на iOS тоже выглядит примерно так же, как и раньше.

Еще одна странная вещь, которую я заметил, - это то, что в мобильных Chrome и Safari масштабирование пальцем, похоже, привязывается к фиксированным уровням

@Eschon Спасибо за тестирование!

Я всегда делаю ошибку, набирая map вместо this._map :-(

После этого он работал, но на моем рабочем компьютере он очень медленный. Особенно в Chrome, но Firefox тоже работал медленно.

Не могли бы вы запустить на этом компьютере профайлер? Было бы неплохо узнать, связано ли замедление с операциями холста. Я заметил, что производительность IE9-IE11 немного снизилась.

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

Я действительно не смотрел, как ребята из OL3 решают эту проблему, и проводят ли они какое-то определение производительности в браузере перед включением компоновки холста. Возможно, стоит взглянуть.

Еще одна странная вещь, которую я заметил, - это то, что в мобильных Chrome и Safari масштабирование пальцем, похоже, привязывается к фиксированным уровням

Бьюсь об заклад, это просто возня с опциями zoomDelta и zoomSnap и не имеет отношения к этой ошибке.

Не могли бы вы запустить на этом компьютере профайлер? Было бы неплохо узнать, связано ли замедление с операциями холста. Я заметил, что производительность IE9-IE11 немного снизилась.

Должен ли я просто записать профиль процессора JavaScript с помощью инструментов разработчика Chrome? У меня нет большого опыта в этом тестировании производительности, поэтому я действительно не знаю, что делать с данными, которые он показывает.

Да, вот в чем дело. Затем поищите широкие объекты на карте пламени.

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

Первое, что я заметил, у версии с исправлением был дополнительный "пик" (не знаю, правильное ли это слово), вызванный _onZoomTransitionEnd

В версии без исправления _onZoomTransitionEnd занимало 1,8 мс, и я даже не видел этого на графике. В версии с исправлением это заняло 6 мс и вызывало «пик» активности.

Два других «пика» выглядят очень похоже в двух профилях, просто кажется, что все происходит немного по-другому, но разрыв между ними больше в версии с исправлением.

Надеюсь, это вам как-то поможет. Я также сохранил два профиля, поэтому, если они вам понадобятся, я могу их куда-нибудь загрузить.

В качестве грязного обходного пути я просто добавляю коэффициент масштабирования 0,002 к исходной функции, которая устанавливает свойство translate3d для каждой плитки, что почти полностью устраняет пробелы. «.002» было самым низким коэффициентом масштабирования в моих тестовых примерах, чтобы полностью устранить пробелы. Никаких побочных эффектов не заметил.

Протестировано в последних версиях Chrome, Firefox и IE 11. Протестировано на 40-дюймовом мониторе 4k с масштабированием окон, установленным на 150%, с различными значениями масштабирования в браузерах.

есть ли прогресс по этой ошибке? @AlexanderUhl вызывает ли ваше решение какое-либо изменение изображений при размещении или это влияет только на перемещение / масштабирование?

Кажется, что для решения этой проблемы было добавлено свойство will-change CSS, нужно только проверить предупреждения о производительности FF, описанные здесь: https://github.com/Leaflet/Leaflet/issues/4686

@themre : свойство scale применяется ко всем
style="width: 256px; height: 256px; transform: translate3d(545px, 745px, 0px) scale(1.002); opacity: 1;"

Но это приведет к небольшому растяжению изображения, что является нежелательным эффектом. Я пробовал версию 1.0.3, которая имеет свойство will-change CSS, но по-прежнему вызывает белый пробел. Постараюсь еще посмотреть.

Конечно, это растягивает img, вот и подход ;-) Я согласен, что этот обходной путь не идеален, но обычно это обходной путь, не так ли? Тем не менее, этот подход лучше всего работает для меня с незначительными побочными эффектами (для моего случая использования, который представляет собой картографическое приложение с плитками из mapbox, здесь, osm и т. Д.)

ну, если мы воспользуемся обычным translate , проблема исчезнет. Не знаю, действительно ли свойство CSS translate3d работает намного быстрее, чем обычный перевод, возможно, было бы лучше добавить этот флаг в качестве опции. Думаю, я собираюсь заменить translate3d на translate.

вид в действии:
translatemap

image
Возникла такая же проблема с последней версией (1.0.3).

это будет исправлено в 1.1 возможно

8 марта 2017 г., среда в 10:13, Gaurav [email protected] написал:

[image: image]
https://cloud.githubusercontent.com/assets/13112509/23697357/7dcc4cf6-040d-11e7-881a-df3a44254015.png
Возникла такая же проблема с последней версией (1.0.3).

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Leaflet/Leaflet/issues/3575#issuecomment-284987808 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AAeKj_23QbtFCaXeFjGRJcU-jg0284xBks5rjnEvgaJpZM4FO8Jh
.

Если значения nx и ny, которые используются для перевода div .leaflet-pane.leaflet-map-pane, округлены до целого числа, то проблема с линиями / пробелами решена в Safari. Я не вижу вредных побочных эффектов (для моего использования).

Найдите это:
translate3d("+n.x+"px,"+n.y+"px,0)")
и заменить на:
translate3d("+Math.round(n.x)+"px,"+Math.round(n.y)+"px,0)")

@LudwigBogner, скорее всего, все равно не настройте масштабирование браузера или во время анимации масштабирования). См. Приведенный выше пример, в котором сами переводы округлены: http://playground-leaflet.rhcloud.com/vicu/edit?html , output

Только что укусил этот. Мне кажется, что его не существует в Chromium или FireFox. Только некоторые частичные увеличения в Safari.

Прогресс?

Эта проблема все еще существует в Chrome и Firefox (Mac). Я использую zoomDelta и zoomSnap 0,25.

Как ни странно, установка .leaflet-tile { will-change: auto !important; } (или "unset") устраняет проблему в Firefox (только). Все еще ищу обходной путь для Chrome.

Другие предлагаемые исправления для отключения инерции и изменения кода translate3d для добавления округления также не работали.

@jawinn Я думаю, вы можете добиться успеха, установив для L_DISABLE_3D значение true , IIRC, это имеет примерно тот же эффект, что и изменение will-change , то есть удалит аппаратное ускорение при рендеринге карта. Имейте в виду, что это, вероятно, даст вам гораздо худшую производительность для анимации масштабирования, панорамирования и т. Д.

Все это очень чувствительно к деталям реализации браузера, поэтому отнеситесь к этому с долей скептицизма, с тех пор как я изучал это довольно давно.

@jawinn
Обход, который я использую, - это работа

/* 
 * Workaround for 1px lines appearing in some browsers due to fractional transforms
 * and resulting anti-aliasing.
 * https://github.com/Leaflet/Leaflet/issues/3575
 */
(function(){
    var originalInitTile = L.GridLayer.prototype._initTile
    L.GridLayer.include({
        _initTile: function (tile) {
            originalInitTile.call(this, tile);

            var tileSize = this.getTileSize();

            tile.style.width = tileSize.x + 1 + 'px';
            tile.style.height = tileSize.y + 1 + 'px';
        }
    });
})()

Это не идеально, но, насколько я могу судить, это не влияет на производительность

Спасибо ребята. @Eschon Это сработало. В Chrome или Firefox больше нет пробелов.

Тем, кто собирается использовать «1px fix», будьте готовы к тому, что ваши изображения станут размытыми и немного битыми:

Для меня, который часами рендерил и фотографировал исходное изображение, это было неприемлемо. Так что мне пришлось использовать L_DISABLE_3D = true и терпеть мерцающую анимацию и резкое увеличение в конце ..

Подробнее: Я использую плагин leaflet-rastercoords, и мои настройки карты следующие:

    map = L.map('map', {
        center: [3250, 1700],
        zoom: 5,
        minZoom: 2,
        maxZoom: 6,
        maxBoundsViscosity: 1,
    });

В: Какая следующая лучшая альтернатива Leaflet не имеет такого бага с «пространством между плитками»?

Сам погуглию, но может кто-то уже пошел по этому пути.

@ n3tman вы тоже пробовали плагин NoGap ? Может, стоит попробовать.

@perliedman, спасибо, что упомянули об этом.
К сожалению, не удалось заставить его работать локально .. Получена ошибка Cannot read property 'appendChild' of undefined в функции _initContainer .
Пробовал с предыдущими версиями Leaflet (до 1.0.2) - результат тот же. Демо показывает ту же ошибку в Chrome / Firefox.
Может быть, я упускаю что-то очевидное, не могли бы вы помочь?

Тот же зазор в 1 пиксель здесь.

Версия Chrome 66.0.3359.139 (64-разрядная)
Windows 10 (1709)
Брошюра 1.3.1

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

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

Похоже, это связано с # 6069 и (отчасти) исправлено # 6240, хотя пробелы все еще появляются при масштабировании и панорамировании.

Я также удалил обходной путь из других моих проектов, и, похоже, он тоже происходит там, поэтому он не относится к плагину GoogleMutant

Вдохновленный обходным решением @cmulders 1px ... Он работает, но размытый эффект, который появляется на некоторых изображениях,

(function(){
    var originalInitTile = L.GridLayer.prototype._initTile
    L.GridLayer.include({
        _initTile: function (tile) {
            originalInitTile.call(this, tile);

            var tileSize = this.getTileSize();
            var map = this._map;
            var isFix = function() {
                return !(parseFloat(tile.style.width) % 0 === 0);
            };
            var fixOn = function() {
                if(!isFix()) return;
                tile.style.width = tileSize.x + 0.5 + 'px';
                tile.style.height = tileSize.y + 0.5 + 'px';
            };
            var fixOff = function() {
                if(isFix()) return;
                tile.style.width = tileSize.x + 'px';
                tile.style.height = tileSize.y + 'px';
            };
            var checkFix = function(){
                var zoom = map.getZoom();
                if(zoom % 1 === 0) {
                    fixOff();
                }
                else {
                    fixOn();
                }                
            };
            map.on('zoomstart',fixOn);
            map.on('zoomend',checkFix);
            checkFix();
        }
    });
})()

screen shot 2018-09-28 at 3 56 33 pm
Спустя более 3 лет эта ошибка все еще не решена в Chrome! Я не вижу эффекта в Firefox или Safari. Спасибо @cmulders за вашу работу!

То же самое и здесь (Windows 7 Enterprise SP1). Линии тут и там в Firefox, полная линейная сетка в Chrome, но нормально в IE11. Исправление @cmulders работает в Firefox и Chrome.

Вот полные результаты еще нескольких тестов:

  • Windows 7 Корпоративная с пакетом обновления 1 (SP1)

    • IE11: ОК

    • Chrome, Firefox: не нормально

  • Windows 10

    • IE11, Edge, Firefox: ОК

    • Chrome: не в порядке

  • macOS

    • Safari, Chrome, Firefox: ОК

  • Linux:

    • Chrome, Firefox: ОК

  • Android:

    • Chrome, Firefox: ОК

    • Приложение с использованием WebView: ОК

Дальнейшее тестирование показало, что это, очевидно, проблема очень низкого уровня, не имеющая ничего общего с Leaflet.
Тестирование на Windows 7 Enterprise SP1 изначально проводилось на двух разных ПК, но оба с графикой NVIDIA.

Когда тот же тест был проведен в Windows 7 Enterprise SP1 с графикой AMD Radeon, он прошел нормально, никаких линий между плитками в Chrome или Firefox!

Поскольку NoGap упоминался ранее, я просто хотел сообщить вам, что я взял исходный код и изменил его, чтобы он работал с текущими версиями листовок (например, листовка ^ 1.3.3):
https://github.com/Endebert/squadmc/blob/master/src/assets/Leaflet_extensions/NoGapTileLayer.js

Затем его можно использовать как обычный TileLayer: https://github.com/Endebert/squadmc/blob/master/src/assets/SquadMap.js#L34

Он использует функции es6, поэтому вам, возможно, придется внести некоторые изменения, чтобы он работал на вас.

Поскольку NoGap упоминался ранее, я просто хотел сообщить вам, что я взял исходный код и изменил его, чтобы он работал с текущими версиями листовок (например, листовка ^ 1.3.3):
https://github.com/Endebert/squadmc/blob/master/src/assets/Leaflet_extensions/NoGapTileLayer.js

Затем его можно использовать как обычный TileLayer: https://github.com/Endebert/squadmc/blob/master/src/assets/SquadMap.js#L34

Он использует функции es6, поэтому вам, возможно, придется внести некоторые изменения, чтобы он работал на вас.

@Endebert Удалось ли вам заставить NoGap работать? Некоторое время назад я получил ту же ошибку, что и @ n3tman :
Cannot read property 'appendChild' of undefined ошибка в функции _initContainer .

Единственное изменение, которое я нашел в вашей версии, - это добавление try ... catch к вызову level.ctx.drawImage(imageSource, offset.x, offset.y, tileSize.x, tileSize.y);
и это не помогло.

@TomazicM Да, моя реализация используется в моем приложении SquadMC . На настольных компьютерах это может быть не так очевидно, но на мобильных устройствах очевидно, что дробное масштабирование без привязки включено.

Что касается реализации: в исходной версии используется TileLayer.include() , а я использую TileLayer.extend() . Я считаю, что это важное различие. Как уже упоминалось, это новый класс, который предназначен для использования вместо TileLayer.

Итак, вот мой результат 2-х часового расследования. Проблема вызвана десятичными знаками пикселей внутри translate3d .

<div class="leaflet-pane leaflet-map-pane" style="transform: translate3d(-10.7773px, -8.41406px, 0px);"></div>

Вот почему он хорошо работает с L_DISABLE_3D = true , потому что согласно этой части исходного кода он не использует translate3d .

Решение

Только не допускайте десятичных пикселей. Этого можно достичь, например:

```js
var pos = (offset && offset.round()) || new Point(0, 0);
```

  • или многими и многими другими решениями ...

Итак, вот мой результат 2-х часового расследования. Проблема вызвана десятичными знаками пикселей внутри translate3d .

Я предполагаю, что сегодня 2019 год, и использование translate3d чтобы заставить современный браузер использовать графический процессор, довольно устарело. Хорошая работа @mdorda

@mdorda Привет! Звучит интересно, но имейте в виду, что округление уже обсуждалось ранее в этой теме: https://github.com/Leaflet/Leaflet/issues/3575#issuecomment -327237235

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

Первым шагом к решению этой проблемы будет отправка PR, чтобы мы могли это проверить.

Для тех, кто работает с Angular 2+, я решил это так. Это работает для моего простого случая использования, не знаю о других последствиях ...

const tileLayer = new TileLayer(this.tileLayerUrlTemplate, this.tileLayerOptions);
tileLayer.on('load', () => {
    for (const tile of Array.from(document.getElementsByClassName('leaflet-tile'))) {
        (<HTMLElement>tile).style.height = (tile.clientHeight + 1) + 'px';
        (<HTMLElement>tile).style.width = (tile.clientWidth + 1) + 'px';
    }
});
this.map.addLayer(tileLayer);

Я вижу этот пиксельный разрыв при частичном увеличении в LeafletJS версии 1.5.1 в Windows 10 Pro 1903 с Mozilla Firefox 69.0.1 (64-разрядная версия).
Пиксельный разрыв
Я думаю, что причина пиксельного разрыва может быть не в функции CSS translate3d (), а в scale ().
image
В версии 1.5.1, как я вижу, параметры translate3d () только целые.


С уважением, IMMSPgisgroup

Вы нашли какое-нибудь решение этой ошибки?

У меня сейчас такая же проблема, и я не нашел решения.

error

Я использую:

  • Windows 10 Домашняя 1803
  • Google Chrome Versión 77.0.3865.90 (официальная сборка) (64 бит)
  • Брошюра 1.5.1

Когда я использую Firefox, белые линии не появляются

no_error

@ Arenivar93

Нет, я наблюдал за этим уже несколько лет. Они пробовали несколько обходных путей, но ничего не помогло исправить это без серьезных компромиссов. Корень проблемы в хроме: https://bugs.chromium.org/p/chromium/issues/detail?id=600120

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

Проблема очень заметна, особенно на более темных картах или изображениях (в моем случае). Я думаю, что сглаживание в браузере вызывает пробелы между плитками из-за _fractional_ преобразования во время масштабирования, как упоминал Иван.

Пока не будет найдено лучшее решение, я буду использовать этот обходной путь (или взломать), чтобы увеличить плитки на 1 пиксель с побочным эффектом, что они перекрываются на 1 пиксель. Кроме того, плитки будут немного увеличены и масштабированы (на 1 пиксель).

/* 
 * Workaround for 1px lines appearing in some browsers due to fractional transforms
 * and resulting anti-aliasing.
 * https://github.com/Leaflet/Leaflet/issues/3575
 */
(function(){
    var originalInitTile = L.GridLayer.prototype._initTile
    L.GridLayer.include({
        _initTile: function (tile) {
            originalInitTile.call(this, tile);

            var tileSize = this.getTileSize();

            tile.style.width = tileSize.x + 1 + 'px';
            tile.style.height = tileSize.y + 1 + 'px';
        }
    });
})()

нам очень повезло с этим решением. не идеально, но, на наш взгляд, лучше линий.

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

опять не идеально, но помогает

@colbyfayock Могу я спросить, где и как вы реализуете свое решение?

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

// Fix via https://github.com/Leaflet/Leaflet/issues/3575#issuecomment-150544739
import L from 'leaflet';

(function () {
  if (!L || !L.GridLayer || !L.GridLayer.prototype) return;

  var originalInitTile = L.GridLayer.prototype._initTile;

  L.GridLayer.include({
    _initTile: function (tile) {
      originalInitTile.call(this, tile);

      var tileSize = this.getTileSize();

      tile.style.width = tileSize.x + 1 + 'px';
      tile.style.height = tileSize.y + 1 + 'px';
    }
  });
})();

возврат вверху объясняется тем, что он предварительно компилируется и не всегда доступен (здесь не важно)

то я просто импортирую его после импорта листовки

import L from 'leaflet';
...
import '../plugins/leaflet-tilelayer-subpixel-fix';

Мой обходной путь - округлить значение преобразования без внесения изменений в исходный код.
Используется с реакцией, но может использоваться любым интерфейсом, так как это JQuery
Не говоря, что это лучший вариант, просто доводит дело до конца.
Это решает проблему, и я не заметил заметного размытия или какого-либо дефекта. Стоит попробовать.

    this.leftMap.on('move', () => {
      const mapDiv = window.document.getElementsByClassName("leaflet-pane leaflet-map-pane")
      let styleString = mapDiv.getAttribute("style");
      const transformValue = styleString.substring(styleString.indexOf("(")+1,styleString.indexOf("px,"));
      const roundedTransformValue = Math.round(parseFloat(transformValue));
      styleString = styleString.replace(transformValue,roundedTransformValue);
      mapDiv.setAttribute("style",styleString);
    });

@colbyfayock - Мне кажется, это не помогает. :(

Я использую листок React с масштабированием 0.

image

решение никогда не было идеальным для нас, но в нашем определенно нет таких последовательных линий. Я не был уверен в функции zoomSnap поэтому пришлось поискать ее, я не совсем уверен, как 0 будет работать с этим свойством, судя по тому, как я читал описание. Вы пытались удалить это, чтобы увидеть, присутствуют ли линии без него?

https://leafletjs.com/examples/zoom-levels/#fractional -zoom

Изменить: продолжил читать после примера gif

zoomSnap можно установить равным нулю. Это означает, что Leaflet не будет привязать уровень масштабирования.

Я предполагаю, что вы попадаете в дробные состояния, с которыми решение не может справиться, кроме полной целочисленной привязки по умолчанию 🤷‍♂️ но не уверен

Leaflet 1.7-dev и проблема не устранена. Линии на границах плиток в Firefox и Chrome в Windows 7 и Windows 10. Нет проблем в IE11 и старом (не Crome) Edge, такая же проблема в новом Edge на базе Chrome.

Есть два рабочих решения, но мне не нравится ни одно из них. Один из них - отключить 3D с помощью <script>L_DISABLE_3D = true;</script> , но это означает потерю анимированных операций. Другой вариант - удлинить элементы HTML плитки на 1 пиксель, но это означает, что плитки будут слегка размытыми.

Я нашел очень примитивное решение, но по самой своей природе оно работает на 100%. Может, кому-то это пригодится.

Решение состоит в том, чтобы отобразить один и тот же проблемный слой листов дважды на двух разных панелях карты, одна из которых расположена под другой и имеет смещение [-1, -1].

Код выглядит примерно так:

<style>
  .originOffset {
    transform: translate(-1px, -1px);
  }
</style>

<script>
  var pane1 = map.createPane('pane1');
  var pane2 = map.createPane('pane2');

  pane1.style.zIndex = 210;
  pane2.style.zIndex = 220;

  L.DomUtil.addClass(pane1, 'originOffset');

  var layer1 = L.tileLayer( ... , {
    pane: 'pane1',
    ...
  });
  var layer2 = L.tileLayer( ... , {
    pane: 'pane2,
    ...
  });
  var layer = L.layerGroup([layer1, layer2]);
</script>

из предложения @cmulders У меня есть эта строка, и между плитками больше нет места

map.on ('увеличить перетаскивание', function () {
$ ('# map> div.leaflet-pane.leaflet-map-pane> div.leaflet-pane.leaflet-tile-pane> div> div> img'). each (function () {
if (String ($ (this) .css ("width")). includes ('. 5') === false) {
var imgW = String ($ (this) .css ("ширина")). split ("px") .join (".5")
var imgH = String ($ (this) .css ("height")). split ("px") .join (".5")
$ (this) .css ("ширина", imgW);
$ (this) .css ("высота", imgH);
}
});

Я использую @mdorda , когда панорамирование и масштабирование приводят к целым числам в вызове leaflet-map-pane к translate3d() вместо долей пикселей, тогда (с моими шорами) проблема исчезла и все снова красиво. Я не утверждаю, что понимаю все нюансы того, когда необходимо сохранять дроби, но если это дает Leaflet некоторое облегчение от проблемы браузера, тогда было бы приемлемо, если бы L.Draggable._onMove положили в какой-то пол

    offset.x = Math.floor(offset.x / this._parentScale.x);
    offset.y = Math.floor(offset.y / this._parentScale.y);

или даже в L.DomUtils.setTransform

    pos.x = Math.floor(pos.x);
    pos.y = Math.floor(pos.y);

так что ни один перевод не дается дроби, но это, вероятно, было бы расточительно, поскольку изображения плитки не приводят к дробному преобразованию (верно?).

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

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

@tokenflow : эта тема буквально посвящена «расстоянию между плитками при дробном масштабировании в браузерах Webkit». Я действительно стараюсь не показывать себя дерзким, но вы действительно думаете, что ваша информация о масштабировании до 100% может быть полезна для кого-то?

Я столкнулся с этой ошибкой в ​​версии для Linux как Firefox, так и Chromium.
Проблема (по крайней мере, для Firefox), похоже, заключается в scale() основного .leaflet-tile-container , который мне удалось найти с помощью document.querySelector('.leaflet-tile-container:last-of-type').getAttribute('style') .
E. g. у вас есть scale(0.707107) , но все дочерние элементы img имеют ширину / высоту 256. Итак, вы вычислите: 0.707107 * 256px = 181.019392px , которое является десятичным числом. Когда вы затем округляете пиксели и принимаете это за масштаб, кажется, что это работает в Firefox / Linux: 181px / 256px = 0.70703125 .

Я написал функцию, которая помогает отлаживать:

function getCurrentScale (doFix = false) {
    const tileContainer = document.querySelector('.leaflet-tile-container:last-of-type');

    if (!tileContainer) {
        return;
    }

    const tileStyle = tileContainer.getAttribute('style');
    const scaleRegEx = /scale\(([0-9\.]+)\)/i;
    const matches = scaleRegEx.exec(tileStyle);

    if (!matches || matches.length !== 2) {
        return;
    }

    const scale = parseFloat(matches[1]);

    const mod = (scale * 256) % 1;

    if (mod) {
        console.log('scale is off by px:', mod);
        if (doFix) {
            const newScale = Math.round(scale * 256) / 256;
            console.log('old scale / new scale', scale, newScale);
            const newStyle = tileStyle.replace(scaleRegEx, `scale(${newScale})`);
            tileContainer.setAttribute('style', newStyle);
        }
    } else {
        console.log('scale seems to be fine:', scale);
    }
}

Если вы вызываете его с true в качестве первого параметра, он пытается исправить масштаб. Однако у меня это работает только в Firefox.

Я нашел глупое исправление, которое действительно не должно работать.

.leaflet-tile-container img {
    width: 256.5px !important;
    height: 256.5px !important;
}

Это должно привести к массивным швам вокруг плиток, но я их совсем не вижу, и это исправляет пространство между плитками.

@ ChrisLowe-Takor каким-то образом это действительно работает, хотя на первый взгляд я не могу понять почему.
Это может быть предпочтительнее обходного пути JavaScript, который я использую сейчас, но мне придется еще раз изучить его и провести некоторое тестирование, но.

@ ChrisLowe-Takor - Вау, у меня тоже работает! Хорошая находка 👍

@ ChrisLowe-Takor Также, похоже, у меня работает в R с использованием пакета Leaflet. Большое спасибо!

@ ChrisLowe-Takor Каким-то образом этот трюк действительно работает со всеми тупыми современными браузерами (для старого почтенного IE11 он не нужен)! Следует иметь в виду только одну вещь: если для слоя плитки используется опция detectRetina а для отображения используется сетчатка, размер плитки будет 125 x 125 пикселей.

Но это решение имеет тот же недостаток, что и все решения, основанные на расширении контейнера тайлов: тайл немного размывается.

@ ChrisLowe-Takor Я пробовал. Если maxZoom больше maxNativeZoom, плитки не будут отображаться должным образом.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги