// TODO add comments for each step // Margins are common to both canvas and SVG var margins = { left: 48, top: 48, right: 48, bottom: 48 }, width = 1800 - margins.left - margins.right, height = 300 - margins.top - margins.bottom // Style for the candles that are drawn with pure canvas var candleStyle = { up: { stroke: '#399F6E', fill: '#399F6E' }, down: { stroke: '#FE4747', fill: '#FE4747' } } // Take the current date and round it to the nearest minute var d2 = new Date(Math.round(new Date().getTime() / 60000) * 60000) // Take the rounded time from above and go 10 minutes before it var d1 = d3.timeMinute.offset(d2, -10) // Make a time scale that stretches from the last 10 minutes till now var xScale = d3.scaleTime() .domain([d1, d2]) .range([0, width]) // Create the X axis var xAxis = d3.axisBottom() .scale(xScale) // This function is responsible for drawing the SVG version of the candle // It accepts the length of the upper wick, the candle body and the lower wick and draws a single path that represents the whole candle // We start at (x, y) which represents the leftmost coordinate and top of the candle's high // Draw a line from the center high to the center of open or close whichever comes first // Draw 4 lines representing the rectangle's body. We could have used a rect but we choose to use a path so that we have only 1 object per candle in DOM // Draw the lower wick from the center bottom of the candle's body to the low function drawSVGCandle(x, y, width, upper, body, lower) { var path = d3.path() // path.moveTo(x + width/2, y) // path.lineTo(x + width/2, y + upper + body + lower) var hasUpper = upper >= 1 var isBodyLine = body <= 1 var isBodyRect = body > 1 var hasLower = lower >= 1 //draw the candle upper wick if (hasUpper) { path.moveTo(x + width / 2, y) path.lineTo(x + width / 2, y + upper) } //draw the candle body if (isBodyLine) { path.moveTo(x, y + upper) path.lineTo(x + width, y + upper) } else { path.moveTo(x, y + upper) path.lineTo(x + width, y + upper) path.lineTo(x + width, y + upper + body) path.lineTo(x, y + upper + body) path.closePath() } //lower wick if (hasLower) { path.moveTo(x + width / 2, y + upper + body) path.lineTo(x + width / 2, y + upper + body + lower) } return path } // We follow the same approach for drawing a candle with canvas except we need the candle style for specifying stroke and fill // We also need the context object which is responsible for drawing everything else function drawCanvasCandle(context, x, y, width, upper, body, lower, up) { context.beginPath() context.strokeStyle = up ? candleStyle.up.stroke : candleStyle.down.stroke context.fillStyle = up ? candleStyle.up.fill : candleStyle.down.fill var hasUpper = upper >= 1 var isBodyLine = body <= 1 var isBodyRect = body > 1 var hasLower = lower >= 1 //draw the candle upper wick if (hasUpper) { context.moveTo(x + width / 2, y) context.lineTo(x + width / 2, y + upper) context.stroke() } if (isBodyLine) { context.moveTo(x, y + upper) context.lineTo(x + width, y + upper) context.stroke() } else { context.rect(x, y + upper, width, body) context.fill() } //lower wick if (hasLower) { context.moveTo(x + width / 2, y + upper + body) context.lineTo(x + width / 2, y + upper + body + lower) context.stroke() } } // This method adds the SVG element, scales, axes etc etc in SVG function drawSVG() { var container = d3.select("#svgChart") var svg = container .append("svg") .attr("width", width + margins.left + margins.right) .attr("height", height + margins.top + margins.bottom) .append('g') .attr('transform', 'translate(' + margins.left + ',' + margins.top + ')') var p = svg.selectAll('path.svgcandle') .data(data) .enter() .append('path') .attr('class', function(d){ return d[6] ? 'svgcandle up' : 'svgcandle down' }) .attr('d', function(d) { return drawSVGCandle(d[0], d[1], d[2], d[3], d[4], d[5]) }) var x = svg.append('g') .attr('class', 'x axis') .attr('transform', 'translate(0,' + height + ')') .call(xAxis) } // This method adds the scales, candles etc etc in Canvas function drawCanvas() { var container = d3.select('#canvasChart') var svg = container .append("svg") .attr("width", width + margins.left + margins.right) .attr("height", height + margins.top + margins.bottom) .attr('class', 'svg-area') .append('g') .attr('transform', 'translate(' + margins.left + ',' + margins.top + ')') var canvas = container.append('canvas') .attr('width', width) .attr('height', height) .style('margin-left', margins.left + 'px') .style('margin-top', margins.top + 'px') .attr('class', 'canvas-area') var context = canvas.node().getContext('2d'); for(var i =0; i < data.length; i++){ var d = data[i] drawCanvasCandle(context, d[0], d[1], d[2], d[3], d[4], d[5], d[6]) } // drawCanvasCandle(270, 150, 40, 70, 30, 20) var x = svg.append('g') .attr('class', 'x axis') .attr('transform', 'translate(0,' + height + ')') .call(xAxis) } // Setup buttons d3.select("#btnCanvas").on("click", function () { var t0 = performance.now(); drawCanvas() var t1 = performance.now(); d3.select("#canvasTime").text("Drawing on canvas took " + (t1 - t0) + " milliseconds.") } ); d3.select("#btnSVG").on("click", function () { var t0 = performance.now(); drawSVG() var t1 = performance.now(); d3.select("#svgTime").text("Drawing on SVG took " + (t1 - t0) + " milliseconds."); });

!