I am trying to draw Scroll reach map of my website using canvas. I have bunch Y coordinates and the number of visitors who reached the point. I am coloring each coordinates using the number of visitors who reached the point. Let's say
10 users are visiting my website.
5 users scrolling upto = 0, 300px 3 users scrolling upto = 300, 700px 2 users scrolling upto = 700, 800px
Now, I have 3 color stops (300, 700, 800)px respectively. And the coloring should be based on the number of users. I did this but the transition between the stops are smooth, it looks solid.
var Scroll = function(config, data) { var container, computed; this.data = data; this.config = config; container = document.querySelector(this.config.container); this.canvas = document.createElement('canvas'); this.ctx = this.canvas.getContext('2d'); computed = getComputedStyle(container) || {}; this.canvas.className = 'zarget-scrollmap-canvas'; this.width = this.canvas.width = this.config.width || +(computed.width.replace(/px/, '')); this.height = this.canvas.height = this.config.height || +(computed.height.replace(/px/, '')); this.canvas.style.cssText = 'position:absolute; top: 0px; left:0px'; container.appendChild(this.canvas); var map = function(value, istart, istop, ostart, ostop) { return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); }; this.mapIntensityToColor = function(intensity, min, max) { var cint = map(intensity, min, max, 0, 255); /** * Based On Rainbow Gradient */ if (cint > 204) { return [255, Math.round(map(intensity, min, max, 255, 0)), 0]; } if (cint > 153) { max = (203 / 255 * 100) * (max / 100); return [Math.round(map(intensity, 0, max, 255, 0)), 255, 0]; } if (cint > 102) { max = (153 / 255 * 100) * (max / 100); return [0, 255, Math.round(map(intensity, 0, max, 255, 0))]; } if (cint > 0) { max = (102 / 255 * 100) * (max / 100); return [0, Math.round(map(intensity, 0, max, 255, 0)), 255]; } max = (51 / 255 * 100) * (max / 100); return [0, 0, Math.round(map(intensity, 0, max, 0, 255))]; }; this.draw = function(data) { var min = 0; var max = 1300; var data = [ [0, 50, 1300], [50, 100, 1100], [100, 150, 1100], [150, 200, 1000], [200, 250, 500], [250, 300, 400], [300, 350, 300], [350, 450, 200], [450, 500, 400], [500, 900, 0], [900, 950, 300], [950, 3350, 0] ]; var point, startY, endY, alpha, val, color; this.ctx.globalAlpha = 0.75; for (var i = 0, l = data.length; i < l; i++) { point = data[i]; startY = point[0]; endY = point[1]; val = point[2]; color = this.mapIntensityToColor(val, min, max); this.ctx.fillStyle = "rgb(" + color.join(",") + ")"; this.ctx.fillRect(0, startY, this.width, endY - startY); } };}; var a = new Scroll({"container": "#overlay"}); a.draw();
#overlay { width: 100%; height: 2000px; position: absolute; top: 0px; left: 0px; }
<div id="overlay"></div>
Output of above function
Expected Output
I tried using Linear gradient but the color blending is very poor.
Linear Gradient
2 Answers
Answers 1
You could just use a linear gradient with a single color stop at the "middle" of each step:
this.draw = function(data) { var min = 0; var max = 1300; var data = [ [0, 50, 1300], [50, 100, 1100], [100, 150, 1100], [150, 200, 1000], [200, 250, 500], [250, 300, 400], [300, 350, 300], [350, 450, 200], [450, 500, 400], [500, 900, 0], [900, 950, 300], [950, 3350, 0] ]; var point, startY, endY, alpha, val, color; this.ctx.globalAlpha = 0.75; // Calculate the maximum y scrolled var maxScrollY = 0; for(var i = 0, l = data.length; i < l; i++){ point = data[i]; if(maxScrollY < point[1]){ maxScrollY = point[1]; } } // here, maxScrollY will be 3350 var linearGrad = ctx.createLinearGradient(0, 0, 0, maxScrollY); // Build the gradient for (var i = 0, l = data.length; i < l; i++) { point = data[i]; // You have to divide the coordinate by maxScrollY to get // coordinates between 0.0 and 1.0 startY = point[0] / maxScrollY; endY = point[1] / maxScrollY; // Take the y coordinate between startY and endY for the color stop coordinate var middleY = startY + (endY-startY)/2; val = point[2]; color = this.mapIntensityToColor(val, min, max); linearGrad.addColorStop(middleY, "rgb(" + color.join(",") + ")"); } // Then just render ctx.fillStyle = linearGrad; ctx.fillRect(0, 0, this.width, maxScrollY); }
Here is a demo: https://jsfiddle.net/4n294hb1/
Answers 2
Fiddle here: https://jsfiddle.net/Lfhdzw2c/
I replaced your IntensityToColor function with this, which I borrowed from https://rainbowcoding.com/how-to-create-rainbow-text-in-html-css-javascript/
function color_from_hue(hue) { var h = hue/60; var c = 255; var x = (1 - Math.abs(h%2 - 1))*255; var color; var i = Math.floor(h); if (i == 0) color = rgb_to_hex(c, x, 0); else if (i == 1) color = rgb_to_hex(x, c, 0); else if (i == 2) color = rgb_to_hex(0, c, x); else if (i == 3) color = rgb_to_hex(0, x, c); else if (i == 4) color = rgb_to_hex(x, 0, c); else color = rgb_to_hex(c, 0, x); return color; }
0 comments:
Post a Comment