|
1 |
| -import { computeDimensions, computePositionFromAnchor, computeItemLayout } from './compute' |
2 |
| -import { LegendCanvasProps } from './types' |
| 1 | +import { CompleteTheme, degreesToRadians } from '@nivo/core' |
| 2 | +import { |
| 3 | + computeDimensions, |
| 4 | + computePositionFromAnchor, |
| 5 | + computeItemLayout, |
| 6 | + computeContinuousColorsLegend, |
| 7 | +} from './compute' |
| 8 | +import { AnchoredContinuousColorsLegendProps, LegendCanvasProps } from './types' |
| 9 | +import { continuousColorsLegendDefaults } from './defaults' |
3 | 10 |
|
4 | 11 | const textAlignMapping = {
|
5 | 12 | start: 'left',
|
@@ -94,3 +101,135 @@ export const renderLegendToCanvas = (
|
94 | 101 |
|
95 | 102 | ctx.restore()
|
96 | 103 | }
|
| 104 | + |
| 105 | +export const renderContinuousColorLegendToCanvas = ( |
| 106 | + ctx: CanvasRenderingContext2D, |
| 107 | + { |
| 108 | + containerWidth, |
| 109 | + containerHeight, |
| 110 | + anchor, |
| 111 | + translateX = 0, |
| 112 | + translateY = 0, |
| 113 | + scale, |
| 114 | + length = continuousColorsLegendDefaults.length, |
| 115 | + thickness = continuousColorsLegendDefaults.thickness, |
| 116 | + direction = continuousColorsLegendDefaults.direction, |
| 117 | + ticks: _ticks, |
| 118 | + tickPosition = continuousColorsLegendDefaults.tickPosition, |
| 119 | + tickSize = continuousColorsLegendDefaults.tickSize, |
| 120 | + tickSpacing = continuousColorsLegendDefaults.tickSpacing, |
| 121 | + tickOverlap = continuousColorsLegendDefaults.tickOverlap, |
| 122 | + tickFormat = continuousColorsLegendDefaults.tickFormat, |
| 123 | + title, |
| 124 | + titleAlign = continuousColorsLegendDefaults.titleAlign, |
| 125 | + titleOffset = continuousColorsLegendDefaults.titleOffset, |
| 126 | + theme, |
| 127 | + }: AnchoredContinuousColorsLegendProps & { |
| 128 | + theme: CompleteTheme |
| 129 | + } |
| 130 | +) => { |
| 131 | + const { |
| 132 | + width, |
| 133 | + height, |
| 134 | + gradientX1, |
| 135 | + gradientY1, |
| 136 | + gradientX2, |
| 137 | + gradientY2, |
| 138 | + colorStops, |
| 139 | + ticks, |
| 140 | + titleText, |
| 141 | + titleX, |
| 142 | + titleY, |
| 143 | + titleRotation, |
| 144 | + titleVerticalAlign, |
| 145 | + titleHorizontalAlign, |
| 146 | + } = computeContinuousColorsLegend({ |
| 147 | + scale, |
| 148 | + ticks: _ticks, |
| 149 | + length, |
| 150 | + thickness, |
| 151 | + direction, |
| 152 | + tickPosition, |
| 153 | + tickSize, |
| 154 | + tickSpacing, |
| 155 | + tickOverlap, |
| 156 | + tickFormat, |
| 157 | + title, |
| 158 | + titleAlign, |
| 159 | + titleOffset, |
| 160 | + }) |
| 161 | + |
| 162 | + const { x, y } = computePositionFromAnchor({ |
| 163 | + anchor, |
| 164 | + translateX, |
| 165 | + translateY, |
| 166 | + containerWidth, |
| 167 | + containerHeight, |
| 168 | + width, |
| 169 | + height, |
| 170 | + }) |
| 171 | + |
| 172 | + ctx.save() |
| 173 | + ctx.translate(x, y) |
| 174 | + |
| 175 | + const gradient = ctx.createLinearGradient( |
| 176 | + gradientX1 * width, |
| 177 | + gradientY1 * height, |
| 178 | + gradientX2 * width, |
| 179 | + gradientY2 * height |
| 180 | + ) |
| 181 | + colorStops.forEach(colorStop => { |
| 182 | + gradient.addColorStop(colorStop.offset, colorStop.stopColor) |
| 183 | + }) |
| 184 | + |
| 185 | + ctx.fillStyle = gradient |
| 186 | + ctx.fillRect(0, 0, width, height) |
| 187 | + |
| 188 | + ctx.font = `${ |
| 189 | + theme.legends.ticks.text.fontWeight ? `${theme.legends.ticks.text.fontWeight} ` : '' |
| 190 | + }${theme.legends.ticks.text.fontSize}px ${theme.legends.ticks.text.fontFamily}` |
| 191 | + |
| 192 | + ticks.forEach(tick => { |
| 193 | + if ((theme.legends.ticks.line.strokeWidth ?? 0) > 0) { |
| 194 | + ctx.lineWidth = Number(theme.axis.ticks.line.strokeWidth) |
| 195 | + if (theme.axis.ticks.line.stroke) { |
| 196 | + ctx.strokeStyle = theme.axis.ticks.line.stroke |
| 197 | + } |
| 198 | + ctx.lineCap = 'square' |
| 199 | + |
| 200 | + ctx.beginPath() |
| 201 | + ctx.moveTo(tick.x1, tick.y1) |
| 202 | + ctx.lineTo(tick.x2, tick.y2) |
| 203 | + ctx.stroke() |
| 204 | + } |
| 205 | + |
| 206 | + if (theme.legends.ticks.text.fill) { |
| 207 | + ctx.fillStyle = theme.legends.ticks.text.fill |
| 208 | + } |
| 209 | + ctx.textAlign = tick.textHorizontalAlign === 'middle' ? 'center' : tick.textHorizontalAlign |
| 210 | + ctx.textBaseline = tick.textVerticalAlign === 'central' ? 'middle' : tick.textVerticalAlign |
| 211 | + |
| 212 | + ctx.fillText(tick.text, tick.textX, tick.textY) |
| 213 | + }) |
| 214 | + |
| 215 | + if (titleText) { |
| 216 | + ctx.save() |
| 217 | + ctx.translate(titleX, titleY) |
| 218 | + ctx.rotate(degreesToRadians(titleRotation)) |
| 219 | + |
| 220 | + ctx.font = `${ |
| 221 | + theme.legends.title.text.fontWeight ? `${theme.legends.title.text.fontWeight} ` : '' |
| 222 | + }${theme.legends.title.text.fontSize}px ${theme.legends.title.text.fontFamily}` |
| 223 | + if (theme.legends.title.text.fill) { |
| 224 | + ctx.fillStyle = theme.legends.title.text.fill |
| 225 | + } |
| 226 | + ctx.textAlign = titleHorizontalAlign === 'middle' ? 'center' : titleHorizontalAlign |
| 227 | + ctx.textBaseline = titleVerticalAlign |
| 228 | + |
| 229 | + ctx.fillText(titleText, 0, 0) |
| 230 | + |
| 231 | + ctx.restore() |
| 232 | + } |
| 233 | + |
| 234 | + ctx.restore() |
| 235 | +} |
0 commit comments