In order to bind data to a lot of elements (62500) it is possible to first bind the data to virtual custom
elements and draw them using d3-timer
.
Last active
February 17, 2018 20:29
-
-
Save Niekes/74a509b574e9e30b0e67d2c8cbd70699 to your computer and use it in GitHub Desktop.
data-binding and canvas with d3.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<body> | |
<style type="text/css"> | |
div { | |
align-items: center; | |
display: flex; | |
flex-direction: column; | |
position: absolute; | |
right: 10px; | |
top: 10px; | |
} | |
canvas { | |
margin: auto; | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
} | |
</style> | |
<div id="hue"> | |
<label>Hue <span></span></label> | |
<input type="range" min="0" max="360" step="0.01" value="0"> | |
</div> | |
<div id="saturation" style="top: 60px"> | |
<label>Saturation <span></span></label> | |
<input type="range" min="0" max="100" step="0.01" value="100"> | |
</div> | |
<div id="lightness" style="top: 110px"> | |
<label>Lightness <span></span></label> | |
<input type="range" min="0" max="100" step="0.01" value="100"> | |
</div> | |
<div id="opacity" style="top: 160px"> | |
<label>Opacity <span></span></label> | |
<input type="range" min="0" max="1" step="0.01" value="1"> | |
</div> | |
<canvas id="canvas"></canvas> | |
<script> | |
var width = 150; | |
var height = 150; | |
var tt = 1500; | |
var hue = null; | |
var saturation = null; | |
var lightness = null; | |
var opacity = null; | |
var detachedContainer = document.createElement('custom'); | |
var dataContainer = d3.select(detachedContainer); | |
var canvas = d3.select('#canvas').attr('width', width).attr('height', height); | |
var context = canvas.node().getContext('2d'); | |
function databind(data) { | |
var dataBinding = dataContainer.selectAll('custom.pixel').data(data); | |
dataBinding | |
.enter() | |
.append('custom') | |
.attr('class', 'pixel') | |
.attr('size', 1) | |
.attr('fillStyle', setColorOfPixel) | |
.attr('x', setXPosOfPixel) | |
.attr('y', setYPosOfPixel) | |
.merge(dataBinding) | |
.transition().duration(tt) | |
.attr('size', 1) | |
.attr('x', setXPosOfPixel) | |
.attr('y', setYPosOfPixel) | |
.attr('fillStyle', setColorOfPixel); | |
} | |
function setColorOfPixel(d){ | |
var hsl = d3.hsl(d.value); | |
hsl.h = hsl.h + hue; | |
hsl.s = hsl.s * (saturation/100); | |
hsl.l = hsl.l * (lightness/100); | |
hsl.opacity = opacity; | |
return hsl; | |
} | |
function setXPosOfPixel(d){ | |
return d.x; | |
} | |
function setYPosOfPixel(d){ | |
return d.y; | |
} | |
function draw() { | |
context.fillStyle = '#fff'; | |
context.rect(0, 0, width, height); | |
context.fill(); | |
var elements = dataContainer.selectAll('custom.pixel'); | |
elements.each(function() { | |
var node = d3.select(this); | |
context.beginPath(); | |
context.fillStyle = node.attr('fillStyle'); | |
context.rect(node.attr('x'), node.attr('y'), node.attr('size'), node.attr('size')); | |
context.fill(); | |
context.closePath(); | |
}); | |
} | |
function update(){ | |
var h = document.querySelector('#hue'); | |
var s = document.querySelector('#saturation'); | |
var l = document.querySelector('#lightness'); | |
var o = document.querySelector('#opacity'); | |
var pixels = []; | |
hue = h.querySelector('input').value ? parseInt(h.querySelector('input').value) : 100; | |
saturation = s.querySelector('input').value ? parseInt(s.querySelector('input').value) : 100; | |
lightness = l.querySelector('input').value ? parseInt(l.querySelector('input').value) : 100; | |
opacity = o.querySelector('input').value ? o.querySelector('input').value : 100; | |
h.querySelector('span').innerHTML = hue + '°'; | |
s.querySelector('span').innerHTML = saturation + '%'; | |
l.querySelector('span').innerHTML = lightness + '%'; | |
o.querySelector('span').innerHTML = opacity; | |
var img = new Image(); | |
img.src = 'lena.png'; | |
img.onload = function() { | |
var vis = document.createElement('canvas'); | |
var ctx = vis.getContext('2d'); | |
vis.width = width; | |
vis.height = height; | |
ctx.drawImage(img, 0, 0); | |
img.style.display = 'none'; | |
var imageData = ctx.getImageData(0, 0, width, height); | |
var data = imageData.data; | |
for (var i = 0; i < data.length; i += 4) { | |
var index = i / 4; | |
var xCoord = index % width; | |
var yCoord = (index - xCoord) / width; | |
pixels.push({ | |
x : xCoord, | |
y : yCoord, | |
value : d3.rgb(data[i], data[i + 1], data[i + 2], data[i + 3] / 255) | |
}); | |
} | |
databind(pixels); | |
var t = d3.timer(function(elapsed) { | |
draw(); | |
if (elapsed > tt) t.stop(); | |
}); | |
}; | |
} | |
d3.selectAll('input').on('change', update); | |
update(); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment