From 7465314a1266defbe94e4c6529b8266b86056fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre?= Date: Sun, 23 May 2021 16:35:40 +0200 Subject: [PATCH] Improve front rendering performance --- src/front/map/common.js | 20 +++++++++++ src/front/map/index.js | 25 +++++++------ src/front/map/network.js | 78 ++++++++++++++++++++-------------------- 3 files changed, 73 insertions(+), 50 deletions(-) diff --git a/src/front/map/common.js b/src/front/map/common.js index 4f869ae..7a902c0 100644 --- a/src/front/map/common.js +++ b/src/front/map/common.js @@ -18,6 +18,26 @@ export const makeCourseColor = mainColor => { 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 = { segmentOuter: 8, segmentInner: 6, diff --git a/src/front/map/index.js b/src/front/map/index.js index 8efd858..5831701 100644 --- a/src/front/map/index.js +++ b/src/front/map/index.js @@ -3,17 +3,16 @@ import "ol/ol.css"; import { Map, View } from "ol"; import { getVectorContext } from "ol/render"; import Point from "ol/geom/Point"; +import { fromExtent } from 'ol/geom/Polygon'; import * as proj from "ol/proj"; import { Style, Icon } from "ol/style"; import * as tilesLayers from "./tiles"; import * as networkLayers from "./network"; -import { sizes, makeBorderColor, makeCourseColor } from "./common"; +import { sizes, cacheStyle, makeBorderColor, makeCourseColor } from "./common"; import network from "../../tam/network.json"; -const courseStyles = {}; - -const getCourseStyle = lineColor => { - if (!(lineColor in courseStyles)) { +const courseStyle = cacheStyle( + lineColor => { const icon = window.document.createElement("canvas"); const shapeSize = sizes.courseSize; @@ -47,16 +46,14 @@ const getCourseStyle = lineColor => { iconCtx.fill(); } - courseStyles[lineColor] = new Style({ + return new Style({ image: new Icon({ img: icon, imgSize: [icon.width, icon.height] }) }); - } - - return courseStyles[lineColor]; -}; + }, +); export const create = (target, coursesSimulation, onClick) => { const view = new View({ @@ -96,11 +93,17 @@ export const create = (target, coursesSimulation, onClick) => { /* eslint-enable no-underscore-dangle */ const ctx = getVectorContext(ev); + const bbox = fromExtent(map.getView().calculateExtent()); + bbox.scale(1.05); for (const course of Object.values(coursesSimulation.courses)) { + if (!bbox.intersectsCoordinate(course.position)) { + continue; + } + const point = new Point(course.position); const color = network.lines[course.line].color; - const style = getCourseStyle(color); + const style = courseStyle(color); style.getImage().setRotation( view.getRotation() + diff --git a/src/front/map/network.js b/src/front/map/network.js index c4f99f0..b6e3429 100644 --- a/src/front/map/network.js +++ b/src/front/map/network.js @@ -1,8 +1,8 @@ import network from "../../tam/network.json"; -import { makeBorderColor, sizes } from "./common"; +import { cacheStyle, makeBorderColor, sizes } from "./common"; 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 { 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" }); // Style used for the border of line segments -const segmentBorderStyle = feature => new Style({ - stroke: new Stroke({ - color: makeBorderColor(feature.get("colors")[0]), - width: sizes.segmentOuter - }) -}); +const segmentBorderStyle = cacheStyle( + color => new Style({ + stroke: new Stroke({ + color: makeBorderColor(color), + width: sizes.segmentOuter + }) + }), + feature => feature.get("colors")[0], +); // Style used for the inner part of line segments -const segmentInnerStyle = feature => new Style({ - stroke: new Stroke({ - color: feature.get("colors")[0], - width: sizes.segmentInner - }) -}); +const segmentInnerStyle = cacheStyle( + color => new Style({ + stroke: new Stroke({ + color, + width: sizes.segmentInner + }) + }), + feature => feature.get("colors")[0], +); // Style used for line stops -const stopStyle = feature => new Style({ - image: new Circle({ - fill: new Fill({ - color: feature.get("colors")[0] - }), - stroke: new Stroke({ - color: makeBorderColor(feature.get("colors")[0]), - width: sizes.stopBorder - }), - radius: sizes.stopRadius - }) -}); +const stopStyle = cacheStyle( + color => new Style({ + image: new Circle({ + fill: new Fill({ color }), + stroke: new Stroke({ + color: makeBorderColor(color), + width: sizes.stopBorder + }), + radius: sizes.stopRadius + }) + }), + feature => feature.get("colors")[0], +); /** * 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 const readFeatures = hash => Object.values(hash).map(json => { json.properties.lines = json.properties.routes.filter( - // Only consider normal routes (excluding alternate routes) ([lineRef, routeRef]) => network.lines[lineRef].routes[routeRef].state === "normal" @@ -96,34 +102,28 @@ export const getLayers = () => { stopsSource.addFeatures(readFeatures(network.stops)); // Background layer on which the darker borders of line segments are drawn - const segmentsBorderLayer = new VectorLayer({ + const segmentsBorderLayer = new VectorImageLayer({ source: segmentsSource, renderOrder: lineFeaturesOrder, style: segmentBorderStyle, - - updateWhileInteracting: true, - updateWhileAnimating: true + imageRatio: 2, }); // Foreground layer on which the lighter inner part of line segments are // drawn. The two layers are separated so that forks blend nicely together - const segmentsInnerLayer = new VectorLayer({ + const segmentsInnerLayer = new VectorImageLayer({ source: segmentsSource, renderOrder: lineFeaturesOrder, style: segmentInnerStyle, - - updateWhileInteracting: true, - updateWhileAnimating: true + imageRatio: 2, }); - const stopsLayer = new VectorLayer({ + const stopsLayer = new VectorImageLayer({ source: stopsSource, renderOrder: lineFeaturesOrder, style: stopStyle, - + imageRatio: 2, minZoom: 13, - updateWhileInteracting: true, - updateWhileAnimating: true }); return [segmentsBorderLayer, segmentsInnerLayer, stopsLayer];