138 lines
3.5 KiB
JavaScript
138 lines
3.5 KiB
JavaScript
/**
|
|
* @module ol/render/canvas/ZIndexContext
|
|
*/
|
|
|
|
import {getSharedCanvasContext2D} from '../../dom.js';
|
|
|
|
/** @typedef {CanvasRenderingContext2D & {globalAlpha: any}} ZIndexContextProxy */
|
|
|
|
/**
|
|
* @extends {CanvasRenderingContext2D}
|
|
*/
|
|
class ZIndexContext {
|
|
constructor() {
|
|
/**
|
|
* @private
|
|
* @type {Array<Array<*>>}
|
|
*/
|
|
this.instructions_ = [];
|
|
/**
|
|
* @type {number}
|
|
*/
|
|
this.zIndex = 0;
|
|
/**
|
|
* @private
|
|
* @type {number}
|
|
*/
|
|
this.offset_ = 0;
|
|
|
|
/**
|
|
* @private
|
|
* @type {ZIndexContextProxy}
|
|
*/
|
|
this.context_ = /** @type {ZIndexContextProxy} */ (
|
|
new Proxy(getSharedCanvasContext2D(), {
|
|
get: (target, property) => {
|
|
if (
|
|
typeof (/** @type {*} */ (getSharedCanvasContext2D())[property]) !==
|
|
'function'
|
|
) {
|
|
// we only accept calling functions on the proxy, not accessing properties
|
|
return undefined;
|
|
}
|
|
this.push_(property);
|
|
return this.pushMethodArgs_;
|
|
},
|
|
set: (target, property, value) => {
|
|
this.push_(property, value);
|
|
return true;
|
|
},
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {...*} args Arguments to push to the instructions array.
|
|
* @private
|
|
*/
|
|
push_(...args) {
|
|
const instructions = this.instructions_;
|
|
const index = this.zIndex + this.offset_;
|
|
if (!instructions[index]) {
|
|
instructions[index] = [];
|
|
}
|
|
instructions[index].push(...args);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @param {...*} args Args.
|
|
* @return {ZIndexContext} This.
|
|
*/
|
|
pushMethodArgs_ = (...args) => {
|
|
this.push_(args);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Push a function that renders to the context directly.
|
|
* @param {function(CanvasRenderingContext2D): void} render Function.
|
|
*/
|
|
pushFunction(render) {
|
|
this.push_(render);
|
|
}
|
|
|
|
/**
|
|
* Get a proxy for CanvasRenderingContext2D which does not support getting state
|
|
* (e.g. `context.globalAlpha`, which will return `undefined`). To set state, if it relies on a
|
|
* previous state (e.g. `context.globalAlpha = context.globalAlpha / 2`), set a function,
|
|
* e.g. `context.globalAlpha = (context) => context.globalAlpha / 2`.
|
|
* @return {ZIndexContextProxy} Context.
|
|
*/
|
|
getContext() {
|
|
return this.context_;
|
|
}
|
|
|
|
/**
|
|
* @param {CanvasRenderingContext2D} context Context.
|
|
*/
|
|
draw(context) {
|
|
this.instructions_.forEach((instructionsAtIndex) => {
|
|
for (let i = 0, ii = instructionsAtIndex.length; i < ii; ++i) {
|
|
const property = instructionsAtIndex[i];
|
|
if (typeof property === 'function') {
|
|
property(context);
|
|
continue;
|
|
}
|
|
const instructionAtIndex = instructionsAtIndex[++i];
|
|
if (typeof (/** @type {*} */ (context)[property]) === 'function') {
|
|
/** @type {*} */ (context)[property](...instructionAtIndex);
|
|
} else {
|
|
if (typeof instructionAtIndex === 'function') {
|
|
/** @type {*} */ (context)[property] = instructionAtIndex(context);
|
|
continue;
|
|
}
|
|
/** @type {*} */ (context)[property] = instructionAtIndex;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
clear() {
|
|
this.instructions_.length = 0;
|
|
this.zIndex = 0;
|
|
this.offset_ = 0;
|
|
}
|
|
|
|
/**
|
|
* Offsets the zIndex by the highest current zIndex. Useful for rendering multiple worlds or tiles, to
|
|
* avoid conflicting context.clip() or context.save()/restore() calls.
|
|
*/
|
|
offset() {
|
|
this.offset_ = this.instructions_.length;
|
|
this.zIndex = 0;
|
|
}
|
|
}
|
|
|
|
export default ZIndexContext;
|