Skip to content

Commit 424de07

Browse files
committed
looks good
1 parent 201abac commit 424de07

1 file changed

Lines changed: 77 additions & 17 deletions

File tree

‎src/components/canvas/GpxLayer.tsx‎

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ import { getMapPointer } from '../../utils/mapPointer'
2222

2323
const LONG_PRESS_MS = 500
2424

25+
/** Viewport-sized buffer so heat-map segments flatten before opacity is applied. */
26+
let heatMapScratch: HTMLCanvasElement | null = null
27+
28+
function getHeatMapScratch(
29+
width: number,
30+
height: number,
31+
): HTMLCanvasElement | null {
32+
if (width < 1 || height < 1) return null
33+
if (!heatMapScratch) {
34+
heatMapScratch = document.createElement('canvas')
35+
}
36+
if (heatMapScratch.width !== width || heatMapScratch.height !== height) {
37+
heatMapScratch.width = width
38+
heatMapScratch.height = height
39+
}
40+
return heatMapScratch
41+
}
42+
2543
function Track({
2644
track,
2745
adjustMode,
@@ -169,31 +187,73 @@ function Track({
169187

170188
const drawHeatMapStroke = (context: Context) => {
171189
const ctx = context._context
172-
ctx.save()
173-
ctx.globalAlpha = track.opacity
174-
ctx.lineCap = 'round'
175-
ctx.lineJoin = 'round'
176-
ctx.lineWidth = strokeWidth
190+
const scratch = getHeatMapScratch(ctx.canvas.width, ctx.canvas.height)
191+
const octx = scratch?.getContext('2d')
192+
if (!scratch || !octx) return
193+
177194
const n = warped.length / 2
195+
if (n < 2) return
196+
197+
const colorAt = (index: number): string => {
198+
if (paceMode && paceScale) {
199+
return paceToColor(
200+
paceSamples[index]?.paceMinPerKm ?? null,
201+
paceScale.min,
202+
paceScale.max,
203+
)
204+
}
205+
if (hrMode && hrScale) {
206+
return hrToColor(
207+
hrSamples[index]?.hrBpm ?? null,
208+
hrScale.min,
209+
hrScale.max,
210+
)
211+
}
212+
return '#9ca3af'
213+
}
214+
215+
octx.setTransform(1, 0, 0, 1, 0, 0)
216+
octx.clearRect(0, 0, scratch.width, scratch.height)
217+
octx.setTransform(ctx.getTransform())
218+
octx.globalAlpha = 1
219+
octx.lineCap = 'round'
220+
octx.lineJoin = 'round'
221+
222+
octx.lineWidth = outlineWidth
223+
octx.strokeStyle = '#000000'
224+
octx.beginPath()
225+
octx.moveTo(warped[0]!, warped[1]!)
226+
for (let i = 1; i < n; i++) {
227+
octx.lineTo(warped[i * 2]!, warped[i * 2 + 1]!)
228+
}
229+
octx.stroke()
230+
231+
octx.lineWidth = strokeWidth
178232
for (let i = 1; i < n; i++) {
179233
const x0 = warped[(i - 1) * 2]!
180234
const y0 = warped[(i - 1) * 2 + 1]!
181235
const x1 = warped[i * 2]!
182236
const y1 = warped[i * 2 + 1]!
183-
if (paceMode && paceScale) {
184-
const pace = paceSamples[i]?.paceMinPerKm ?? null
185-
ctx.strokeStyle = paceToColor(pace, paceScale.min, paceScale.max)
186-
} else if (hrMode && hrScale) {
187-
const hr = hrSamples[i]?.hrBpm ?? null
188-
ctx.strokeStyle = hrToColor(hr, hrScale.min, hrScale.max)
237+
const cEnd = colorAt(i)
238+
const cStart = i === 1 ? cEnd : colorAt(i - 1)
239+
if (cStart === cEnd) {
240+
octx.strokeStyle = cEnd
189241
} else {
190-
continue
242+
const gradient = octx.createLinearGradient(x0, y0, x1, y1)
243+
gradient.addColorStop(0, cStart)
244+
gradient.addColorStop(1, cEnd)
245+
octx.strokeStyle = gradient
191246
}
192-
ctx.beginPath()
193-
ctx.moveTo(x0, y0)
194-
ctx.lineTo(x1, y1)
195-
ctx.stroke()
247+
octx.beginPath()
248+
octx.moveTo(x0, y0)
249+
octx.lineTo(x1, y1)
250+
octx.stroke()
196251
}
252+
253+
ctx.save()
254+
ctx.setTransform(1, 0, 0, 1, 0, 0)
255+
ctx.globalAlpha = track.opacity
256+
ctx.drawImage(scratch, 0, 0)
197257
ctx.restore()
198258
}
199259

@@ -208,7 +268,7 @@ function Track({
208268
hitStrokeWidth={strokeWidth * 5}
209269
lineCap="round"
210270
lineJoin="round"
211-
opacity={track.opacity}
271+
opacity={0}
212272
listening={adjustMode}
213273
onClick={addAnchorAtPointer}
214274
onTap={addAnchorAtPointer}

0 commit comments

Comments
 (0)