Search code examples
typescriptopenlayerstransparency

Openlayers: Gradient glow on LineString


I have a layer that renders LineStrings and am trying to apply a glow effect to the lines. The Style I created uses a custom renderer to create a stroke with a gradient perpendicular to each line segment:

const glow_style = new Style({
  renderer: (_coords, state) => {
    const ctx = state.context;
    const coords = _coords as Coordinate[];
    ctx.lineWidth = 25;
    for (let i = 1; i < coords.length; i++) {
      const start = coords[i - 1];
      const end = coords[i];
      const [grd_start, grd_end] = getPerpendicularPoints(start, end, ctx.lineWidth);
      const grd = ctx.createLinearGradient(grd_start[0], grd_start[1], grd_end[0], grd_end[1]);
      grd.addColorStop(0, '#ffffff00');
      grd.addColorStop(0.5, 'white');
      grd.addColorStop(1, '#ffffff00');
      ctx.strokeStyle = grd;
      ctx.beginPath();
      ctx.moveTo(start[0], start[1]);
      ctx.lineTo(end[0], end[1]);
      ctx.stroke();
    }
  }
});

This style works for completely straight lines, but breaks down at corners, because the gradient doesn't connect nicely between line segments. If ctx.lineCap is left as butt, the gradient is discontiguous around corners. If it's set to round, the segments touch, but the gradient becomes discontinuous because of overlapping. Here are examples of each:

gradient glow w/ "butt" line caps gradient glow w/ "round" line caps

What options do I have for creating a smooth gradient along the entire LineString?


Solution

  • It would be simpler to render the entire linestring without breaking it into segments by drawing lines of decreasing width. For a smooth gradient the opacity of each line should be defined as the fraction of the remaining transparency. It could even be done as an OpenLayers style array:

    var steps = 13;
    var styles = [];
    for (var i = 0; i < steps; i++) {
        styles.push(
            new ol.style.Style({
                stroke: new ol.style.Stroke({
                    color: [255, 255, 255, 1/(steps - i)],
                    width: (steps-i)*2 - 1
                })
            })
        );
    }