From a309b2405351dc760edcf0acd5218d82faa577fb Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 17 Dec 2012 10:23:02 +0100 Subject: [PATCH] Only use 3d transforms when stylesheet has transform on layerContainerDiv This allows users to control whether 3d acceleration should be used or not: Just like with plain web pages, having a stylesheet that sets a transform on the map's layerContainerDiv will make OpenLayers use translate3d and scale3d. When no such transform is set in the stylesheet, style.left and style.top will be used, except for e.g. pinch zoom, where scaling is needed. --- lib/OpenLayers/Map.js | 86 ++++++++++++++++++++++++++----------------- tests/Map.html | 22 ++++++++--- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index c8963747ef..07c15496ed 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -2660,41 +2660,61 @@ OpenLayers.Map = OpenLayers.Class({ * the map's * scale - {Number} scale. Defaults to 1 if not provided. */ - applyTransform: function(dx, dy, scale) { - var origin = this.layerContainerOriginPx; - dx = dx || origin.x; - dy = dy || origin.y; - scale = scale || 1; + applyTransform: function(x, y, scale) { + scale = scale || 1; + var origin = this.layerContainerOriginPx, + needTransform = scale !== 1; + x = x || origin.x; + y = y || origin.y; - var style = this.layerContainerDiv.style, - transform = this.applyTransform.transform, - template = this.applyTransform.template; + var style = this.layerContainerDiv.style, + transform = this.applyTransform.transform, + template = this.applyTransform.template; - if (transform === undefined) { - transform = OpenLayers.Util.vendorPrefix.style('transform'); - this.applyTransform.transform = transform; - } - - if (transform) { - if (!template) { - // try translate3d - template = ['translate3d(', ',0) ', 'scale3d(', ',1)']; - style[transform] = [template[0], '0,0', template[1]].join(''); - // if translate3d does not stick, use translate and scale - if (!~style[transform].indexOf(template[0])) { - template = ['translate(', ') ', 'scale(', ')']; - } - this.applyTransform.template = template; - } - style[transform] = [ - template[0], dx, 'px,', dy, 'px', template[1], - template[2], scale, ',', scale, template[3] - ].join(''); - } else { - style.left = dx + 'px'; - style.top = dy + 'px'; - } - }, + if (transform === undefined) { + transform = OpenLayers.Util.vendorPrefix.style('transform'); + this.applyTransform.transform = transform; + if (transform) { + // Try translate3d, but only if the viewPortDiv has a transform + // defined in a stylesheet + var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv, OpenLayers.Util.vendorPrefix.css('transform')); + if (!computedStyle || computedStyle !== 'none') { + template = ['translate3d(', ',0) ', 'scale3d(', ',1)']; + style[transform] = [template[0], '0,0', template[1]].join(''); + } + // If no transform is defined in the stylesheet or translate3d + // does not stick, use translate and scale + if (!template || !~style[transform].indexOf(template[0])) { + template = ['translate(', ') ', 'scale(', ')']; + } + this.applyTransform.template = template; + } + } + + // If we do 3d transforms, we always want to use them. If we do 2d + // transforms, we only use them when we need to. + if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) { + // Our 2d transforms are combined with style.left and style.top, so + // adjust x and y values and set the origin as left and top + if (needTransform === true && template[0] === 'translate(') { + x -= origin.x; + y -= origin.y; + style.left = origin.x + 'px'; + style.top = origin.y + 'px'; + } + style[transform] = [ + template[0], x, 'px,', y, 'px', template[1], + template[2], scale, ',', scale, template[3] + ].join(''); + } else { + style.left = x + 'px'; + style.top = y + 'px'; + // We previously might have had needTransform, so remove transform + if (transform !== null) { + style[transform] = ''; + } + } + }, CLASS_NAME: "OpenLayers.Map" }); diff --git a/tests/Map.html b/tests/Map.html index e57020b065..fb05de0ffd 100644 --- a/tests/Map.html +++ b/tests/Map.html @@ -2065,33 +2065,45 @@ } function test_applyTransform(t) { - t.plan(4); + t.plan(10); + var origStylePrefix = OpenLayers.Util.vendorPrefix.style; + OpenLayers.Util.vendorPrefix.style = function(key) { return 'transform'; }; var map = new OpenLayers.Map('map'); map.layerContainerDiv = {style: {}}; delete map.applyTransform.transform; delete map.applyTransform.template; - var origStylePrefix = OpenLayers.Util.vendorPrefix.style; - OpenLayers.Util.vendorPrefix.style = function(key) { return 'transform'; }; + var origGetStyle = OpenLayers.Element.getStyle; + OpenLayers.Element.getStyle = function() { return 'foo'; } map.applyTransform(1, 2, 3); - OpenLayers.Util.vendorPrefix.style = origStylePrefix; + OpenLayers.Element.getStyle = origGetStyle; t.eq(map.layerContainerDiv.style.transform, 'translate3d(1px,2px,0) scale3d(3,3,1)', '3d transform and scale used when available'); + delete map.applyTransform.transform; delete map.applyTransform.template; var origIndexOf = String.prototype.indexOf; String.prototype.indexOf = function() { return -1; }; + map.layerContainerOriginPx = {x: -3, y: 2}; map.applyTransform(1, 2, 3); String.prototype.indexOf = origIndexOf; - t.eq(map.layerContainerDiv.style.transform, 'translate(1px,2px) scale(3,3)', '2d translate and scale correct'); + t.eq(map.layerContainerDiv.style.transform, 'translate(4px,0px) scale(3,3)', '2d translate and scale correct'); + t.eq(map.layerContainerDiv.style.left, '-3px', 'container origin x set as style.left'); + t.eq(map.layerContainerDiv.style.top, '2px', 'container origin y set as style.top'); + map.applyTransform(1, 2); + t.ok(!map.layerContainerDiv.style.transform, 'no transform set when no transform needed'); + t.eq(map.layerContainerDiv.style.left, '1px', 'style.left correct when no transform needed'); + t.eq(map.layerContainerDiv.style.top, '2px', 'style.top correct when no transform needed'); map.applyTransform.transform = null; map.applyTransform(4, 5, 6); t.eq(map.layerContainerDiv.style.left, '4px', 'style.left set when transform not available') t.eq(map.layerContainerDiv.style.top, '5px', 'style.top set when transform not available') + t.ok(!map.layerContainerDiv.style.transform, 'no transform set, because not supported'); map.destroy(); delete map.applyTransform.transform; delete map.applyTransform.template; + OpenLayers.Util.vendorPrefix.style = origStylePrefix; } function test_options(t) {