Move gradient and pattern fills with the view

This commit is contained in:
Andreas Hocevar
2016-10-13 09:27:55 +02:00
parent 043a8ab1db
commit f655f6740e
4 changed files with 67 additions and 49 deletions

View File

@@ -1,5 +1,10 @@
## Upgrade notes ## Upgrade notes
#### `ol.style.Fill` with `CanvasGradient` or `CanvasPattern`
Previously, gradients and patterns were aligned with the canvas, so they did not
move and rotate with the map. This was changed to a more expected behavior by anchoring the fill to the map origin (usually at map coordinate `[0, 0]`).
#### `goog.DEBUG` define was renamed to `ol.DEBUG` #### `goog.DEBUG` define was renamed to `ol.DEBUG`
As last step in the removal of the dependency on Google Closure Library, the `goog.DEBUG` compiler define was renamed to `ol.DEBUG`. Please change accordingly in your custom build configuration json files. As last step in the removal of the dependency on Google Closure Library, the `goog.DEBUG` compiler define was renamed to `ol.DEBUG`. Please change accordingly in your custom build configuration json files.

View File

@@ -1,6 +1,7 @@
goog.require('ol.Map'); goog.require('ol.Map');
goog.require('ol.View'); goog.require('ol.View');
goog.require('ol.format.GeoJSON'); goog.require('ol.format.GeoJSON');
goog.require('ol.has');
goog.require('ol.layer.Vector'); goog.require('ol.layer.Vector');
goog.require('ol.proj'); goog.require('ol.proj');
goog.require('ol.source.Vector'); goog.require('ol.source.Vector');
@@ -8,14 +9,22 @@ goog.require('ol.style.Fill');
goog.require('ol.style.Stroke'); goog.require('ol.style.Stroke');
goog.require('ol.style.Style'); goog.require('ol.style.Style');
// Will contain ol.Style instances key by country. var canvas = document.createElement('canvas');
var styleLookup = {}; var context = canvas.getContext('2d');
// Gradient and pattern are in canvas pixel space, so we adjust for the
// renderer's pixel ratio
var pixelRatio = ol.has.DEVICE_PIXEL_RATIO;
// Generate a rainbow gradient // Generate a rainbow gradient
var gradient = (function() { function gradient(feature, resolution) {
var canvas = document.createElement('canvas'); var extent = feature.getGeometry().getExtent();
var context = canvas.getContext('2d'); // Gradient starts on the left edge of each feature, and ends on the right.
var grad = context.createLinearGradient(0,0,1000,0); // Coordinate origin is [0, 0], so we just divide by resolution and multiply
// with pixelRatio to match the renderer's pixel coordinate system.
var grad = context.createLinearGradient(
extent[0] / resolution * pixelRatio, 0,
extent[2] / resolution * pixelRatio, 0);
grad.addColorStop(0, 'red'); grad.addColorStop(0, 'red');
grad.addColorStop(1 / 6, 'orange'); grad.addColorStop(1 / 6, 'orange');
grad.addColorStop(2 / 6, 'yellow'); grad.addColorStop(2 / 6, 'yellow');
@@ -24,33 +33,35 @@ var gradient = (function() {
grad.addColorStop(5 / 6, 'blue'); grad.addColorStop(5 / 6, 'blue');
grad.addColorStop(1, 'purple'); grad.addColorStop(1, 'purple');
return grad; return grad;
}()); }
// Generate a canvasPattern with two circles // Generate a canvasPattern with two circles on white background
var pattern = (function() { var pattern = (function() {
var canvas = document.createElement('canvas'); canvas.width = 11 * pixelRatio;
var context = canvas.getContext('2d'); canvas.height = 11 * pixelRatio;
canvas.width = 11; // white background
canvas.height = 11; context.fillStyle = 'white';
context.fillRect(0, 0, canvas.width, canvas.height);
// outer circle
context.fillStyle = 'rgba(102, 0, 102, 0.5)'; context.fillStyle = 'rgba(102, 0, 102, 0.5)';
context.beginPath(); context.beginPath();
context.arc(5, 5, 4, 0, 2 * Math.PI); context.arc(5 * pixelRatio, 5 * pixelRatio, 4 * pixelRatio, 0, 2 * Math.PI);
context.fill(); context.fill();
// inner circle
context.fillStyle = 'rgb(55, 0, 170)'; context.fillStyle = 'rgb(55, 0, 170)';
context.beginPath(); context.beginPath();
context.arc(5, 5, 2, 0, 2 * Math.PI); context.arc(5 * pixelRatio, 5 * pixelRatio, 2 * pixelRatio, 0, 2 * Math.PI);
context.fill(); context.fill();
return context.createPattern(canvas, 'repeat'); return context.createPattern(canvas, 'repeat');
}()); }());
// Generate a background style that all features will reuse // Generate style for gradient or pattern fill
var backgroundStyle = new ol.style.Style({ var fill = new ol.style.Fill();
var style = new ol.style.Style({
fill: fill,
stroke: new ol.style.Stroke({ stroke: new ol.style.Stroke({
color: '#333', color: '#333',
width: 2 width: 2
}),
fill: new ol.style.Fill({
color: '#fff'
}) })
}); });
@@ -58,33 +69,14 @@ var backgroundStyle = new ol.style.Style({
* The styling function for the vector layer, will return an array of styles * The styling function for the vector layer, will return an array of styles
* which either contains the aboove gradient or pattern. * which either contains the aboove gradient or pattern.
* *
* @param {ol.Feature} feature the feature to style. * @param {ol.Feature} feature The feature to style.
* @return {Array<ol.style.Style>} the styles to use for the feature. * @param {number} resolution Resolution.
* @return {ol.style.Style} The style to use for the feature.
*/ */
var getStackedStyle = function(feature) { var getStackedStyle = function(feature, resolution) {
var id = feature.getId(); var id = feature.getId();
if (!styleLookup[id]) { fill.setColor(id > 'J' ? gradient(feature, resolution) : pattern);
var patternOrGradient; return style;
if (id > 'J') { // some shall get the gradient, others the pattern.
patternOrGradient = gradient;
} else {
patternOrGradient = pattern;
}
// Store the style in the lookup, next call will just return the stored
// style for the feature.
styleLookup[id] = [
// 1. Use the common background style
// (white fill and blackish stroke)
backgroundStyle,
// 2. On top of that, draw the pattern or gradient
new ol.style.Style({
fill: new ol.style.Fill({
color: patternOrGradient
})
})
];
}
return styleLookup[id];
}; };
// Create a vector layer that makes use of the style function above… // Create a vector layer that makes use of the style function above…

View File

@@ -344,7 +344,7 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() {
var miterLimit = state.miterLimit; var miterLimit = state.miterLimit;
if (fillStyle !== undefined && state.currentFillStyle != fillStyle) { if (fillStyle !== undefined && state.currentFillStyle != fillStyle) {
this.instructions.push( this.instructions.push(
[ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle]); [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle, typeof fillStyle != 'string']);
state.currentFillStyle = state.fillStyle; state.currentFillStyle = state.fillStyle;
} }
if (strokeStyle !== undefined) { if (strokeStyle !== undefined) {

View File

@@ -58,6 +58,12 @@ ol.render.canvas.Replay = function(tolerance, maxExtent, resolution, overlaps) {
*/ */
this.resolution = resolution; this.resolution = resolution;
/**
* @private
* @type {boolean}
*/
this.alignFill_ = false;
/** /**
* @private * @private
* @type {Array.<*>} * @type {Array.<*>}
@@ -185,6 +191,19 @@ ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) {
}; };
ol.render.canvas.Replay.prototype.fill_ = function(context, transform, rotation) {
if (this.alignFill_) {
context.translate(transform[4], transform[5]);
context.rotate(rotation);
}
context.fill();
if (this.alignFill_) {
context.rotate(-rotation);
context.translate(-transform[4], -transform[5]);
}
};
/** /**
* @private * @private
* @param {CanvasRenderingContext2D} context Context. * @param {CanvasRenderingContext2D} context Context.
@@ -250,7 +269,7 @@ ol.render.canvas.Replay.prototype.replay_ = function(
break; break;
case ol.render.canvas.Instruction.BEGIN_PATH: case ol.render.canvas.Instruction.BEGIN_PATH:
if (pendingFill > batchSize) { if (pendingFill > batchSize) {
context.fill(); this.fill_(context, transform, viewRotation);
pendingFill = 0; pendingFill = 0;
} }
if (pendingStroke > batchSize) { if (pendingStroke > batchSize) {
@@ -429,7 +448,7 @@ ol.render.canvas.Replay.prototype.replay_ = function(
if (batchSize) { if (batchSize) {
pendingFill++; pendingFill++;
} else { } else {
context.fill(); this.fill_(context, transform, viewRotation);
} }
++i; ++i;
break; break;
@@ -467,8 +486,10 @@ ol.render.canvas.Replay.prototype.replay_ = function(
ol.colorlike.isColorLike(instruction[1]), ol.colorlike.isColorLike(instruction[1]),
'2nd instruction should be a string, ' + '2nd instruction should be a string, ' +
'CanvasPattern, or CanvasGradient'); 'CanvasPattern, or CanvasGradient');
this.alignFill_ = instruction[2];
if (pendingFill) { if (pendingFill) {
context.fill(); this.fill_(context, transform, viewRotation);
pendingFill = 0; pendingFill = 0;
} }
@@ -534,7 +555,7 @@ ol.render.canvas.Replay.prototype.replay_ = function(
} }
} }
if (pendingFill) { if (pendingFill) {
context.fill(); this.fill_(context, transform, viewRotation);
} }
if (pendingStroke) { if (pendingStroke) {
context.stroke(); context.stroke();