@@ -141,6 +141,9 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay
141141 }
142142 }
143143
144+ // Cache line-width evaluation per bucket zoom for dash anchoring to avoid evaluating per tile.
145+ const floorwidthByZoom : Record < number , number > = { } ;
146+
144147 const renderTiles = ( coords : OverscaledTileID [ ] , baseDefines : DynamicDefinesType [ ] , depthMode : DepthMode , stencilMode3D : StencilMode , elevated : boolean , firstPass : boolean ) => {
145148 for ( const coord of coords ) {
146149 const tile = sourceCache . getTile ( coord ) ;
@@ -225,6 +228,25 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay
225228 const matrix = isDraping ? coord . projMatrix : null ;
226229 const lineWidthScale = unitInMeters ? ( 1.0 / bucket . tileToMeter ) / pixelsToTileUnits ( tile , 1 , painter . transform . zoom ) : 1.0 ;
227230 const lineFloorWidthScale = unitInMeters ? ( 1.0 / bucket . tileToMeter ) / pixelsToTileUnits ( tile , 1 , Math . floor ( painter . transform . zoom ) ) : 1.0 ;
231+
232+ // Avoid dash flickering while loading ideal tiles on zoom level traversal.
233+ // Override the floorwidth paint property to use width evaluated at bucket zoom
234+ // instead of camera zoom. This ensures stable dash texture coordinates when an
235+ // overscaled lower-zoom tile is temporarily rendered. Restore after draw.
236+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
237+ const widthProperty : { value : { kind : string ; value : number } } | null = dasharray ? ( layer . paint as any ) . _values [ 'line-floorwidth' ] : null ;
238+ let savedFloorwidth : number | undefined ;
239+ if ( widthProperty && widthProperty . value . kind === 'constant' ) {
240+ const bz = bucket . zoom ;
241+ if ( ! ( bz in floorwidthByZoom ) ) {
242+ floorwidthByZoom [ bz ] = Math . max ( 0.01 , layer . widthExpression ( ) . evaluate ( { zoom : bz } ) ) ;
243+ }
244+ savedFloorwidth = widthProperty . value . value ;
245+ const floorZoom = Math . floor ( painter . transform . zoom ) ;
246+ const zoomDiff = floorZoom - tile . tileID . overscaledZ ;
247+ widthProperty . value . value = floorwidthByZoom [ bz ] * Math . pow ( 2 , zoomDiff ) ;
248+ }
249+
228250 const uniformValues : UniformValues < LineUniformsType | LinePatternUniformsType > = image ?
229251 linePatternUniformValues ( painter , tile , layer , matrix , pixelRatio , lineWidthScale , lineFloorWidthScale , [ trimStart , trimEnd ] , groundShadowFactor , patternTransition ) :
230252 lineUniformValues ( painter , tile , layer , matrix , bucket . lineClipsArray . length , pixelRatio , lineWidthScale , lineFloorWidthScale , [ trimStart , trimEnd ] , groundShadowFactor ) ;
@@ -329,6 +351,10 @@ export default function drawLine(painter: Painter, sourceCache: SourceCache, lay
329351 }
330352 renderLine ( elevated ? stencilMode3D : painter . stencilModeForClipping ( coord ) ) ;
331353 }
354+ // Restore floorwidth paint property after draw
355+ if ( savedFloorwidth !== undefined ) {
356+ widthProperty . value . value = savedFloorwidth ;
357+ }
332358 }
333359 } ;
334360
0 commit comments