/** * @module ol/render/canvas/ZIndexContext */ import {getSharedCanvasContext2D} from '../../dom.js'; /** @typedef {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D & {globalAlpha: any}} ZIndexContextProxy */ /** * @extends {CanvasRenderingContext2D} */ class ZIndexContext { constructor() { /** * @private * @type {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|OffscreenCanvasRenderingContext2D} 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;