From d6c7c7d7caf22cdbf3f6f0ffee5b4409dc6ef76f Mon Sep 17 00:00:00 2001 From: Jonathan Eiten Date: Sat, 23 Nov 2019 13:59:25 -0500 Subject: [PATCH 1/4] removed blitting support (properties.useBitBlit, canvas.bc, canvas.buffer) --- src/defaults.js | 10 ---------- src/lib/Canvas.js | 51 +++++++++++------------------------------------ 2 files changed, 12 insertions(+), 49 deletions(-) diff --git a/src/defaults.js b/src/defaults.js index b222862c6..7bd84a772 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -816,16 +816,6 @@ var defaults = { */ repaintImmediately: false, - //enable or disable double buffering - - /** - * @default - * @type {boolean} - * @memberOf module:defaults - */ - useBitBlit: false, - - /** * @default * @type {boolean} diff --git a/src/lib/Canvas.js b/src/lib/Canvas.js index 265461836..450dfdc9f 100644 --- a/src/lib/Canvas.js +++ b/src/lib/Canvas.js @@ -38,7 +38,6 @@ function Canvas(div, component, contextAttributes) { // create and append the canvas this.gc = getCachedContext(this.canvas = document.createElement('canvas'), contextAttributes); - this.bc = getCachedContext(this.buffer = document.createElement('canvas'), contextAttributes); this.div.appendChild(this.canvas); @@ -260,33 +259,17 @@ Canvas.prototype = { this.width = box.width; this.height = box.height; - //fix ala sir spinka, see - //http://www.html5rocks.com/en/tutorials/canvas/hidpi/ - //just add 'hdpi' as an attribute to the fin-canvas tag - var ratio = 1; + // http://www.html5rocks.com/en/tutorials/canvas/hidpi/ var isHIDPI = window.devicePixelRatio && this.component.properties.useHiDPI; - if (isHIDPI) { - var devicePixelRatio = window.devicePixelRatio || 1; - var backingStoreRatio = this.gc.webkitBackingStorePixelRatio || - this.gc.mozBackingStorePixelRatio || - this.gc.msBackingStorePixelRatio || - this.gc.oBackingStorePixelRatio || - this.gc.backingStorePixelRatio || 1; - - ratio = devicePixelRatio / backingStoreRatio; - //this.canvasCTX.scale(ratio, ratio); - } + var ratio = isHIDPI && window.devicePixelRatio || 1; - this.buffer.width = this.canvas.width = this.width * ratio; - this.buffer.height = this.canvas.height = this.height * ratio; + this.canvas.width = this.width * ratio; + this.canvas.height = this.height * ratio; - this.canvas.style.width = this.buffer.style.width = this.width + 'px'; - this.canvas.style.height = this.buffer.style.height = this.height + 'px'; + this.canvas.style.width = this.width + 'px'; + this.canvas.style.height = this.height + 'px'; - this.bc.scale(ratio, ratio); - if (isHIDPI && !this.component.properties.useBitBlit) { - this.gc.scale(ratio, ratio); - } + this.gc.scale(ratio, ratio); this.bounds = new rectangular.Rectangle(0, 0, this.width, this.height); this.component.setBounds(this.bounds); @@ -306,29 +289,19 @@ Canvas.prototype = { }, paintNow: function() { - var useBitBlit = this.component.properties.useBitBlit, - gc = useBitBlit ? this.bc : this.gc; - try { - gc.cache.save(); + this.gc.cache.save(); this.dirty = false; - this.component.paint(gc); + this.component.paint(this.gc); } catch (e) { console.error(e); } finally { - gc.cache.restore(); - } - - if (useBitBlit) { - this.flushBuffer(); + this.gc.cache.restore(); } }, - flushBuffer: function() { - if (this.buffer.width > 0 && this.buffer.height > 0) { - this.gc.drawImage(this.buffer, 0, 0); - } - }, + // flushBuffer deprecated in 3.3.0 + flushBuffer: function() {}, newEvent: function(primitiveEvent, name, detail) { var event = { From bef2a5cd9305a3e30922eebc0affce2c741b86a3 Mon Sep 17 00:00:00 2001 From: Jonathan Eiten Date: Mon, 25 Nov 2019 13:10:39 -0500 Subject: [PATCH 2/4] respect document.body.style.zoom --- src/Hypergrid/index.js | 49 +++++++++++++++------ src/features/ColumnMoving.js | 51 +++++++++++++--------- src/lib/Canvas.js | 82 +++++++++++++++++++++++++++++++----- 3 files changed, 139 insertions(+), 43 deletions(-) diff --git a/src/Hypergrid/index.js b/src/Hypergrid/index.js index 7ec2f15aa..5db1e9072 100644 --- a/src/Hypergrid/index.js +++ b/src/Hypergrid/index.js @@ -238,6 +238,15 @@ var Hypergrid = Base.extend('Hypergrid', { */ isWebkit: true, + /** + * We still support IE 11; we do NOT support older versions of IE. + * (We do NOT officially support Edge.) + * @see https://stackoverflow.com/questions/21825157/internet-explorer-11-detection#answer-21825207 + * @type {boolean} + * @memberOf Hypergrid# + */ + isIE11: !!(window.MSInputMethodContext && document.documentMode), + /** * The pixel location of an initial mousedown click, either for editing a cell or for dragging a selection. * @type {Point} @@ -1482,19 +1491,8 @@ var Hypergrid = Base.extend('Hypergrid', { * @memberOf Hypergrid# * @returns {number} The HiDPI ratio. */ - getHiDPI: function(ctx) { - if (window.devicePixelRatio && this.properties.useHiDPI) { - var devicePixelRatio = window.devicePixelRatio || 1, - backingStoreRatio = ctx.webkitBackingStorePixelRatio || - ctx.mozBackingStorePixelRatio || - ctx.msBackingStorePixelRatio || - ctx.oBackingStorePixelRatio || - ctx.backingStorePixelRatio || 1, - result = devicePixelRatio / backingStoreRatio; - } else { - result = 1; - } - return result; + getHiDPI: function() { + return this.canvas.devicePixelRatio; }, /** @@ -1570,6 +1568,31 @@ var Hypergrid = Base.extend('Hypergrid', { this.computeCellsBounds(); }, + /** + * @memberOf Hypergrid# + * @desc Reset zoom factor used by mouse tracking and placement + * of cell editors on top of canvas. + * + * Call this after resetting `document.body.style.zoom`. + * (Do not set `zoom` style on canvas or any other ancestor thereof.) + * + * **NOTE THE FOLLOWING:** + * 1. `zoom` is non-standard (unsupported by FireFox) + * 2. The alternative suggested on MDN, `transform`, is ignored + * here as it is not a practical replacement for `zoom`. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/zoom + * + * @todo Scrollbars need to be repositioned when `canvas.style.zoom` !== 1. (May need update to finbars.) + */ + resetZoom: function() { + this.abortEditing(); + this.canvas.resetZoom(); + }, + + getBodyZoomFactor: function() { + return this.canvas.bodyZoomFactor; + }, + /** * @memberOf Hypergrid# * @desc Enable/disable if this component can receive the focus. diff --git a/src/features/ColumnMoving.js b/src/features/ColumnMoving.js index e271409c2..f9965c796 100644 --- a/src/features/ColumnMoving.js +++ b/src/features/ColumnMoving.js @@ -18,6 +18,15 @@ var draggerCTX; var floatColumn; var floatColumnCTX; +function translate(grid, x, y) { + if (grid.isIE11) { + var zoomFactor = grid.getBodyZoomFactor(); + x *= zoomFactor; + y *= zoomFactor; + } + return 'translate(' + x + 'px, ' + y + 'px)'; +} + /** * @constructor * @extends Feature @@ -297,14 +306,14 @@ var ColumnMoving = Feature.extend('ColumnMoving', { return function() { var d = floatColumn; d.style.display = 'inline'; - self.setCrossBrowserProperty(d, 'transform', 'translate(' + floaterStartX + 'px, ' + 0 + 'px)'); + self.setCrossBrowserProperty(d, 'transform', translate(grid, floaterStartX, 0)); //d.style.webkit-webkit-Transform = 'translate(' + floaterStartX + 'px, ' + 0 + 'px)'; //d.style.webkit-webkit-Transform = 'translate(' + floaterStartX + 'px, ' + 0 + 'px)'; requestAnimationFrame(function() { self.setCrossBrowserProperty(d, 'transition', (self.isWebkit ? '-webkit-' : '') + 'transform ' + columnAnimationTime + 'ms ease'); - self.setCrossBrowserProperty(d, 'transform', 'translate(' + draggerStartX + 'px, ' + -2 + 'px)'); + self.setCrossBrowserProperty(d, 'transform', translate(grid, draggerStartX, -2)); }); grid.repaint(); //need to change this to key frames @@ -353,7 +362,7 @@ var ColumnMoving = Feature.extend('ColumnMoving', { } var columnWidth = grid.getColumnWidth(columnIndex); - var colHeight = grid.div.clientHeight; + var colHeight = grid.canvas.canvas.clientHeight; var d = floatColumn; var style = d.style; var location = grid.div.getBoundingClientRect(); @@ -361,13 +370,16 @@ var ColumnMoving = Feature.extend('ColumnMoving', { style.top = (location.top - 2) + 'px'; style.left = location.left + 'px'; - var hdpiRatio = grid.getHiDPI(floatColumnCTX); + var hdpiRatio = grid.getHiDPI(); - d.setAttribute('width', Math.round(columnWidth * hdpiRatio) + 'px'); - d.setAttribute('height', Math.round(colHeight * hdpiRatio) + 'px'); + d.setAttribute('width', Math.round(columnWidth * hdpiRatio)); + d.setAttribute('height', Math.round(colHeight * hdpiRatio)); style.boxShadow = '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)'; - style.width = columnWidth + 'px'; //Math.round(columnWidth / hdpiRatio) + 'px'; - style.height = colHeight + 'px'; //Math.round(colHeight / hdpiRatio) + 'px'; + + var zoomFactor = grid.isIE11 ? grid.getBodyZoomFactor() : 1; + style.width = columnWidth * zoomFactor + 'px'; //Math.round(columnWidth / hdpiRatio) + 'px'; + style.height = colHeight * zoomFactor + 'px'; //Math.round(colHeight / hdpiRatio) + 'px'; + style.borderTop = '1px solid ' + grid.properties.lineColor; style.backgroundColor = grid.properties.backgroundColor; @@ -385,7 +397,7 @@ var ColumnMoving = Feature.extend('ColumnMoving', { }; style.zIndex = '4'; - this.setCrossBrowserProperty(d, 'transform', 'translate(' + startX + 'px, ' + -2 + 'px)'); + this.setCrossBrowserProperty(d, 'transform', translate(grid, startX, -2)); GRABBING.forEach(setName, style); grid.repaint(); }, @@ -435,9 +447,9 @@ var ColumnMoving = Feature.extend('ColumnMoving', { scrollLeft = 0; } - var hdpiRatio = grid.getHiDPI(draggerCTX); + var hdpiRatio = grid.getHiDPI(); var columnWidth = grid.getColumnWidth(columnIndex); - var colHeight = grid.div.clientHeight; + var colHeight = grid.canvas.canvas.clientHeight; var d = dragger; var location = grid.div.getBoundingClientRect(); var style = d.style; @@ -450,11 +462,12 @@ var ColumnMoving = Feature.extend('ColumnMoving', { style.borderTop = '1px solid ' + grid.properties.lineColor; style.backgroundColor = grid.properties.backgroundColor; - d.setAttribute('width', Math.round(columnWidth * hdpiRatio) + 'px'); - d.setAttribute('height', Math.round(colHeight * hdpiRatio) + 'px'); + d.setAttribute('width', Math.round(columnWidth * hdpiRatio)); + d.setAttribute('height', Math.round(colHeight * hdpiRatio)); - style.width = columnWidth + 'px'; //Math.round(columnWidth / hdpiRatio) + 'px'; - style.height = colHeight + 'px'; //Math.round(colHeight / hdpiRatio) + 'px'; + var zoomFactor = grid.isIE11 ? grid.getBodyZoomFactor() : 1; + style.width = columnWidth * zoomFactor + 'px'; //Math.round(columnWidth / hdpiRatio) + 'px'; + style.height = colHeight * zoomFactor + 'px'; //Math.round(colHeight / hdpiRatio) + 'px'; var startX = grid.renderer.visibleColumns[columnIndex - scrollLeft].left * hdpiRatio; @@ -470,7 +483,7 @@ var ColumnMoving = Feature.extend('ColumnMoving', { hdpiratio: hdpiRatio }; - this.setCrossBrowserProperty(d, 'transform', 'translate(' + x + 'px, -5px)'); + this.setCrossBrowserProperty(d, 'transform', translate(grid, x, -5)); style.zIndex = '5'; GRABBING.forEach(setName, style); grid.repaint(); @@ -489,7 +502,7 @@ var ColumnMoving = Feature.extend('ColumnMoving', { var autoScrollingNow = this.columnDragAutoScrollingRight || this.columnDragAutoScrollingLeft; - var hdpiRatio = grid.getHiDPI(draggerCTX); + var hdpiRatio = grid.getHiDPI(); var dragColumnIndex = grid.renderOverridesCache.dragger.columnIndex; @@ -508,7 +521,7 @@ var ColumnMoving = Feature.extend('ColumnMoving', { this.setCrossBrowserProperty(d, 'transition', (self.isWebkit ? '-webkit-' : '') + 'transform ' + 0 + 'ms ease, box-shadow ' + columnAnimationTime + 'ms ease'); - this.setCrossBrowserProperty(d, 'transform', 'translate(' + x + 'px, ' + -10 + 'px)'); + this.setCrossBrowserProperty(d, 'transform', translate(grid, x, -10)); requestAnimationFrame(function() { d.style.display = 'inline'; }); @@ -639,7 +652,7 @@ var ColumnMoving = Feature.extend('ColumnMoving', { var d = dragger; var changed = grid.renderOverridesCache.dragger.startIndex !== grid.renderOverridesCache.dragger.columnIndex; self.setCrossBrowserProperty(d, 'transition', (self.isWebkit ? '-webkit-' : '') + 'transform ' + columnAnimationTime + 'ms ease, box-shadow ' + columnAnimationTime + 'ms ease'); - self.setCrossBrowserProperty(d, 'transform', 'translate(' + startX + 'px, ' + -1 + 'px)'); + self.setCrossBrowserProperty(d, 'transform', translate(grid, startX, -1)); d.style.boxShadow = '0px 0px 0px #888888'; setTimeout(function() { diff --git a/src/lib/Canvas.js b/src/lib/Canvas.js index 450dfdc9f..11b0ac92f 100644 --- a/src/lib/Canvas.js +++ b/src/lib/Canvas.js @@ -22,6 +22,10 @@ var RESIZE_POLLING_INTERVAL = 200, resizeInterval, charMap = makeCharMap(); +// We still support IE 11; we do NOT support older versions of IE (and we do NOT officially support Edge) +// https://stackoverflow.com/questions/21825157/internet-explorer-11-detection#answer-21825207 +var isIE11 = !!(window.MSInputMethodContext && document.documentMode); + function Canvas(div, component, contextAttributes) { var self = this; @@ -109,6 +113,8 @@ function Canvas(div, component, contextAttributes) { this.canvas.setAttribute('tabindex', 0); + this.resetZoom(); + this.resize(); this.beginResizing(); @@ -225,9 +231,34 @@ Canvas.prototype = { this.stopResizing(); }, + getBoundingClientRect: function(el) { + var rect = el.getBoundingClientRect(); + + if (isIE11) { + var r = 1 / this.bodyZoomFactor; + var top = rect.top * r; + var right = rect.right * r; + var bottom = rect.bottom * r; + var left = rect.left * r; + + rect = { + top: top, + right: right, + bottom: bottom, + left: left, + width: right - left, + height: bottom - top, + x: left, + y: top + }; + } + + return rect; + }, + getDivBoundingClientRect: function() { // Make sure our canvas has integral dimensions - var rect = this.div.getBoundingClientRect(); + var rect = this.getBoundingClientRect(this.div); var top = Math.floor(rect.top), left = Math.floor(rect.left), width = Math.ceil(rect.width), @@ -246,15 +277,14 @@ Canvas.prototype = { }, checksize: function() { - //this is expensive lets do it at some modulo var sizeNow = this.getDivBoundingClientRect(); if (sizeNow.width !== this.size.width || sizeNow.height !== this.size.height) { - this.resize(); + this.resize(sizeNow); } }, - resize: function() { - var box = this.size = this.getDivBoundingClientRect(); + resize: function(box) { + box = this.size = box || this.getDivBoundingClientRect(); this.width = box.width; this.height = box.height; @@ -263,8 +293,10 @@ Canvas.prototype = { var isHIDPI = window.devicePixelRatio && this.component.properties.useHiDPI; var ratio = isHIDPI && window.devicePixelRatio || 1; - this.canvas.width = this.width * ratio; - this.canvas.height = this.height * ratio; + this.devicePixelRatio = ratio *= this.bodyZoomFactor; + + this.canvas.width = Math.round(this.width * ratio); + this.canvas.height = Math.round(this.height * ratio); this.canvas.style.width = this.width + 'px'; this.canvas.style.height = this.height + 'px'; @@ -284,6 +316,30 @@ Canvas.prototype = { }); }, + resetZoom: function() { + var factor = 1; + + // IE11 bug: must use getPropertyValue because zoom is omitted from returned object + var zoomProp = getComputedStyle(document.body).getPropertyValue('zoom'); + + if (zoomProp) { + // IE11: always returns percentage + percent sign (others return factor) + var m = zoomProp.match(/^(.+?)(%)?$/); + if (m) { + var zoom = Number(m[1]); + if (m[2]) { + zoom /= 100; + } + zoom = Number(zoom || 1); + factor *= zoom; + } + } + + this.bodyZoomFactor = factor; + + this.resize(); + }, + getBounds: function() { return this.bounds; }, @@ -345,7 +401,6 @@ Canvas.prototype = { this.dragstart = new rectangular.Point(this.mouseLocation.x, this.mouseLocation.y); } this.mouseLocation = this.getLocal(e); - //console.log(this.mouseLocation); if (this.isDragging()) { this.dispatchNewMouseKeysEvent(e, 'fin-canvas-drag', { dragstart: this.dragstart, @@ -553,14 +608,19 @@ Canvas.prototype = { }, getOrigin: function() { - var rect = this.canvas.getBoundingClientRect(); + var rect = this.getBoundingClientRect(this.canvas); var p = new rectangular.Point(rect.left, rect.top); return p; }, getLocal: function(e) { - var rect = this.canvas.getBoundingClientRect(); - var p = new rectangular.Point(e.clientX - rect.left, e.clientY - rect.top); + var rect = this.getBoundingClientRect(this.canvas); + + var p = new rectangular.Point( + e.clientX / this.bodyZoomFactor - rect.left, + e.clientY / this.bodyZoomFactor - rect.top + ); + return p; }, From 5d5ca8859bce228de3ec09e186beacea5ad1f0e8 Mon Sep 17 00:00:00 2001 From: Jonathan Eiten Date: Mon, 25 Nov 2019 13:56:30 -0500 Subject: [PATCH 3/4] bump version to v3.3.0 --- README.md | 27 ++++++++++++--------------- package.json | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index dc97cf708..22edeba08 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ It also highlights a DOM-based custom external editor triggered via hypergrid ev ## Table of Contents -* [Current Release](#current-release-302---25-September-2018) +* [Current Release](#current-release) * [Distribution](#distribution) * [Demos](#demos) * [Features](#features) @@ -15,28 +15,25 @@ It also highlights a DOM-based custom external editor triggered via hypergrid ev * [Roadmap](#roadmap) * [Contributing](#contributors) -### Current Release (3.2.1 - 2 May 2019) +### Current Release -> **CAUTION:** For those considering upgrading directly from v2, be advised Hypergrid v3 introduced a revised data model _with breaking changes._ The impact of these changes has been intentionally minimized and should not affect the vast majority of users. See the [v3.0.0 release notes](https://github.com/fin-hypergrid/core/releases/tag/v3.0.0) for more information. - -_For a complete list of changes, see the [release notes](https://github.com/fin-hypergrid/core/releases)._ +**v3.3.0** +25 November 2019 ### Distribution #### npm module _(recommended)_ -Published as a CommonJS module to npmjs.org. -Specify a SEMVER of `"fin-hypergrid": "3.2.1"` (or `"^3.2.1"`) in your package.json file, +Published as a CommonJS module to [**npm**](http://npmjs.com/package/fin-hypergrid). +Specify a SEMVER of `"fin-hypergrid": "3.3.0"` (or `"^3.3.0"`) in your package.json file, issue the `npm install` command, and let your bundler (wepback, Browserify) create a single file containing both Hypergrid and your application. -#### Build files _(for small and informal examples and proofs-of-concept)_ -Also published as pre-bundled build files (`fin-hypergrid.js` and `fin-hypergrid.min.js`) to the GitHub CDN. -See the [CDN index](https://fin-hypergrid.github.io#index) for links. +#### Build files +For small and informal examples and proofs-of-concept, load a pre-bundled build file (`fin-hypergrid.js` or `fin-hypergrid.min.js`) from the GitHub CDN. See the [CDN index](https://fin-hypergrid.github.io#index) for links. + +Your application can load one of these pre-bundled build files (in a ``). ### Demos @@ -99,7 +96,7 @@ Hypergrid global configurations can be found [here](https://fin-hypergrid.github ### Roadmap -For our current queue of up coming work you can find it [here](ROADMAP.md) +For our current queue of upcoming work you can find it [here](ROADMAP.md) ### Contributors diff --git a/package.json b/package.json index ffcc23150..45cb3081e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fin-hypergrid", - "version": "3.2.2", + "version": "3.3.0", "description": "Canvas-based high-performance grid", "main": "src/Hypergrid", "repository": { From 1ee24aa3389b6b444c5e71e988ecac613a381f9c Mon Sep 17 00:00:00 2001 From: Jonathan Eiten Date: Mon, 25 Nov 2019 16:52:43 -0500 Subject: [PATCH 4/4] bump to 3.3.2 --- README.md | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 22edeba08..df5714682 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ It also highlights a DOM-based custom external editor triggered via hypergrid ev ### Current Release -**v3.3.0** +**v3.3.2** 25 November 2019 ### Distribution #### npm module _(recommended)_ Published as a CommonJS module to [**npm**](http://npmjs.com/package/fin-hypergrid). -Specify a SEMVER of `"fin-hypergrid": "3.3.0"` (or `"^3.3.0"`) in your package.json file, +Specify a SEMVER of `"fin-hypergrid": "3.3.2"` (or `"^3.3.2"`) in your package.json file, issue the `npm install` command, and let your bundler (wepback, Browserify) create a single file containing both Hypergrid and your application. @@ -33,7 +33,7 @@ For small and informal examples and proofs-of-concept, load a pre-bundled build Your application can load one of these pre-bundled build files (in a ``). +As of v3.2.1, the same build files are also available in a `umd` folder on npm for distribution via the [**unpkg**](https://unpkg.com/) CDN which processes SEMVER semantics when provided. For example, `` loads v3.3.2 which is the greatest (most recent) version number matching the SEMVER pattern `^3.2` (aka 3.*.*). ### Demos diff --git a/package.json b/package.json index 45cb3081e..d20f575c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fin-hypergrid", - "version": "3.3.0", + "version": "3.3.2", "description": "Canvas-based high-performance grid", "main": "src/Hypergrid", "repository": {