From 6e5d3e26e64665e32749fe1caed6426e208d796b Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 14 Jan 2014 16:26:58 +0200 Subject: [PATCH 01/10] use translate2d instead of top/left for tiles when hw-accelerated this fixes gaps between tiles in Safari and makes iOS Safari butter smooth when zooming --- src/dom/DomUtil.js | 16 +++++++--------- src/layer/tile/GridLayer.js | 7 +++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index 77d592d4..dddd83c1 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -130,24 +130,22 @@ L.DomUtil = { return false; }, - setTransform: function (el, offset, scale) { - var is3d = L.Browser.webkit3d, - pos = offset || new L.Point(0, 0); + setTransform: function (el, offset, scale, no3d) { + no3d = no3d || !L.Browser.any3d, + pos = offset || new L.Point(0, 0); el.style[L.DomUtil.TRANSFORM] = - 'translate' + (is3d ? '3d(' : '(') + pos.x + 'px,' + pos.y + 'px' + (is3d ? ',0)' : ')') + + 'translate' + (no3d ? '(' : '3d(') + pos.x + 'px,' + pos.y + 'px' + (no3d ? ')' : ',0)') + (scale ? ' scale(' + scale + ')' : ''); }, - setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean]) + setPosition: function (el, point, no3d) { // (HTMLElement, Point[, Boolean]) // jshint camelcase: false el._leaflet_pos = point; - if (!disable3D && L.Browser.any3d) { - // on WebKit browsers, using translate3d instead of translate makes animation smoother - // as it ensures HW acceleration is used. Firefox doesn't care (same speed either way). - L.DomUtil.setTransform(el, point); + if (L.Browser.any3d) { + L.DomUtil.setTransform(el, point, null, no3d); } else { el.style.left = point.x + 'px'; el.style.top = point.y + 'px'; diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 4ec16d5d..9ec46cc4 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -405,10 +405,9 @@ L.GridLayer = L.Layer.extend({ setTimeout(L.bind(this._tileReady, this, null, tile), 0); } - // Chrome 20 layouts much faster with top/left (verify with timeline, frames) - // Android 4 browser has display issues with top/left and requires transform instead - // (other browsers don't currently care) - see debug/hacks/jitter.html for an example - L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome); + // we prefer translate over top/left because it fixes gaps between tiles in Safari, + // but not 3d so that we don't create a HW-accelerated layer from each tile which is slow + L.DomUtil.setPosition(tile, tilePos, true); // save tile in cache this._tiles[this._tileCoordsToKey(coords)] = tile; From cfde916edf7087b137a6a548fe7a14a558d4afb1 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 14 Jan 2014 16:28:40 +0200 Subject: [PATCH 02/10] make sure tiles in iOS Safari are not hw-accelerated layers --- src/layer/tile/GridLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 9ec46cc4..c9533850 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -383,7 +383,7 @@ L.GridLayer = L.Layer.extend({ // without this hack, tiles disappear after zoom on Chrome for Android // https://github.com/Leaflet/Leaflet/issues/2078 - if (L.Browser.mobileWebkit3d) { + if (L.Browser.android && !L.Browser.android23) { tile.style.WebkitBackfaceVisibility = 'hidden'; } }, From 592d2ce8f492b33b548d963c1af1c8eb647f71ad Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 14 Jan 2014 16:29:39 +0200 Subject: [PATCH 03/10] horrible hacks to fix iOS Safari bugging out hw-acceleration --- dist/leaflet.css | 5 +++++ src/layer/tile/GridLayer.js | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/dist/leaflet.css b/dist/leaflet.css index e419c4db..c7a00057 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -140,6 +140,11 @@ opacity: 1; } +.leaflet-hack { + width: 1px; + height: 1px; +} + .leaflet-zoom-anim .leaflet-zoom-animated { -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index c9533850..327e1491 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -171,6 +171,8 @@ L.GridLayer = L.Layer.extend({ this._bgBuffer = L.DomUtil.create('div', className, this._container); this._tileContainer = L.DomUtil.create('div', className, this._container); + L.DomUtil.setTransform(this._tileContainer); + } else { this._tileContainer = this._container; } @@ -297,6 +299,14 @@ L.GridLayer = L.Layer.extend({ for (i = 0; i < tilesToLoad; i++) { this._addTile(queue[i], fragment); } + + if (this._zoomAnimated) { + // a pretty funny hack - Safari bugs out with HW-acceleration when you pan to the right + // and there are no elements in the top left corner of the tile container, this fixes it + var hack = L.DomUtil.create('div', 'leaflet-hack', this._tileContainer); + L.DomUtil.setTransform(hack); + } + this._tileContainer.appendChild(fragment); }, From 322039ea85c42ff0fd76d3a87954bf81cc260781 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 14 Jan 2014 17:15:58 +0200 Subject: [PATCH 04/10] move the hack to tile container reset --- src/layer/tile/GridLayer.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 327e1491..7641d284 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -197,6 +197,13 @@ L.GridLayer = L.Layer.extend({ this._tileContainer.innerHTML = ''; + if (this._zoomAnimated) { + // a pretty funny hack - Safari bugs out with HW-acceleration when you pan to the right + // and there are no elements in the top left corner of the tile container, this fixes it + var hack = L.DomUtil.create('div', 'leaflet-hack leaflet-tile-loaded', this._tileContainer); + L.DomUtil.setTransform(hack, null, null, true); + } + if (this._zoomAnimated && e && e.hard) { this._clearBgBuffer(); } @@ -300,13 +307,6 @@ L.GridLayer = L.Layer.extend({ this._addTile(queue[i], fragment); } - if (this._zoomAnimated) { - // a pretty funny hack - Safari bugs out with HW-acceleration when you pan to the right - // and there are no elements in the top left corner of the tile container, this fixes it - var hack = L.DomUtil.create('div', 'leaflet-hack', this._tileContainer); - L.DomUtil.setTransform(hack); - } - this._tileContainer.appendChild(fragment); }, From cd0c9ff8a50b00d56b9ea66a533d0f5c32bf19ef Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 14 Jan 2014 17:18:51 +0200 Subject: [PATCH 05/10] position the hack absolutely --- src/layer/tile/GridLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 7641d284..4102a052 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -200,7 +200,7 @@ L.GridLayer = L.Layer.extend({ if (this._zoomAnimated) { // a pretty funny hack - Safari bugs out with HW-acceleration when you pan to the right // and there are no elements in the top left corner of the tile container, this fixes it - var hack = L.DomUtil.create('div', 'leaflet-hack leaflet-tile-loaded', this._tileContainer); + var hack = L.DomUtil.create('div', 'leaflet-hack leaflet-tile leaflet-tile-loaded', this._tileContainer); L.DomUtil.setTransform(hack, null, null, true); } From f081ff290878c11fa5a4b4f750afa195a562079a Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 14 Jan 2014 17:20:58 +0200 Subject: [PATCH 06/10] fix build --- src/dom/DomUtil.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index dddd83c1..6faa8f06 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -131,8 +131,8 @@ L.DomUtil = { }, setTransform: function (el, offset, scale, no3d) { - no3d = no3d || !L.Browser.any3d, - pos = offset || new L.Point(0, 0); + var pos = offset || new L.Point(0, 0); + no3d = no3d || !L.Browser.any3d; el.style[L.DomUtil.TRANSFORM] = 'translate' + (no3d ? '(' : '3d(') + pos.x + 'px,' + pos.y + 'px' + (no3d ? ')' : ',0)') + From cf35d6965526bf3abe90975c818bf0a2c4472308 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 27 Jan 2014 17:54:12 +0200 Subject: [PATCH 07/10] size-based tile layer HW hack; use top/left for tiles everywhere --- src/core/Browser.js | 1 + src/dom/DomUtil.js | 10 ++++------ src/layer/tile/GridLayer.js | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/core/Browser.js b/src/core/Browser.js index 5015e730..856b9990 100644 --- a/src/core/Browser.js +++ b/src/core/Browser.js @@ -41,6 +41,7 @@ android: ua.indexOf('android') !== -1, android23: android23, chrome: ua.indexOf('chrome') !== -1, + safari: ua.indexOf('safari') !== -1, ie3d: ie3d, webkit3d: webkit3d, diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index 6faa8f06..ba77e735 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -130,13 +130,11 @@ L.DomUtil = { return false; }, - setTransform: function (el, offset, scale, no3d) { + setTransform: function (el, offset, scale) { var pos = offset || new L.Point(0, 0); - no3d = no3d || !L.Browser.any3d; el.style[L.DomUtil.TRANSFORM] = - 'translate' + (no3d ? '(' : '3d(') + pos.x + 'px,' + pos.y + 'px' + (no3d ? ')' : ',0)') + - (scale ? ' scale(' + scale + ')' : ''); + 'translate3d(' + pos.x + 'px,' + pos.y + 'px' + ',0)' + (scale ? ' scale(' + scale + ')' : ''); }, setPosition: function (el, point, no3d) { // (HTMLElement, Point[, Boolean]) @@ -144,8 +142,8 @@ L.DomUtil = { // jshint camelcase: false el._leaflet_pos = point; - if (L.Browser.any3d) { - L.DomUtil.setTransform(el, point, null, no3d); + if (L.Browser.any3d && !no3d) { + L.DomUtil.setTransform(el, point); } else { el.style.left = point.x + 'px'; el.style.top = point.y + 'px'; diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 4102a052..2760e490 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -195,13 +195,15 @@ L.GridLayer = L.Layer.extend({ this._tilesToLoad = 0; this._tilesTotal = 0; - this._tileContainer.innerHTML = ''; + var front = this._tileContainer; - if (this._zoomAnimated) { - // a pretty funny hack - Safari bugs out with HW-acceleration when you pan to the right - // and there are no elements in the top left corner of the tile container, this fixes it - var hack = L.DomUtil.create('div', 'leaflet-hack leaflet-tile leaflet-tile-loaded', this._tileContainer); - L.DomUtil.setTransform(hack, null, null, true); + front.innerHTML = ''; + + // hack that prevents hw layers "stretching" when loading new tiles + if (this._zoomAnimated && L.Browser.safari) { + front.style.width = '1600px'; + front.style.height = '1600px'; + front.style.WebkitTransformOrigin = '0 0'; } if (this._zoomAnimated && e && e.hard) { @@ -415,8 +417,8 @@ L.GridLayer = L.Layer.extend({ setTimeout(L.bind(this._tileReady, this, null, tile), 0); } - // we prefer translate over top/left because it fixes gaps between tiles in Safari, - // but not 3d so that we don't create a HW-accelerated layer from each tile which is slow + // we prefer top/left over translate3d so that we don't create a HW-accelerated layer from each tile + // which is slow, and it also fixes gaps between tiles in Safari L.DomUtil.setPosition(tile, tilePos, true); // save tile in cache From 9228b3089f89840c857aad6039ab063508f9e93a Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 27 Jan 2014 18:04:58 +0200 Subject: [PATCH 08/10] fix blurry non-retina tiles on retina iOS --- dist/leaflet.css | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/leaflet.css b/dist/leaflet.css index c7a00057..89bfcc40 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -25,6 +25,7 @@ -moz-user-select: none; user-select: none; -webkit-user-drag: none; + image-rendering:-webkit-optimize-contrast; } .leaflet-marker-icon, .leaflet-marker-shadow { From 187d9c2368fd7700f42ef24b6706ccf5ec7b8c93 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 27 Jan 2014 18:18:04 +0200 Subject: [PATCH 09/10] move Safari hacks to CSS, do not optimize contrast in Chrome --- dist/leaflet.css | 11 ++++++++++- src/core/Browser.js | 5 +++-- src/layer/tile/GridLayer.js | 5 ----- src/map/Map.js | 1 + 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dist/leaflet.css b/dist/leaflet.css index 89bfcc40..ff692dbb 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -25,7 +25,16 @@ -moz-user-select: none; user-select: none; -webkit-user-drag: none; - image-rendering:-webkit-optimize-contrast; + } +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; } .leaflet-marker-icon, .leaflet-marker-shadow { diff --git a/src/core/Browser.js b/src/core/Browser.js index 856b9990..f60e0390 100644 --- a/src/core/Browser.js +++ b/src/core/Browser.js @@ -12,6 +12,7 @@ webkit = ua.indexOf('webkit') !== -1, phantomjs = ua.indexOf('phantom') !== -1, android23 = ua.search('android [23]') !== -1, + chrome = ua.indexOf('chrome') !== -1, mobile = typeof orientation !== 'undefined', msPointer = navigator.msPointerEnabled && navigator.msMaxTouchPoints && !window.PointerEvent, @@ -40,8 +41,8 @@ gecko: (ua.indexOf('gecko') !== -1) && !webkit && !window.opera && !ie, android: ua.indexOf('android') !== -1, android23: android23, - chrome: ua.indexOf('chrome') !== -1, - safari: ua.indexOf('safari') !== -1, + chrome: chrome, + safari: !chrome && ua.indexOf('safari') !== -1, ie3d: ie3d, webkit3d: webkit3d, diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 2760e490..d5906879 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -200,11 +200,6 @@ L.GridLayer = L.Layer.extend({ front.innerHTML = ''; // hack that prevents hw layers "stretching" when loading new tiles - if (this._zoomAnimated && L.Browser.safari) { - front.style.width = '1600px'; - front.style.height = '1600px'; - front.style.WebkitTransformOrigin = '0 0'; - } if (this._zoomAnimated && e && e.hard) { this._clearBgBuffer(); diff --git a/src/map/Map.js b/src/map/Map.js index 679bc034..4ae2101d 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -435,6 +435,7 @@ L.Map = L.Evented.extend({ (L.Browser.touch ? ' leaflet-touch' : '') + (L.Browser.retina ? ' leaflet-retina' : '') + (L.Browser.ielt9 ? ' leaflet-oldie' : '') + + (L.Browser.safari ? ' leaflet-safari' : '') + (this._fadeAnimated ? ' leaflet-fade-anim' : '')); var position = L.DomUtil.getStyle(container, 'position'); From 02d58967400e7c59e5e7f10d8de0ea0f88aec43c Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 27 Jan 2014 18:21:06 +0200 Subject: [PATCH 10/10] remove leftover code --- dist/leaflet.css | 5 ----- src/layer/tile/GridLayer.js | 6 +----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/dist/leaflet.css b/dist/leaflet.css index ff692dbb..1edf2b14 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -150,11 +150,6 @@ opacity: 1; } -.leaflet-hack { - width: 1px; - height: 1px; -} - .leaflet-zoom-anim .leaflet-zoom-animated { -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index d5906879..fa08f270 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -195,11 +195,7 @@ L.GridLayer = L.Layer.extend({ this._tilesToLoad = 0; this._tilesTotal = 0; - var front = this._tileContainer; - - front.innerHTML = ''; - - // hack that prevents hw layers "stretching" when loading new tiles + this._tileContainer.innerHTML = ''; if (this._zoomAnimated && e && e.hard) { this._clearBgBuffer();