Skip to content

Commit

Permalink
Use a moderately sized canvas for the span graph (jaegertracing#257)
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Farro <joef@uber.com>
Signed-off-by: vvvprabhakar <vvvprabhakar@gmail.com>
  • Loading branch information
tiffon authored and yurishkuro committed Sep 27, 2018
1 parent dc4d9f0 commit 80367a6
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import * as React from 'react';

import renderIntoCanvas, { CV_WIDTH } from './render-into-canvas';
import renderIntoCanvas from './render-into-canvas';
import colorGenerator from '../../../utils/color-generator';

import './CanvasSpanGraph.css';
Expand Down Expand Up @@ -57,13 +57,6 @@ export default class CanvasSpanGraph extends React.PureComponent<CanvasSpanGraph
}

render() {
return (
<canvas
className="CanvasSpanGraph"
ref={this._setCanvasRef}
width={CV_WIDTH}
height={this.props.items.length}
/>
);
return <canvas className="CanvasSpanGraph" ref={this._setCanvasRef} />;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,48 @@
// limitations under the License.

// exported for tests
export const CV_WIDTH = 4000;
export const MIN_WIDTH = 16;
export const BG_COLOR = '#f5f5f5';
export const ITEM_ALPHA = 0.8;
export const MIN_ITEM_HEIGHT = 2;
export const MAX_TOTAL_HEIGHT = 200;
export const MIN_ITEM_WIDTH = 10;
export const MIN_TOTAL_HEIGHT = 60;
export const ALPHA = 0.8;

export default function renderIntoCanvas(
canvas: HTMLCanvasElement,
items: { valueWidth: number, valueOffset: number, serviceName: string }[],
totalValueWidth: number,
getFillColor: string => [number, number, number]
) {
// eslint-disable-next-line no-param-reassign
canvas.width = CV_WIDTH;
let itemHeight = 1;
let itemYChange = 1;
if (items.length < MIN_TOTAL_HEIGHT) {
// eslint-disable-next-line no-param-reassign
canvas.height = MIN_TOTAL_HEIGHT;
itemHeight = MIN_TOTAL_HEIGHT / items.length;
itemYChange = MIN_TOTAL_HEIGHT / items.length;
} else {
// eslint-disable-next-line no-param-reassign
canvas.height = items.length;
itemYChange = 1;
itemHeight = 1 / (MIN_TOTAL_HEIGHT / items.length);
}
const ctx = canvas.getContext('2d');
const fillCache: Map<string, ?string> = new Map();
const cHeight =
items.length < MIN_TOTAL_HEIGHT ? MIN_TOTAL_HEIGHT : Math.min(items.length, MAX_TOTAL_HEIGHT);
const cWidth = window.innerWidth * 2;
// eslint-disable-next-line no-param-reassign
canvas.width = cWidth;
// eslint-disable-next-line no-param-reassign
canvas.height = cHeight;
const itemHeight = Math.max(MIN_ITEM_HEIGHT, cHeight / items.length);
const itemYChange = cHeight / items.length;

const ctx = canvas.getContext('2d', { alpha: false });
ctx.fillStyle = BG_COLOR;
ctx.fillRect(0, 0, cWidth, cHeight);
for (let i = 0; i < items.length; i++) {
const { valueWidth, valueOffset, serviceName } = items[i];
// eslint-disable-next-line no-bitwise
const x = (valueOffset / totalValueWidth * CV_WIDTH) | 0;
// eslint-disable-next-line no-bitwise
let width = (valueWidth / totalValueWidth * CV_WIDTH) | 0;
if (width < MIN_WIDTH) {
width = MIN_WIDTH;
const x = valueOffset / totalValueWidth * cWidth;
let width = valueWidth / totalValueWidth * cWidth;
if (width < MIN_ITEM_WIDTH) {
width = MIN_ITEM_WIDTH;
}
let fillStyle = fillCache.get(serviceName);
if (fillStyle) {
ctx.fillStyle = fillStyle;
} else {
if (!fillStyle) {
fillStyle = `rgba(${getFillColor(serviceName)
.concat(ALPHA)
.concat(ITEM_ALPHA)
.join()})`;
fillCache.set(serviceName, fillStyle);
ctx.fillStyle = fillStyle;
}
ctx.fillStyle = fillStyle;
ctx.fillRect(x, i * itemYChange, width, itemHeight);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,24 @@

import _range from 'lodash/range';

import renderIntoCanvas, { ALPHA, CV_WIDTH, MIN_TOTAL_HEIGHT, MIN_WIDTH } from './render-into-canvas';
import renderIntoCanvas, {
BG_COLOR,
ITEM_ALPHA,
MIN_ITEM_HEIGHT,
MAX_TOTAL_HEIGHT,
MIN_ITEM_WIDTH,
MIN_TOTAL_HEIGHT,
} from './render-into-canvas';

const getCanvasWidth = () => window.innerWidth * 2;
const getBgFillRect = items => ({
fillStyle: BG_COLOR,
height:
!items || items.length < MIN_TOTAL_HEIGHT ? MIN_TOTAL_HEIGHT : Math.min(MAX_TOTAL_HEIGHT, items.length),
width: getCanvasWidth(),
x: 0,
y: 0,
});

describe('renderIntoCanvas()', () => {
const basicItem = { valueWidth: 100, valueOffset: 50, serviceName: 'some-name' };
Expand Down Expand Up @@ -72,7 +89,7 @@ describe('renderIntoCanvas()', () => {
const canvas = new Canvas();
expect(canvas.width !== canvas.width).toBe(true);
renderIntoCanvas(canvas, [basicItem], 150, getColorFactory());
expect(canvas.width).toBe(CV_WIDTH);
expect(canvas.width).toBe(getCanvasWidth());
});

describe('when there are limited number of items', () => {
Expand All @@ -83,6 +100,18 @@ describe('renderIntoCanvas()', () => {
expect(canvas.height).toBe(MIN_TOTAL_HEIGHT);
});

it('draws the background', () => {
const expectedDrawing = [getBgFillRect()];
const canvas = new Canvas();
const items = [];
const totalValueWidth = 4000;
const getFillColor = getColorFactory();
renderIntoCanvas(canvas, items, totalValueWidth, getFillColor);
expect(canvas.getContext.mock.calls).toEqual([['2d', { alpha: false }]]);
expect(canvas.contexts.length).toBe(1);
expect(canvas.contexts[0].fillRectAccumulator).toEqual(expectedDrawing);
});

it('draws the map', () => {
const totalValueWidth = 4000;
const items = [
Expand All @@ -95,19 +124,24 @@ describe('renderIntoCanvas()', () => {
{ input: items[1].serviceName, output: [1, 1, 1] },
{ input: items[2].serviceName, output: [2, 2, 2] },
];
const expectedDrawings = items.map((item, i) => {
const { valueWidth: width, valueOffset: x } = item;
const color = expectedColors[i].output;
const fillStyle = `rgba(${color.concat(ALPHA).join()})`;
const height = MIN_TOTAL_HEIGHT / items.length;
const y = height * i;
return { fillStyle, height, width, x, y };
});
const expectedDrawings = [
getBgFillRect(),
...items.map((item, i) => {
const { valueWidth, valueOffset } = item;
const color = expectedColors[i].output;
const fillStyle = `rgba(${color.concat(ITEM_ALPHA).join()})`;
const height = MIN_TOTAL_HEIGHT / items.length;
const width = valueWidth / totalValueWidth * getCanvasWidth();
const x = valueOffset / totalValueWidth * getCanvasWidth();
const y = height * i;
return { fillStyle, height, width, x, y };
}),
];
const canvas = new Canvas();
const getFillColor = getColorFactory();
renderIntoCanvas(canvas, items, totalValueWidth, getFillColor);
expect(getFillColor.inputOutput).toEqual(expectedColors);
expect(canvas.getContext.mock.calls).toEqual([['2d']]);
expect(canvas.getContext.mock.calls).toEqual([['2d', { alpha: false }]]);
expect(canvas.contexts.length).toBe(1);
expect(canvas.contexts[0].fillRectAccumulator).toEqual(expectedDrawings);
});
Expand All @@ -132,25 +166,28 @@ describe('renderIntoCanvas()', () => {
valueOffset: i,
serviceName: `service-name-${i}`,
}));
const itemHeight = 1 / (MIN_TOTAL_HEIGHT / items.length);
const expectedColors = items.map((item, i) => ({
input: item.serviceName,
output: [i, i, i],
}));
const expectedDrawings = items.map((item, i) => {
const { valueWidth, valueOffset: x } = item;
const width = Math.max(valueWidth, MIN_WIDTH);
const color = expectedColors[i].output;
const fillStyle = `rgba(${color.concat(ALPHA).join()})`;
const height = itemHeight;
const y = i;
return { fillStyle, height, width, x, y };
});
const expectedDrawings = [
getBgFillRect(items),
...items.map((item, i) => {
const { valueWidth, valueOffset } = item;
const color = expectedColors[i].output;
const fillStyle = `rgba(${color.concat(ITEM_ALPHA).join()})`;
const height = MIN_ITEM_HEIGHT;
const width = Math.max(MIN_ITEM_WIDTH, valueWidth / totalValueWidth * getCanvasWidth());
const x = valueOffset / totalValueWidth * getCanvasWidth();
const y = MAX_TOTAL_HEIGHT / items.length * i;
return { fillStyle, height, width, x, y };
}),
];
const canvas = new Canvas();
const getFillColor = getColorFactory();
renderIntoCanvas(canvas, items, totalValueWidth, getFillColor);
expect(getFillColor.inputOutput).toEqual(expectedColors);
expect(canvas.getContext.mock.calls).toEqual([['2d']]);
expect(canvas.getContext.mock.calls).toEqual([['2d', { alpha: false }]]);
expect(canvas.contexts.length).toBe(1);
expect(canvas.contexts[0].fillRectAccumulator).toEqual(expectedDrawings);
});
Expand Down

0 comments on commit 80367a6

Please sign in to comment.