This example uses OpenLayers v10.9.0. The latest is v.
Edit

#### Measure using vector styles

draw 17 edit 13 measure 3 modify 11 style 27 vector 86

#####

Measurement type
Show segment lengths: Clear previous measure:

Example of measuring lengths and areas using vector styles.

Using vector styles instead of overlays makes it easy to set up, then modify and clear a measure. Additional information such as the lengths of individual segments or sides can be included as required.

The `getLength()` and `getArea()` functions calculate spherical lengths and areas for geometries. Lengths are calculated by assuming great circle segments between geometry coordinates. Areas are calculated as if edges of polygons were great circle segments.

Note that the `geometry.getLength()` and `geometry.getArea()` methods return measures of projected (planar) geometries. These can be very different than on-the-ground measures in certain situations — in northern and southern latitudes using Web Mercator for example. For better results, use the functions in the `ol/sphere` module.

##### main.js

    import Map from 'ol/Map.js';
    import View from 'ol/View.js';
    import LineString from 'ol/geom/LineString.js';
    import Point from 'ol/geom/Point.js';
    import Draw from 'ol/interaction/Draw.js';
    import Modify from 'ol/interaction/Modify.js';
    import TileLayer from 'ol/layer/Tile.js';
    import VectorLayer from 'ol/layer/Vector.js';
    import OSM from 'ol/source/OSM.js';
    import VectorSource from 'ol/source/Vector.js';
    import {getArea, getLength} from 'ol/sphere.js';
    import CircleStyle from 'ol/style/Circle.js';
    import Fill from 'ol/style/Fill.js';
    import RegularShape from 'ol/style/RegularShape.js';
    import Stroke from 'ol/style/Stroke.js';
    import Style from 'ol/style/Style.js';
    import Text from 'ol/style/Text.js';

    const typeSelect = document.getElementById('type');
    const showSegments = document.getElementById('segments');
    const clearPrevious = document.getElementById('clear');

    const style = new Style({
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)',
      }),
      stroke: new Stroke({
        color: 'rgba(0, 0, 0, 0.5)',
        lineDash: [10, 10],
        width: 2,
      }),
      image: new CircleStyle({
        radius: 5,
        stroke: new Stroke({
          color: 'rgba(0, 0, 0, 0.7)',
        }),
        fill: new Fill({
          color: 'rgba(255, 255, 255, 0.2)',
        }),
      }),
    });

    const labelStyle = new Style({
      text: new Text({
        font: '14px Calibri,sans-serif',
        fill: new Fill({
          color: 'rgba(255, 255, 255, 1)',
        }),
        backgroundFill: new Fill({
          color: 'rgba(0, 0, 0, 0.7)',
        }),
        padding: [3, 3, 3, 3],
        textBaseline: 'bottom',
        offsetY: -15,
      }),
      image: new RegularShape({
        radius: 8,
        points: 3,
        angle: Math.PI,
        displacement: [0, 10],
        fill: new Fill({
          color: 'rgba(0, 0, 0, 0.7)',
        }),
      }),
    });

    const tipStyle = new Style({
      text: new Text({
        font: '12px Calibri,sans-serif',
        fill: new Fill({
          color: 'rgba(255, 255, 255, 1)',
        }),
        backgroundFill: new Fill({
          color: 'rgba(0, 0, 0, 0.4)',
        }),
        padding: [2, 2, 2, 2],
        textAlign: 'left',
        offsetX: 15,
      }),
    });

    const modifyStyle = new Style({
      image: new CircleStyle({
        radius: 5,
        stroke: new Stroke({
          color: 'rgba(0, 0, 0, 0.7)',
        }),
        fill: new Fill({
          color: 'rgba(0, 0, 0, 0.4)',
        }),
      }),
      text: new Text({
        text: 'Drag to modify',
        font: '12px Calibri,sans-serif',
        fill: new Fill({
          color: 'rgba(255, 255, 255, 1)',
        }),
        backgroundFill: new Fill({
          color: 'rgba(0, 0, 0, 0.7)',
        }),
        padding: [2, 2, 2, 2],
        textAlign: 'left',
        offsetX: 15,
      }),
    });

    const segmentStyle = new Style({
      text: new Text({
        font: '12px Calibri,sans-serif',
        fill: new Fill({
          color: 'rgba(255, 255, 255, 1)',
        }),
        backgroundFill: new Fill({
          color: 'rgba(0, 0, 0, 0.4)',
        }),
        padding: [2, 2, 2, 2],
        textBaseline: 'bottom',
        offsetY: -12,
      }),
      image: new RegularShape({
        radius: 6,
        points: 3,
        angle: Math.PI,
        displacement: [0, 8],
        fill: new Fill({
          color: 'rgba(0, 0, 0, 0.4)',
        }),
      }),
    });

    const segmentStyles = [segmentStyle];

    const formatLength = function (line) {
      const length = getLength(line);
      let output;
      if (length > 100) {
        output = Math.round((length / 1000) * 100) / 100 + ' km';
      } else {
        output = Math.round(length * 100) / 100 + ' m';
      }
      return output;
    };

    const formatArea = function (polygon) {
      const area = getArea(polygon);
      let output;
      if (area > 10000) {
        output = Math.round((area / 1000000) * 100) / 100 + ' km\xB2';
      } else {
        output = Math.round(area * 100) / 100 + ' m\xB2';
      }
      return output;
    };

    const raster = new TileLayer({
      source: new OSM(),
    });

    const source = new VectorSource();

    const modify = new Modify({source: source, style: modifyStyle});

    let tipPoint;

    function styleFunction(feature, segments, drawType, tip) {
      const styles = [];
      const geometry = feature.getGeometry();
      const type = geometry.getType();
      let point, label, line;
      if (!drawType || drawType === type || type === 'Point') {
        styles.push(style);
        if (type === 'Polygon') {
          point = geometry.getInteriorPoint();
          label = formatArea(geometry);
          line = new LineString(geometry.getCoordinates()[0]);
        } else if (type === 'LineString') {
          point = new Point(geometry.getLastCoordinate());
          label = formatLength(geometry);
          line = geometry;
        }
      }
      if (segments && line) {
        let count = 0;
        line.forEachSegment(function (a, b) {
          const segment = new LineString([a, b]);
          const label = formatLength(segment);
          if (segmentStyles.length - 1 < count) {
            segmentStyles.push(segmentStyle.clone());
          }
          const segmentPoint = new Point(segment.getCoordinateAt(0.5));
          segmentStyles[count].setGeometry(segmentPoint);
          segmentStyles[count].getText().setText(label);
          styles.push(segmentStyles[count]);
          count++;
        });
      }
      if (label) {
        labelStyle.setGeometry(point);
        labelStyle.getText().setText(label);
        styles.push(labelStyle);
      }
      if (
        tip &&
        type === 'Point' &&
        !modify.getOverlay().getSource().getFeatures().length
      ) {
        tipPoint = geometry;
        tipStyle.getText().setText(tip);
        styles.push(tipStyle);
      }
      return styles;
    }

    const vector = new VectorLayer({
      source: source,
      style: function (feature) {
        return styleFunction(feature, showSegments.checked);
      },
    });

    const map = new Map({
      layers: [raster, vector],
      target: 'map',
      view: new View({
        center: [-11000000, 4600000],
        zoom: 15,
      }),
    });

    map.addInteraction(modify);

    let draw; // global so we can remove it later

    function addInteraction() {
      const drawType = typeSelect.value;
      const activeTip =
        'Click to continue drawing the ' +
        (drawType === 'Polygon' ? 'polygon' : 'line');
      const idleTip = 'Click to start measuring';
      let tip = idleTip;
      draw = new Draw({
        source: source,
        type: drawType,
        style: function (feature) {
          return styleFunction(feature, showSegments.checked, drawType, tip);
        },
      });
      draw.on('drawstart', function () {
        if (clearPrevious.checked) {
          source.clear();
        }
        modify.setActive(false);
        tip = activeTip;
      });
      draw.on('drawend', function () {
        modifyStyle.setGeometry(tipPoint);
        modify.setActive(true);
        map.once('pointermove', function () {
          modifyStyle.setGeometry(null);
        });
        tip = idleTip;
      });
      modify.setActive(true);
      map.addInteraction(draw);
    }

    typeSelect.onchange = function () {
      map.removeInteraction(draw);
      addInteraction();
    };

    addInteraction();

    showSegments.onchange = function () {
      vector.changed();
      draw.getOverlay().changed();
    };

##### index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Measure using vector styles</title>
        <link rel="stylesheet" href="node_modules/ol/ol.css">
        <style>
          .map {
            width: 100%;
            height: 400px;
          }
        </style>
      </head>
      <body>
        <div id="map" class="map"></div>
        <form>
          <label for="type">Measurement type &nbsp;</label>
          <select id="type">
            <option value="LineString">Length (LineString)</option>
            <option value="Polygon">Area (Polygon)</option>
          </select>
          &nbsp;&nbsp;&nbsp;&nbsp;
          <label for="segments">Show segment lengths:&nbsp;</label>
          <input type="checkbox" id="segments" checked />
          &nbsp;&nbsp;&nbsp;&nbsp;
          <label for="clear">Clear previous measure:&nbsp;</label>
          <input type="checkbox" id="clear" checked />
        </form>

        <script type="module" src="main.js"></script>
      </body>
    </html>

##### package.json

    {
      "name": "measure-style",
      "dependencies": {
        "ol": "10.9.0"
      },
      "devDependencies": {
        "vite": "^3.2.3"
      },
      "scripts": {
        "start": "vite",
        "build": "vite build"
      }
    }