Merge pull request #10828 from ahocevar/offscreen-canvas
Offscreen canvas example
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
.ol-rotate {
|
||||||
|
left: .5em;
|
||||||
|
bottom: .5em;
|
||||||
|
top: unset;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
layout: example-verbatim.html
|
layout: example.html
|
||||||
title: Vector tiles created from a Mapbox Style object
|
title: Vector tiles created from a Mapbox Style object
|
||||||
shortdesc: Example of using ol-mapbox-style with tiles from maptiler.com.
|
shortdesc: Example of using ol-mapbox-style with tiles from maptiler.com.
|
||||||
docs: >
|
docs: >
|
||||||
@@ -10,27 +10,4 @@ cloak:
|
|||||||
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<div id="map" class="map"></div>
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
||||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
|
||||||
<title>Mapbox Style objects with ol-mapbox-style</title>
|
|
||||||
<link rel="stylesheet" href="../css/ol.css" type="text/css">
|
|
||||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,String.prototype.startsWith,Object.assign"></script>
|
|
||||||
<style type="text/css">
|
|
||||||
html, body, .map {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="map" class="map"></div>
|
|
||||||
<script src="common.js"></script>
|
|
||||||
<script src="mapbox-style.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
import apply from 'ol-mapbox-style';
|
import apply from 'ol-mapbox-style';
|
||||||
|
import FullScreen from '../src/ol/control/FullScreen.js';
|
||||||
|
|
||||||
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB');
|
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB').then(function(map) {
|
||||||
|
map.addControl(new FullScreen());
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.map {
|
||||||
|
background: rgba(232, 230, 223, 1);
|
||||||
|
}
|
||||||
|
.ol-rotate {
|
||||||
|
left: .5em;
|
||||||
|
bottom: .5em;
|
||||||
|
top: unset;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Vector tiles rendered in an offscreen canvas
|
||||||
|
shortdesc: Example of a map that delegates rendering to a worker.
|
||||||
|
docs: >
|
||||||
|
The map in this example is rendered in a web worker, using `OffscreenCanvas`. **Note:** This is currently only supported in Chrome and Edge.
|
||||||
|
tags: "worker, offscreencanvas, vector-tiles"
|
||||||
|
experimental: true
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import Layer from '../src/ol/layer/Layer.js';
|
||||||
|
import Worker from 'worker-loader!./offscreen-canvas.worker.js'; //eslint-disable-line
|
||||||
|
import {compose, create} from '../src/ol/transform.js';
|
||||||
|
import {createTransformString} from '../src/ol/render/canvas.js';
|
||||||
|
import {createXYZ} from '../src/ol/tilegrid.js';
|
||||||
|
import {FullScreen} from '../src/ol/control.js';
|
||||||
|
import stringify from 'json-stringify-safe';
|
||||||
|
import Source from '../src/ol/source/Source.js';
|
||||||
|
|
||||||
|
const worker = new Worker();
|
||||||
|
|
||||||
|
let container, transformContainer, canvas, rendering, workerFrameState, mainThreadFrameState;
|
||||||
|
|
||||||
|
// Transform the container to account for the differnece between the (newer)
|
||||||
|
// main thread frameState and the (older) worker frameState
|
||||||
|
function updateContainerTransform() {
|
||||||
|
if (workerFrameState) {
|
||||||
|
const viewState = mainThreadFrameState.viewState;
|
||||||
|
const renderedViewState = workerFrameState.viewState;
|
||||||
|
const center = viewState.center;
|
||||||
|
const resolution = viewState.resolution;
|
||||||
|
const rotation = viewState.rotation;
|
||||||
|
const renderedCenter = renderedViewState.center;
|
||||||
|
const renderedResolution = renderedViewState.resolution;
|
||||||
|
const renderedRotation = renderedViewState.rotation;
|
||||||
|
const transform = create();
|
||||||
|
// Skip the extra transform for rotated views, because it will not work
|
||||||
|
// correctly in that case
|
||||||
|
if (!rotation) {
|
||||||
|
compose(transform,
|
||||||
|
(renderedCenter[0] - center[0]) / resolution,
|
||||||
|
(center[1] - renderedCenter[1]) / resolution,
|
||||||
|
renderedResolution / resolution, renderedResolution / resolution,
|
||||||
|
rotation - renderedRotation,
|
||||||
|
0, 0);
|
||||||
|
}
|
||||||
|
transformContainer.style.transform = createTransformString(transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
layers: [
|
||||||
|
new Layer({
|
||||||
|
render: function(frameState) {
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.style.position = 'absolute';
|
||||||
|
container.style.width = '100%';
|
||||||
|
container.style.height = '100%';
|
||||||
|
transformContainer = document.createElement('div');
|
||||||
|
transformContainer.style.position = 'absolute';
|
||||||
|
transformContainer.style.width = '100%';
|
||||||
|
transformContainer.style.height = '100%';
|
||||||
|
container.appendChild(transformContainer);
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.transformOrigin = 'top left';
|
||||||
|
transformContainer.appendChild(canvas);
|
||||||
|
}
|
||||||
|
mainThreadFrameState = frameState;
|
||||||
|
updateContainerTransform();
|
||||||
|
if (!rendering) {
|
||||||
|
rendering = true;
|
||||||
|
worker.postMessage({
|
||||||
|
action: 'render',
|
||||||
|
frameState: JSON.parse(stringify(frameState))
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frameState.animate = true;
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
source: new Source({
|
||||||
|
attributions: [
|
||||||
|
'<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a>',
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
resolutions: createXYZ({tileSize: 512}).getResolutions89,
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2
|
||||||
|
})
|
||||||
|
});
|
||||||
|
map.addControl(new FullScreen());
|
||||||
|
|
||||||
|
// Worker messaging and actions
|
||||||
|
worker.addEventListener('message', message => {
|
||||||
|
if (message.data.action === 'loadImage') {
|
||||||
|
// Image loader for ol-mapbox-style
|
||||||
|
const image = new Image();
|
||||||
|
image.crossOrigin = 'anonymous';
|
||||||
|
image.addEventListener('load', function() {
|
||||||
|
createImageBitmap(image, 0, 0, image.width, image.height).then(imageBitmap => {
|
||||||
|
worker.postMessage({
|
||||||
|
action: 'imageLoaded',
|
||||||
|
image: imageBitmap,
|
||||||
|
src: message.data.src
|
||||||
|
}, [imageBitmap]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
image.src = event.data.src;
|
||||||
|
} else if (message.data.action === 'requestRender') {
|
||||||
|
// Worker requested a new render frame
|
||||||
|
map.render();
|
||||||
|
} else if (canvas && message.data.action === 'rendered') {
|
||||||
|
// Worker provies a new render frame
|
||||||
|
requestAnimationFrame(function() {
|
||||||
|
const imageData = message.data.imageData;
|
||||||
|
canvas.width = imageData.width;
|
||||||
|
canvas.height = imageData.height;
|
||||||
|
canvas.getContext('2d').drawImage(imageData, 0, 0);
|
||||||
|
canvas.style.transform = message.data.transform;
|
||||||
|
workerFrameState = message.data.frameState;
|
||||||
|
updateContainerTransform();
|
||||||
|
});
|
||||||
|
rendering = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import VectorTileLayer from '../src/ol/layer/VectorTile.js';
|
||||||
|
import VectorTileSource from '../src/ol/source/VectorTile.js';
|
||||||
|
import MVT from '../src/ol/format/MVT.js';
|
||||||
|
import {Projection} from '../src/ol/proj.js';
|
||||||
|
import TileQueue from '../src/ol/TileQueue.js';
|
||||||
|
import {getTilePriority as tilePriorityFunction} from '../src/ol/TileQueue.js';
|
||||||
|
import {renderDeclutterItems} from '../src/ol/render.js';
|
||||||
|
import styleFunction from 'ol-mapbox-style/dist/stylefunction.js';
|
||||||
|
import {inView} from '../src/ol/layer/Layer.js';
|
||||||
|
import stringify from 'json-stringify-safe';
|
||||||
|
|
||||||
|
/** @type {any} */
|
||||||
|
const worker = self;
|
||||||
|
|
||||||
|
let frameState, pixelRatio, rendererTransform;
|
||||||
|
const canvas = new OffscreenCanvas(1, 1);
|
||||||
|
// OffscreenCanvas does not have a style, so we mock it
|
||||||
|
canvas.style = {};
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
const sources = {
|
||||||
|
landcover: new VectorTileSource({
|
||||||
|
maxZoom: 9,
|
||||||
|
format: new MVT(),
|
||||||
|
url: 'https://api.maptiler.com/tiles/landcover/{z}/{x}/{y}.pbf?key=get_your_own_D6rA4zTHduk6KOKTXzGB'
|
||||||
|
}),
|
||||||
|
contours: new VectorTileSource({
|
||||||
|
minZoom: 9,
|
||||||
|
maxZoom: 14,
|
||||||
|
format: new MVT(),
|
||||||
|
url: 'https://api.maptiler.com/tiles/contours/{z}/{x}/{y}.pbf?key=get_your_own_D6rA4zTHduk6KOKTXzGB'
|
||||||
|
}),
|
||||||
|
openmaptiles: new VectorTileSource({
|
||||||
|
format: new MVT(),
|
||||||
|
maxZoom: 14,
|
||||||
|
url: 'https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key=get_your_own_D6rA4zTHduk6KOKTXzGB'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const layers = [];
|
||||||
|
|
||||||
|
// Font replacement so we do not need to load web fonts in the worker
|
||||||
|
function getFont(font) {
|
||||||
|
return font[0]
|
||||||
|
.replace('Noto Sans', 'serif')
|
||||||
|
.replace('Roboto', 'sans-serif');
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadStyles() {
|
||||||
|
const styleUrl = 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
|
||||||
|
fetch(styleUrl).then(data => data.json()).then(styleJson => {
|
||||||
|
const buckets = [];
|
||||||
|
let currentSource;
|
||||||
|
styleJson.layers.forEach(layer => {
|
||||||
|
if (!layer.source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentSource !== layer.source) {
|
||||||
|
currentSource = layer.source;
|
||||||
|
buckets.push({
|
||||||
|
source: layer.source,
|
||||||
|
layers: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
buckets[buckets.length - 1].layers.push(layer.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const spriteUrl = styleJson.sprite + (pixelRatio > 1 ? '@2x' : '') + '.json';
|
||||||
|
const spriteImageUrl = styleJson.sprite + (pixelRatio > 1 ? '@2x' : '') + '.png';
|
||||||
|
fetch(spriteUrl).then(data => data.json()).then(spriteJson => {
|
||||||
|
buckets.forEach(bucket => {
|
||||||
|
const source = sources[bucket.source];
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layer = new VectorTileLayer({
|
||||||
|
declutter: true,
|
||||||
|
source,
|
||||||
|
minZoom: source.getTileGrid().getMinZoom()
|
||||||
|
});
|
||||||
|
layer.getRenderer().useContainer = function(target, transform) {
|
||||||
|
this.containerReused = this.getLayer() !== layers[0];
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.context = context;
|
||||||
|
this.container = {
|
||||||
|
firstElementChild: canvas
|
||||||
|
};
|
||||||
|
rendererTransform = transform;
|
||||||
|
};
|
||||||
|
styleFunction(layer, styleJson, bucket.layers, undefined, spriteJson, spriteImageUrl, getFont);
|
||||||
|
layers.push(layer);
|
||||||
|
});
|
||||||
|
worker.postMessage({action: 'requestRender'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal map-like functionality for rendering
|
||||||
|
|
||||||
|
const tileQueue = new TileQueue(
|
||||||
|
(tile, tileSourceKey, tileCenter, tileResolution) => tilePriorityFunction(frameState, tile, tileSourceKey, tileCenter, tileResolution),
|
||||||
|
() => worker.postMessage({action: 'requestRender'}));
|
||||||
|
|
||||||
|
const maxTotalLoading = 8;
|
||||||
|
const maxNewLoads = 2;
|
||||||
|
|
||||||
|
worker.addEventListener('message', event => {
|
||||||
|
if (event.data.action !== 'render') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frameState = event.data.frameState;
|
||||||
|
if (!pixelRatio) {
|
||||||
|
pixelRatio = frameState.pixelRatio;
|
||||||
|
loadStyles();
|
||||||
|
}
|
||||||
|
frameState.tileQueue = tileQueue;
|
||||||
|
frameState.viewState.projection.__proto__ = Projection.prototype;
|
||||||
|
layers.forEach(layer => {
|
||||||
|
if (inView(layer.getLayerState(), frameState.viewState)) {
|
||||||
|
const renderer = layer.getRenderer();
|
||||||
|
renderer.renderFrame(frameState, canvas);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderDeclutterItems(frameState, null);
|
||||||
|
if (tileQueue.getTilesLoading() < maxTotalLoading) {
|
||||||
|
tileQueue.reprioritize();
|
||||||
|
tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
|
||||||
|
}
|
||||||
|
const imageData = canvas.transferToImageBitmap();
|
||||||
|
worker.postMessage({
|
||||||
|
action: 'rendered',
|
||||||
|
imageData: imageData,
|
||||||
|
transform: rendererTransform,
|
||||||
|
frameState: JSON.parse(stringify(frameState))
|
||||||
|
}, [imageData]);
|
||||||
|
});
|
||||||
@@ -57,6 +57,8 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const html = document.getElementById('example-html-source').innerText;
|
const html = document.getElementById('example-html-source').innerText;
|
||||||
const js = document.getElementById('example-js-source').innerText;
|
const js = document.getElementById('example-js-source').innerText;
|
||||||
|
const workerContainer = document.getElementById('example-worker-source');
|
||||||
|
const worker = workerContainer ? workerContainer.innerText : undefined;
|
||||||
const pkgJson = document.getElementById('example-pkg-source').innerText;
|
const pkgJson = document.getElementById('example-pkg-source').innerText;
|
||||||
const form = document.getElementById('codepen-form');
|
const form = document.getElementById('codepen-form');
|
||||||
|
|
||||||
@@ -68,22 +70,28 @@
|
|||||||
|
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
const data = {
|
const files = {
|
||||||
files: {
|
'index.html': {
|
||||||
'index.html': {
|
content: html
|
||||||
content: html
|
},
|
||||||
},
|
'index.js': {
|
||||||
'index.js': {
|
content: js
|
||||||
content: js
|
},
|
||||||
},
|
"package.json": {
|
||||||
"package.json": {
|
content: pkgJson
|
||||||
content: pkgJson
|
},
|
||||||
},
|
'sandbox.config.json': {
|
||||||
'sandbox.config.json': {
|
content: '{"template": "parcel"}'
|
||||||
content: '{"template": "parcel"}'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (worker) {
|
||||||
|
files['worker.js'] = {
|
||||||
|
content: worker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
files: files
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < localResources.length; i++) {
|
for (let i = 0; i < localResources.length; i++) {
|
||||||
data.files[localResources[i]] = results[i];
|
data.files[localResources[i]] = results[i];
|
||||||
|
|||||||
@@ -309,4 +309,4 @@ function createMapboxStreetsV6Style(Style, Fill, Stroke, Icon, Text) {
|
|||||||
styles.length = length;
|
styles.length = length;
|
||||||
return styles;
|
return styles;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -160,6 +160,14 @@
|
|||||||
<pre><legend>index.js</legend><code id="example-js-source" class="language-js">import 'ol/ol.css';
|
<pre><legend>index.js</legend><code id="example-js-source" class="language-js">import 'ol/ol.css';
|
||||||
{{ js.source }}</code></pre>
|
{{ js.source }}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
{{#if worker.source}}
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="source-controls">
|
||||||
|
<a class="copy-button" id="copy-worker-button" data-clipboard-target="#example-worker-source"><i class="fa fa-clipboard"></i> Copy</a>
|
||||||
|
</div>
|
||||||
|
<pre><legend>worker.js</legend><code id="example-worker-source" class="language-js">{{ worker.source }}</code></pre>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="source-controls">
|
<div class="source-controls">
|
||||||
<a class="copy-button" id="copy-pkg-button" data-clipboard-target="#example-pkg-source"><i class="fa fa-clipboard"></i> Copy</a>
|
<a class="copy-button" id="copy-pkg-button" data-clipboard-target="#example-pkg-source"><i class="fa fa-clipboard"></i> Copy</a>
|
||||||
@@ -167,7 +175,6 @@
|
|||||||
<pre><legend>package.json</legend><code id="example-pkg-source" class="language-js">{{ pkgJson }}</code></pre>
|
<pre><legend>package.json</legend><code id="example-pkg-source" class="language-js">{{ pkgJson }}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="./resources/common.js"></script>
|
<script src="./resources/common.js"></script>
|
||||||
<script src="./resources/prism/prism.min.js"></script>
|
<script src="./resources/prism/prism.min.js"></script>
|
||||||
{{{ js.tag }}}
|
{{{ js.tag }}}
|
||||||
|
|||||||
@@ -208,6 +208,10 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
|
|||||||
jsSource = jsSource.replace(new RegExp(entry.key, 'g'), entry.value);
|
jsSource = jsSource.replace(new RegExp(entry.key, 'g'), entry.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Remove worker loader import and modify `new Worker()` to add source
|
||||||
|
jsSource = jsSource.replace(/import Worker from 'worker-loader![^\n]*\n/g, '');
|
||||||
|
jsSource = jsSource.replace('new Worker()', 'new Worker(\'./worker.js\')');
|
||||||
|
|
||||||
data.js = {
|
data.js = {
|
||||||
tag: `<script src="${this.common}.js"></script><script src="${jsName}"></script>`,
|
tag: `<script src="${this.common}.js"></script><script src="${jsName}"></script>`,
|
||||||
source: jsSource
|
source: jsSource
|
||||||
@@ -218,9 +222,33 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
|
|||||||
data.js.tag = prelude + data.js.tag;
|
data.js.tag = prelude + data.js.tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for worker js
|
||||||
|
const workerName = `${name}.worker.js`;
|
||||||
|
const workerPath = path.join(dir, workerName);
|
||||||
|
let workerSource;
|
||||||
|
try {
|
||||||
|
workerSource = await readFile(workerPath, readOptions);
|
||||||
|
} catch (err) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
if (workerSource) {
|
||||||
|
// remove "../src/" prefix and ".js" to have the same import syntax as the documentation
|
||||||
|
workerSource = workerSource.replace(/'\.\.\/src\//g, '\'');
|
||||||
|
workerSource = workerSource.replace(/\.js';/g, '\';');
|
||||||
|
if (data.cloak) {
|
||||||
|
for (const entry of data.cloak) {
|
||||||
|
workerSource = workerSource.replace(new RegExp(entry.key, 'g'), entry.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.worker = {
|
||||||
|
source: workerSource
|
||||||
|
};
|
||||||
|
assets[workerName] = workerSource;
|
||||||
|
}
|
||||||
|
|
||||||
data.pkgJson = JSON.stringify({
|
data.pkgJson = JSON.stringify({
|
||||||
name: name,
|
name: name,
|
||||||
dependencies: getDependencies(jsSource),
|
dependencies: getDependencies(jsSource + workerSource ? `\n${workerSource}` : ''),
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
parcel: '1.11.0'
|
parcel: '1.11.0'
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+22
@@ -12693,6 +12693,28 @@
|
|||||||
"errno": "~0.1.7"
|
"errno": "~0.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"worker-loader": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"loader-utils": "^1.0.0",
|
||||||
|
"schema-utils": "^0.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"schema-utils": {
|
||||||
|
"version": "0.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
|
||||||
|
"integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ajv": "^6.1.0",
|
||||||
|
"ajv-keywords": "^3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||||
|
|||||||
+3
-1
@@ -73,6 +73,7 @@
|
|||||||
"jquery": "3.4.1",
|
"jquery": "3.4.1",
|
||||||
"jsdoc": "3.6.3",
|
"jsdoc": "3.6.3",
|
||||||
"jsdoc-plugin-typescript": "^2.0.5",
|
"jsdoc-plugin-typescript": "^2.0.5",
|
||||||
|
"json-stringify-safe": "^5.0.1",
|
||||||
"karma": "^4.4.1",
|
"karma": "^4.4.1",
|
||||||
"karma-chrome-launcher": "3.1.0",
|
"karma-chrome-launcher": "3.1.0",
|
||||||
"karma-coverage-istanbul-reporter": "^2.1.1",
|
"karma-coverage-istanbul-reporter": "^2.1.1",
|
||||||
@@ -83,7 +84,7 @@
|
|||||||
"loglevelnext": "^3.0.1",
|
"loglevelnext": "^3.0.1",
|
||||||
"marked": "0.8.2",
|
"marked": "0.8.2",
|
||||||
"mocha": "7.1.1",
|
"mocha": "7.1.1",
|
||||||
"ol-mapbox-style": "^6.0.0",
|
"ol-mapbox-style": "^6.1.0",
|
||||||
"pixelmatch": "^5.1.0",
|
"pixelmatch": "^5.1.0",
|
||||||
"pngjs": "^3.4.0",
|
"pngjs": "^3.4.0",
|
||||||
"proj4": "2.6.0",
|
"proj4": "2.6.0",
|
||||||
@@ -104,6 +105,7 @@
|
|||||||
"webpack-cli": "^3.3.2",
|
"webpack-cli": "^3.3.2",
|
||||||
"webpack-dev-middleware": "^3.6.2",
|
"webpack-dev-middleware": "^3.6.2",
|
||||||
"webpack-dev-server": "^3.3.1",
|
"webpack-dev-server": "^3.3.1",
|
||||||
|
"worker-loader": "^2.0.0",
|
||||||
"yargs": "^15.0.2"
|
"yargs": "^15.0.2"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
+2
-22
@@ -12,7 +12,7 @@ import MapProperty from './MapProperty.js';
|
|||||||
import RenderEventType from './render/EventType.js';
|
import RenderEventType from './render/EventType.js';
|
||||||
import BaseObject, {getChangeEventType} from './Object.js';
|
import BaseObject, {getChangeEventType} from './Object.js';
|
||||||
import ObjectEventType from './ObjectEventType.js';
|
import ObjectEventType from './ObjectEventType.js';
|
||||||
import TileQueue from './TileQueue.js';
|
import TileQueue, {getTilePriority} from './TileQueue.js';
|
||||||
import View from './View.js';
|
import View from './View.js';
|
||||||
import ViewHint from './ViewHint.js';
|
import ViewHint from './ViewHint.js';
|
||||||
import {assert} from './asserts.js';
|
import {assert} from './asserts.js';
|
||||||
@@ -24,7 +24,6 @@ import {TRUE} from './functions.js';
|
|||||||
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE, PASSIVE_EVENT_LISTENERS} from './has.js';
|
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE, PASSIVE_EVENT_LISTENERS} from './has.js';
|
||||||
import LayerGroup from './layer/Group.js';
|
import LayerGroup from './layer/Group.js';
|
||||||
import {hasArea} from './size.js';
|
import {hasArea} from './size.js';
|
||||||
import {DROP} from './structs/PriorityQueue.js';
|
|
||||||
import {create as createTransform, apply as applyTransform} from './transform.js';
|
import {create as createTransform, apply as applyTransform} from './transform.js';
|
||||||
import {toUserCoordinate, fromUserCoordinate} from './proj.js';
|
import {toUserCoordinate, fromUserCoordinate} from './proj.js';
|
||||||
|
|
||||||
@@ -891,26 +890,7 @@ class PluggableMap extends BaseObject {
|
|||||||
* @return {number} Tile priority.
|
* @return {number} Tile priority.
|
||||||
*/
|
*/
|
||||||
getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) {
|
getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) {
|
||||||
// Filter out tiles at higher zoom levels than the current zoom level, or that
|
return getTilePriority(this.frameState_, tile, tileSourceKey, tileCenter, tileResolution);
|
||||||
// are outside the visible extent.
|
|
||||||
const frameState = this.frameState_;
|
|
||||||
if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
|
|
||||||
return DROP;
|
|
||||||
}
|
|
||||||
if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
|
|
||||||
return DROP;
|
|
||||||
}
|
|
||||||
// Prioritize the highest zoom level tiles closest to the focus.
|
|
||||||
// Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
|
|
||||||
// Within a zoom level, tiles are prioritized by the distance in pixels between
|
|
||||||
// the center of the tile and the center of the viewport. The factor of 65536
|
|
||||||
// means that the prioritization should behave as desired for tiles up to
|
|
||||||
// 65536 * Math.log(2) = 45426 pixels from the focus.
|
|
||||||
const center = frameState.viewState.center;
|
|
||||||
const deltaX = tileCenter[0] - center[0];
|
|
||||||
const deltaY = tileCenter[1] - center[1];
|
|
||||||
return 65536 * Math.log(tileResolution) +
|
|
||||||
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+32
-1
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import TileState from './TileState.js';
|
import TileState from './TileState.js';
|
||||||
import EventType from './events/EventType.js';
|
import EventType from './events/EventType.js';
|
||||||
import PriorityQueue from './structs/PriorityQueue.js';
|
import PriorityQueue, {DROP} from './structs/PriorityQueue.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,3 +119,34 @@ class TileQueue extends PriorityQueue {
|
|||||||
|
|
||||||
|
|
||||||
export default TileQueue;
|
export default TileQueue;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./PluggableMap.js').FrameState} frameState Frame state.
|
||||||
|
* @param {import("./Tile.js").default} tile Tile.
|
||||||
|
* @param {string} tileSourceKey Tile source key.
|
||||||
|
* @param {import("./coordinate.js").Coordinate} tileCenter Tile center.
|
||||||
|
* @param {number} tileResolution Tile resolution.
|
||||||
|
* @return {number} Tile priority.
|
||||||
|
*/
|
||||||
|
export function getTilePriority(frameState, tile, tileSourceKey, tileCenter, tileResolution) {
|
||||||
|
// Filter out tiles at higher zoom levels than the current zoom level, or that
|
||||||
|
// are outside the visible extent.
|
||||||
|
if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
// Prioritize the highest zoom level tiles closest to the focus.
|
||||||
|
// Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
|
||||||
|
// Within a zoom level, tiles are prioritized by the distance in pixels between
|
||||||
|
// the center of the tile and the center of the viewport. The factor of 65536
|
||||||
|
// means that the prioritization should behave as desired for tiles up to
|
||||||
|
// 65536 * Math.log(2) = 45426 pixels from the focus.
|
||||||
|
const center = frameState.viewState.center;
|
||||||
|
const deltaX = tileCenter[0] - center[0];
|
||||||
|
const deltaY = tileCenter[1] - center[1];
|
||||||
|
return 65536 * Math.log(tileResolution) +
|
||||||
|
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
|
||||||
|
}
|
||||||
|
|||||||
+48
-34
@@ -4,9 +4,13 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} FontParameters
|
* @typedef {Object} FontParameters
|
||||||
* @property {Array<string>} families
|
|
||||||
* @property {string} style
|
* @property {string} style
|
||||||
|
* @property {string} variant
|
||||||
* @property {string} weight
|
* @property {string} weight
|
||||||
|
* @property {string} size
|
||||||
|
* @property {string} lineHeight
|
||||||
|
* @property {string} family
|
||||||
|
* @property {Array<string>} families
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -64,42 +68,52 @@ export const CLASS_CONTROL = 'ol-control';
|
|||||||
*/
|
*/
|
||||||
export const CLASS_COLLAPSED = 'ol-collapsed';
|
export const CLASS_COLLAPSED = 'ol-collapsed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From http://stackoverflow.com/questions/10135697/regex-to-parse-any-css-font
|
||||||
|
* @type {RegExp}
|
||||||
|
*/
|
||||||
|
const fontRegEx = new RegExp([
|
||||||
|
'^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)',
|
||||||
|
'(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)',
|
||||||
|
'(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)',
|
||||||
|
'(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?',
|
||||||
|
'(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))',
|
||||||
|
'(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))',
|
||||||
|
'?\\s*([-,\\"\\\'\\sa-z]+?)\\s*$'
|
||||||
|
].join(''), 'i');
|
||||||
|
const fontRegExMatchIndex = [
|
||||||
|
'style',
|
||||||
|
'variant',
|
||||||
|
'weight',
|
||||||
|
'size',
|
||||||
|
'lineHeight',
|
||||||
|
'family'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of font families from a font spec. Note that this doesn't work
|
* Get the list of font families from a font spec. Note that this doesn't work
|
||||||
* for font families that have commas in them.
|
* for font families that have commas in them.
|
||||||
* @param {string} The CSS font property.
|
* @param {string} fontSpec The CSS font property.
|
||||||
* @return {FontParameters} The font families (or null if the input spec is invalid).
|
* @return {FontParameters} The font parameters (or null if the input spec is invalid).
|
||||||
*/
|
*/
|
||||||
export const getFontParameters = (function() {
|
export const getFontParameters = function(fontSpec) {
|
||||||
/**
|
const match = fontSpec.match(fontRegEx);
|
||||||
* @type {CSSStyleDeclaration}
|
if (!match) {
|
||||||
*/
|
return null;
|
||||||
let style;
|
}
|
||||||
/**
|
const style = /** @type {FontParameters} */ ({
|
||||||
* @type {Object<string, FontParameters>}
|
lineHeight: 'normal',
|
||||||
*/
|
size: '1.2em',
|
||||||
const cache = {};
|
style: 'normal',
|
||||||
return function(font) {
|
weight: 'normal',
|
||||||
if (!style) {
|
variant: 'normal'
|
||||||
style = document.createElement('div').style;
|
});
|
||||||
|
for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) {
|
||||||
|
const value = match[i + 1];
|
||||||
|
if (value !== undefined) {
|
||||||
|
style[fontRegExMatchIndex[i]] = value;
|
||||||
}
|
}
|
||||||
if (!(font in cache)) {
|
}
|
||||||
style.font = font;
|
style.families = style.family.split(/,\s?/);
|
||||||
const family = style.fontFamily;
|
return style;
|
||||||
const fontWeight = style.fontWeight;
|
};
|
||||||
const fontStyle = style.fontStyle;
|
|
||||||
style.font = '';
|
|
||||||
if (!family) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const families = family.split(/,\s?/);
|
|
||||||
cache[font] = {
|
|
||||||
families: families,
|
|
||||||
weight: fontWeight,
|
|
||||||
style: fontStyle
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return cache[font];
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|||||||
+9
-2
@@ -1,8 +1,11 @@
|
|||||||
|
import {WORKER_OFFSCREEN_CANVAS} from './has.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module ol/dom
|
* @module ol/dom
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//FIXME Move this function to the canvas module
|
||||||
/**
|
/**
|
||||||
* Create an html canvas element and returns its 2d context.
|
* Create an html canvas element and returns its 2d context.
|
||||||
* @param {number=} opt_width Canvas width.
|
* @param {number=} opt_width Canvas width.
|
||||||
@@ -12,14 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
|
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
|
||||||
const canvas = opt_canvasPool && opt_canvasPool.length ?
|
const canvas = opt_canvasPool && opt_canvasPool.length ?
|
||||||
opt_canvasPool.shift() : document.createElement('canvas');
|
opt_canvasPool.shift() :
|
||||||
|
WORKER_OFFSCREEN_CANVAS ?
|
||||||
|
new OffscreenCanvas(opt_width || 300, opt_height || 300) :
|
||||||
|
document.createElement('canvas');
|
||||||
if (opt_width) {
|
if (opt_width) {
|
||||||
canvas.width = opt_width;
|
canvas.width = opt_width;
|
||||||
}
|
}
|
||||||
if (opt_height) {
|
if (opt_height) {
|
||||||
canvas.height = opt_height;
|
canvas.height = opt_height;
|
||||||
}
|
}
|
||||||
return canvas.getContext('2d');
|
//FIXME Allow OffscreenCanvasRenderingContext2D as return type
|
||||||
|
return /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -37,7 +37,15 @@ export const MAC = ua.indexOf('macintosh') !== -1;
|
|||||||
* @type {number}
|
* @type {number}
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
|
export const DEVICE_PIXEL_RATIO = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The execution context is a worker with OffscreenCanvas available.
|
||||||
|
* @const
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
export const WORKER_OFFSCREEN_CANVAS = typeof WorkerGlobalScope !== 'undefined' && typeof OffscreenCanvas !== 'undefined' &&
|
||||||
|
self instanceof WorkerGlobalScope; //eslint-disable-line
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image.prototype.decode() is supported.
|
* Image.prototype.decode() is supported.
|
||||||
|
|||||||
+57
-19
@@ -6,6 +6,8 @@ import {createCanvasContext2D} from '../dom.js';
|
|||||||
import {clear} from '../obj.js';
|
import {clear} from '../obj.js';
|
||||||
import BaseObject from '../Object.js';
|
import BaseObject from '../Object.js';
|
||||||
import EventTarget from '../events/Target.js';
|
import EventTarget from '../events/Target.js';
|
||||||
|
import {WORKER_OFFSCREEN_CANVAS} from '../has.js';
|
||||||
|
import {toString} from '../transform.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,34 +299,40 @@ export const measureTextHeight = (function() {
|
|||||||
*/
|
*/
|
||||||
let div;
|
let div;
|
||||||
const heights = textHeights;
|
const heights = textHeights;
|
||||||
return function(font) {
|
return function(fontSpec) {
|
||||||
let height = heights[font];
|
let height = heights[fontSpec];
|
||||||
if (height == undefined) {
|
if (height == undefined) {
|
||||||
if (!div) {
|
if (WORKER_OFFSCREEN_CANVAS) {
|
||||||
div = document.createElement('div');
|
const font = getFontParameters(fontSpec);
|
||||||
div.innerHTML = 'M';
|
const metrics = measureText(fontSpec, 'Žg');
|
||||||
div.style.margin = '0 !important';
|
const lineHeight = isNaN(Number(font.lineHeight)) ? 1.2 : Number(font.lineHeight);
|
||||||
div.style.padding = '0 !important';
|
textHeights[fontSpec] = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
|
||||||
div.style.position = 'absolute !important';
|
} else {
|
||||||
div.style.left = '-99999px !important';
|
if (!div) {
|
||||||
|
div = document.createElement('div');
|
||||||
|
div.innerHTML = 'M';
|
||||||
|
div.style.margin = '0 !important';
|
||||||
|
div.style.padding = '0 !important';
|
||||||
|
div.style.position = 'absolute !important';
|
||||||
|
div.style.left = '-99999px !important';
|
||||||
|
}
|
||||||
|
div.style.font = fontSpec;
|
||||||
|
document.body.appendChild(div);
|
||||||
|
height = div.offsetHeight;
|
||||||
|
heights[fontSpec] = height;
|
||||||
|
document.body.removeChild(div);
|
||||||
}
|
}
|
||||||
div.style.font = font;
|
|
||||||
document.body.appendChild(div);
|
|
||||||
height = div.offsetHeight;
|
|
||||||
heights[font] = height;
|
|
||||||
document.body.removeChild(div);
|
|
||||||
}
|
}
|
||||||
return height;
|
return height;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} font Font.
|
* @param {string} font Font.
|
||||||
* @param {string} text Text.
|
* @param {string} text Text.
|
||||||
* @return {number} Width.
|
* @return {TextMetrics} Text metrics.
|
||||||
*/
|
*/
|
||||||
export function measureTextWidth(font, text) {
|
function measureText(font, text) {
|
||||||
if (!measureContext) {
|
if (!measureContext) {
|
||||||
measureContext = createCanvasContext2D(1, 1);
|
measureContext = createCanvasContext2D(1, 1);
|
||||||
}
|
}
|
||||||
@@ -332,9 +340,17 @@ export function measureTextWidth(font, text) {
|
|||||||
measureContext.font = font;
|
measureContext.font = font;
|
||||||
measureFont = measureContext.font;
|
measureFont = measureContext.font;
|
||||||
}
|
}
|
||||||
return measureContext.measureText(text).width;
|
return measureContext.measureText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} font Font.
|
||||||
|
* @param {string} text Text.
|
||||||
|
* @return {number} Width.
|
||||||
|
*/
|
||||||
|
export function measureTextWidth(font, text) {
|
||||||
|
return measureText(font, text).width;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure text width using a cache.
|
* Measure text width using a cache.
|
||||||
@@ -432,9 +448,31 @@ function executeLabelInstructions(label, context) {
|
|||||||
const contextInstructions = label.contextInstructions;
|
const contextInstructions = label.contextInstructions;
|
||||||
for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) {
|
for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) {
|
||||||
if (Array.isArray(contextInstructions[i + 1])) {
|
if (Array.isArray(contextInstructions[i + 1])) {
|
||||||
CanvasRenderingContext2D.prototype[contextInstructions[i]].apply(context, contextInstructions[i + 1]);
|
context[contextInstructions[i]].apply(context, contextInstructions[i + 1]);
|
||||||
} else {
|
} else {
|
||||||
context[contextInstructions[i]] = contextInstructions[i + 1];
|
context[contextInstructions[i]] = contextInstructions[i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLCanvasElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
let createTransformStringCanvas = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../transform.js").Transform} transform Transform.
|
||||||
|
* @return {string} CSS transform.
|
||||||
|
*/
|
||||||
|
export function createTransformString(transform) {
|
||||||
|
if (WORKER_OFFSCREEN_CANVAS) {
|
||||||
|
return toString(transform);
|
||||||
|
} else {
|
||||||
|
if (!createTransformStringCanvas) {
|
||||||
|
createTransformStringCanvas = createCanvasContext2D(1, 1).canvas;
|
||||||
|
}
|
||||||
|
createTransformStringCanvas.style.transform = toString(transform);
|
||||||
|
return createTransformStringCanvas.style.transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
} from '../../transform.js';
|
} from '../../transform.js';
|
||||||
import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
|
import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
|
||||||
import RBush from 'rbush/rbush.js';
|
import RBush from 'rbush/rbush.js';
|
||||||
|
import {WORKER_OFFSCREEN_CANVAS} from '../../has.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -204,7 +205,9 @@ class Executor {
|
|||||||
contextInstructions.push('lineCap', strokeState.lineCap);
|
contextInstructions.push('lineCap', strokeState.lineCap);
|
||||||
contextInstructions.push('lineJoin', strokeState.lineJoin);
|
contextInstructions.push('lineJoin', strokeState.lineJoin);
|
||||||
contextInstructions.push('miterLimit', strokeState.miterLimit);
|
contextInstructions.push('miterLimit', strokeState.miterLimit);
|
||||||
if (CanvasRenderingContext2D.prototype.setLineDash) {
|
// eslint-disable-next-line
|
||||||
|
const Context = WORKER_OFFSCREEN_CANVAS ? OffscreenCanvasRenderingContext2D : CanvasRenderingContext2D;
|
||||||
|
if (Context.prototype.setLineDash) {
|
||||||
contextInstructions.push('setLineDash', [strokeState.lineDash]);
|
contextInstructions.push('setLineDash', [strokeState.lineDash]);
|
||||||
contextInstructions.push('lineDashOffset', strokeState.lineDashOffset);
|
contextInstructions.push('lineDashOffset', strokeState.lineDashOffset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {fromUserExtent} from '../../proj.js';
|
|||||||
import {getIntersection, isEmpty} from '../../extent.js';
|
import {getIntersection, isEmpty} from '../../extent.js';
|
||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {compose as composeTransform, makeInverse} from '../../transform.js';
|
import {compose as composeTransform, makeInverse} from '../../transform.js';
|
||||||
|
import {createTransformString} from '../../render/canvas.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -109,7 +110,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
|||||||
);
|
);
|
||||||
makeInverse(this.inversePixelTransform, this.pixelTransform);
|
makeInverse(this.inversePixelTransform, this.pixelTransform);
|
||||||
|
|
||||||
const canvasTransform = this.createTransformString(this.pixelTransform);
|
const canvasTransform = createTransformString(this.pixelTransform);
|
||||||
|
|
||||||
this.useContainer(target, canvasTransform, layerState.opacity);
|
this.useContainer(target, canvasTransform, layerState.opacity);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import RenderEvent from '../../render/Event.js';
|
|||||||
import RenderEventType from '../../render/EventType.js';
|
import RenderEventType from '../../render/EventType.js';
|
||||||
import {rotateAtOffset} from '../../render/canvas.js';
|
import {rotateAtOffset} from '../../render/canvas.js';
|
||||||
import LayerRenderer from '../Layer.js';
|
import LayerRenderer from '../Layer.js';
|
||||||
import {create as createTransform, apply as applyTransform, compose as composeTransform, toString} from '../../transform.js';
|
import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
@@ -69,12 +69,6 @@ class CanvasLayerRenderer extends LayerRenderer {
|
|||||||
*/
|
*/
|
||||||
this.containerReused = false;
|
this.containerReused = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {HTMLCanvasElement}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.createTransformStringCanvas_ = createCanvasContext2D(1, 1).canvas;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,15 +263,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import("../../transform.js").Transform} transform Transform.
|
|
||||||
* @return {string} CSS transform.
|
|
||||||
*/
|
|
||||||
createTransformString(transform) {
|
|
||||||
this.createTransformStringCanvas_.style.transform = toString(transform);
|
|
||||||
return this.createTransformStringCanvas_.style.transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CanvasLayerRenderer;
|
export default CanvasLayerRenderer;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js'
|
|||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {apply as applyTransform, compose as composeTransform, makeInverse} from '../../transform.js';
|
import {apply as applyTransform, compose as composeTransform, makeInverse} from '../../transform.js';
|
||||||
import {numberSafeCompareFunction} from '../../array.js';
|
import {numberSafeCompareFunction} from '../../array.js';
|
||||||
|
import {createTransformString} from '../../render/canvas.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -243,7 +244,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
-width / 2, -height / 2
|
-width / 2, -height / 2
|
||||||
);
|
);
|
||||||
|
|
||||||
const canvasTransform = this.createTransformString(this.pixelTransform);
|
const canvasTransform = createTransformString(this.pixelTransform);
|
||||||
|
|
||||||
this.useContainer(target, canvasTransform, layerState.opacity);
|
this.useContainer(target, canvasTransform, layerState.opacity);
|
||||||
const context = this.context;
|
const context = this.context;
|
||||||
|
|||||||
+2
-1
@@ -3,9 +3,10 @@
|
|||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
"lib": ["es2017", "dom"], /* Specify library files to be included in the compilation. */
|
"lib": ["es2017", "dom", "webworker"], /* Specify library files to be included in the compilation. */
|
||||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
"checkJs": true, /* Report errors in .js files. */
|
"checkJs": true, /* Report errors in .js files. */
|
||||||
|
"skipLibCheck": true,
|
||||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
|||||||
Reference in New Issue
Block a user