Improve front rendering performance

This commit is contained in:
Mattéo Delabre 2021-05-23 16:35:40 +02:00
parent 05d7faa725
commit 7465314a12
Signed by: matteo
GPG Key ID: AE3FBD02DC583ABB
3 changed files with 73 additions and 50 deletions

View File

@ -18,6 +18,26 @@ export const makeCourseColor = mainColor => {
return color(mainColor).lighten(0.2).hex(); return color(mainColor).lighten(0.2).hex();
}; };
/**
* Make an OpenLayers style function cache its results.
* @param {Function} createStyle Create a style based on the cache key value.
* @param {Function} [cacheKey] Return the cache key for each feature
* (default: identity function).
* @return {Function} Style function that caches the generated styles.
*/
export const cacheStyle = (createStyle, cacheKey = x => x) => {
const cache = {};
return (feature) => {
const key = cacheKey(feature);
if (!(key in cache)) {
cache[key] = createStyle(key);
}
return cache[key];
};
};
export const sizes = { export const sizes = {
segmentOuter: 8, segmentOuter: 8,
segmentInner: 6, segmentInner: 6,

View File

@ -3,17 +3,16 @@ import "ol/ol.css";
import { Map, View } from "ol"; import { Map, View } from "ol";
import { getVectorContext } from "ol/render"; import { getVectorContext } from "ol/render";
import Point from "ol/geom/Point"; import Point from "ol/geom/Point";
import { fromExtent } from 'ol/geom/Polygon';
import * as proj from "ol/proj"; import * as proj from "ol/proj";
import { Style, Icon } from "ol/style"; import { Style, Icon } from "ol/style";
import * as tilesLayers from "./tiles"; import * as tilesLayers from "./tiles";
import * as networkLayers from "./network"; import * as networkLayers from "./network";
import { sizes, makeBorderColor, makeCourseColor } from "./common"; import { sizes, cacheStyle, makeBorderColor, makeCourseColor } from "./common";
import network from "../../tam/network.json"; import network from "../../tam/network.json";
const courseStyles = {}; const courseStyle = cacheStyle(
lineColor => {
const getCourseStyle = lineColor => {
if (!(lineColor in courseStyles)) {
const icon = window.document.createElement("canvas"); const icon = window.document.createElement("canvas");
const shapeSize = sizes.courseSize; const shapeSize = sizes.courseSize;
@ -47,16 +46,14 @@ const getCourseStyle = lineColor => {
iconCtx.fill(); iconCtx.fill();
} }
courseStyles[lineColor] = new Style({ return new Style({
image: new Icon({ image: new Icon({
img: icon, img: icon,
imgSize: [icon.width, icon.height] imgSize: [icon.width, icon.height]
}) })
}); });
} },
);
return courseStyles[lineColor];
};
export const create = (target, coursesSimulation, onClick) => { export const create = (target, coursesSimulation, onClick) => {
const view = new View({ const view = new View({
@ -96,11 +93,17 @@ export const create = (target, coursesSimulation, onClick) => {
/* eslint-enable no-underscore-dangle */ /* eslint-enable no-underscore-dangle */
const ctx = getVectorContext(ev); const ctx = getVectorContext(ev);
const bbox = fromExtent(map.getView().calculateExtent());
bbox.scale(1.05);
for (const course of Object.values(coursesSimulation.courses)) { for (const course of Object.values(coursesSimulation.courses)) {
if (!bbox.intersectsCoordinate(course.position)) {
continue;
}
const point = new Point(course.position); const point = new Point(course.position);
const color = network.lines[course.line].color; const color = network.lines[course.line].color;
const style = getCourseStyle(color); const style = courseStyle(color);
style.getImage().setRotation( style.getImage().setRotation(
view.getRotation() + view.getRotation() +

View File

@ -1,8 +1,8 @@
import network from "../../tam/network.json"; import network from "../../tam/network.json";
import { makeBorderColor, sizes } from "./common"; import { cacheStyle, makeBorderColor, sizes } from "./common";
import GeoJSON from "ol/format/GeoJSON"; import GeoJSON from "ol/format/GeoJSON";
import VectorLayer from "ol/layer/Vector"; import VectorImageLayer from "ol/layer/VectorImage";
import VectorSource from "ol/source/Vector"; import VectorSource from "ol/source/Vector";
import { Style, Fill, Stroke, Circle } from "ol/style"; import { Style, Fill, Stroke, Circle } from "ol/style";
@ -10,34 +10,41 @@ import { Style, Fill, Stroke, Circle } from "ol/style";
const geojsonReader = new GeoJSON({ featureProjection: "EPSG:3857" }); const geojsonReader = new GeoJSON({ featureProjection: "EPSG:3857" });
// Style used for the border of line segments // Style used for the border of line segments
const segmentBorderStyle = feature => new Style({ const segmentBorderStyle = cacheStyle(
color => new Style({
stroke: new Stroke({ stroke: new Stroke({
color: makeBorderColor(feature.get("colors")[0]), color: makeBorderColor(color),
width: sizes.segmentOuter width: sizes.segmentOuter
}) })
}); }),
feature => feature.get("colors")[0],
);
// Style used for the inner part of line segments // Style used for the inner part of line segments
const segmentInnerStyle = feature => new Style({ const segmentInnerStyle = cacheStyle(
color => new Style({
stroke: new Stroke({ stroke: new Stroke({
color: feature.get("colors")[0], color,
width: sizes.segmentInner width: sizes.segmentInner
}) })
}); }),
feature => feature.get("colors")[0],
);
// Style used for line stops // Style used for line stops
const stopStyle = feature => new Style({ const stopStyle = cacheStyle(
color => new Style({
image: new Circle({ image: new Circle({
fill: new Fill({ fill: new Fill({ color }),
color: feature.get("colors")[0]
}),
stroke: new Stroke({ stroke: new Stroke({
color: makeBorderColor(feature.get("colors")[0]), color: makeBorderColor(color),
width: sizes.stopBorder width: sizes.stopBorder
}), }),
radius: sizes.stopRadius radius: sizes.stopRadius
}) })
}); }),
feature => feature.get("colors")[0],
);
/** /**
* Order for features related to a network line inside the same layer. * Order for features related to a network line inside the same layer.
@ -75,7 +82,6 @@ export const getLayers = () => {
// Turn GeoJSON stops list and segments list into vector sources // Turn GeoJSON stops list and segments list into vector sources
const readFeatures = hash => Object.values(hash).map(json => { const readFeatures = hash => Object.values(hash).map(json => {
json.properties.lines = json.properties.routes.filter( json.properties.lines = json.properties.routes.filter(
// Only consider normal routes (excluding alternate routes) // Only consider normal routes (excluding alternate routes)
([lineRef, routeRef]) => ([lineRef, routeRef]) =>
network.lines[lineRef].routes[routeRef].state === "normal" network.lines[lineRef].routes[routeRef].state === "normal"
@ -96,34 +102,28 @@ export const getLayers = () => {
stopsSource.addFeatures(readFeatures(network.stops)); stopsSource.addFeatures(readFeatures(network.stops));
// Background layer on which the darker borders of line segments are drawn // Background layer on which the darker borders of line segments are drawn
const segmentsBorderLayer = new VectorLayer({ const segmentsBorderLayer = new VectorImageLayer({
source: segmentsSource, source: segmentsSource,
renderOrder: lineFeaturesOrder, renderOrder: lineFeaturesOrder,
style: segmentBorderStyle, style: segmentBorderStyle,
imageRatio: 2,
updateWhileInteracting: true,
updateWhileAnimating: true
}); });
// Foreground layer on which the lighter inner part of line segments are // Foreground layer on which the lighter inner part of line segments are
// drawn. The two layers are separated so that forks blend nicely together // drawn. The two layers are separated so that forks blend nicely together
const segmentsInnerLayer = new VectorLayer({ const segmentsInnerLayer = new VectorImageLayer({
source: segmentsSource, source: segmentsSource,
renderOrder: lineFeaturesOrder, renderOrder: lineFeaturesOrder,
style: segmentInnerStyle, style: segmentInnerStyle,
imageRatio: 2,
updateWhileInteracting: true,
updateWhileAnimating: true
}); });
const stopsLayer = new VectorLayer({ const stopsLayer = new VectorImageLayer({
source: stopsSource, source: stopsSource,
renderOrder: lineFeaturesOrder, renderOrder: lineFeaturesOrder,
style: stopStyle, style: stopStyle,
imageRatio: 2,
minZoom: 13, minZoom: 13,
updateWhileInteracting: true,
updateWhileAnimating: true
}); });
return [segmentsBorderLayer, segmentsInnerLayer, stopsLayer]; return [segmentsBorderLayer, segmentsInnerLayer, stopsLayer];