|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.min.js" integrity="sha256-+9Mf3cAVmxxudDsr1XwXUeRZFtvdWVYdq5/vcgiYyNU=" crossorigin="anonymous"></script> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js" integrity="sha256-HJ7j+71YYw6Kcs8THwQV9lXmPOcR0eXlg7n8KRTZsyA=" crossorigin="anonymous"></script> |
|
<script> |
|
// stats.js - http://github.com/mrdoob/stats.js |
|
(function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault(); |
|
u(++l%c.children.length)},!1);var k=(performance||Date).now(),g=k,a=0,r=e(new f.Panel("FPS","#0ff","#002")),h=e(new f.Panel("MS","#0f0","#020"));if(self.performance&&self.performance.memory)var t=e(new f.Panel("MB","#f08","#201"));u(0);return{REVISION:16,dom:c,addPanel:e,showPanel:u,begin:function(){k=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();h.update(c-k,200);if(c>g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/ |
|
1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v); |
|
b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f}); |
|
</script> |
|
|
|
<style> |
|
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } |
|
canvas, img { |
|
outline: 1px solid gray; |
|
display: block; |
|
margin: 0.5em auto; |
|
} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
|
|
|
|
<canvas></canvas> |
|
<img> |
|
|
|
|
|
|
|
<script> |
|
|
|
var w = 950, h = 300, |
|
pos = [-100, -100], // mouse position |
|
canvas = document.querySelector("canvas"), |
|
ctx = canvas.getContext("2d"); |
|
|
|
d3.select(canvas).attr("width", w).attr("height", h) |
|
.on("mouseleave", function () { pos = [-100, -100]; }) |
|
.on("mousemove", function () { pos = d3.mouse(this); }); |
|
|
|
var genColor = (function () { |
|
var nextCol = 1, _step = 50; |
|
var fn = function () { |
|
var ret = []; |
|
// via http://stackoverflow.com/a/15804183 |
|
if (nextCol < 16777215) { |
|
ret.push(nextCol & 0xff); // R |
|
ret.push((nextCol & 0xff00) >> 8); // G |
|
ret.push((nextCol & 0xff0000) >> 16); // B |
|
|
|
nextCol += _step; |
|
} |
|
var col = "rgb(" + ret.join(",") + ")"; |
|
return col; |
|
}; |
|
fn.step = function (v) { |
|
if (!v || v < 0) { return _step; } |
|
_step = v; |
|
return fn; |
|
}; |
|
fn.clear = function () { |
|
nextCol = 1; |
|
return fn; |
|
}; |
|
return fn; |
|
})(); |
|
|
|
var customNode = document.createElement("custom:cc"), |
|
dataContainer = d3.select(customNode), |
|
// A shallow copy of offscreen canvas that has the same dimmension |
|
hCanvas = canvas.cloneNode(), |
|
hCtx = hCanvas.getContext("2d"), |
|
img = d3.select("img").attr("width", w).attr("height", h), |
|
circleMap = {}, |
|
p = { |
|
count: 200, |
|
canvasImg: function () { |
|
img.attr("src", hCanvas.toDataURL()); |
|
}, |
|
data: genCircles(this.num) |
|
}; |
|
|
|
function genCircles (n) { |
|
if (!n) { n = 100; } |
|
genColor.clear(); |
|
return d3.range(n) |
|
.map(function (d, i) { |
|
var color = genColor(), |
|
node = { |
|
x: Math.random() * w, |
|
y: Math.random() * h, |
|
r: Math.random() * 10 + 3, |
|
_fill: color |
|
}; |
|
circleMap[color] = node; |
|
return node; |
|
}); |
|
} |
|
|
|
var gui = new dat.GUI(); |
|
gui.add(p, "count", 100, 10000) |
|
.step(100) |
|
.onChange(function (n) { |
|
p.data = genCircles(n); |
|
drawCustom(); |
|
}); |
|
gui.add(p, "canvasImg"); |
|
|
|
function drawCustom () { |
|
var u = dataContainer.selectAll("c").data(p.data); |
|
u.enter() |
|
.append("c") |
|
.attr("r", d => d.r) |
|
.attr("x", Math.random() * w) |
|
.attr("y", Math.random() * h) |
|
.merge(u) |
|
.transition() |
|
.duration(2000) |
|
.attr("x", d => d.x) |
|
.attr("y", d => d.y) |
|
.attr("r", d => d.r); |
|
|
|
u.exit().transition() |
|
.duration(2000) |
|
.attr("r", 0) |
|
.remove(); |
|
} |
|
|
|
|
|
function getCircleByColor (pos) { |
|
var rgb = hCtx.getImageData(pos[0], pos[1], 1, 1).data; |
|
rgb = "rgb(" + rgb.slice(0, 3).join(",") + ")"; // avoid alpha charnnel |
|
return circleMap[rgb]; |
|
} |
|
function drawCanvas (ctx) { |
|
ctx.save(); |
|
ctx.clearRect(0, 0, w, h); |
|
ctx.lineWidth = 1; |
|
ctx.strokeStyle = "#335"; |
|
|
|
// pick a color spot and detect which circle under this spot. |
|
var t = getCircleByColor(pos); |
|
ctx.fillStyle = "rgba(70, 130, 180, 0.5)"; |
|
dataContainer.selectAll("c").each(function (d) { |
|
var x = +this.getAttribute("x"), |
|
y = +this.getAttribute("y"), |
|
r = +this.getAttribute("r"); |
|
ctx.beginPath(); |
|
ctx.arc(x, y, r, 0, Math.PI * 2, false); |
|
ctx.fill(); |
|
ctx.stroke(); |
|
}); |
|
|
|
if (t) { |
|
ctx.beginPath(); |
|
ctx.arc(t.x, t.y, t.r, 0, Math.PI * 2, false); |
|
ctx.fillStyle = "red"; |
|
ctx.fill(); |
|
} |
|
|
|
ctx.restore(); |
|
} |
|
|
|
function drawHiddenCanvas (ctx) { |
|
ctx.save(); |
|
ctx.clearRect(0, 0, w, h); |
|
dataContainer.selectAll("c").each(function (d) { |
|
var x = +this.getAttribute("x"), |
|
y = +this.getAttribute("y"), |
|
r = +this.getAttribute("r"); |
|
ctx.fillStyle = d._fill; |
|
ctx.beginPath(); |
|
ctx.arc(x, y, r, 0, Math.PI * 2, false); |
|
ctx.fill(); |
|
}); |
|
ctx.restore(); |
|
} |
|
|
|
function imageText (ctx) { |
|
ctx.save(); |
|
ctx.fillStyle = "gray"; |
|
ctx.font = "50px fantasy"; |
|
ctx.textAlign = "center"; |
|
ctx.textBaseline = "middle"; |
|
ctx.fillText("Click \"canvasImg\" to see", w / 2, 120); |
|
ctx.fillText("what the offscreen canvas looks like", w / 2, 170); |
|
ctx.restore(); |
|
} |
|
|
|
// Performance Monitor |
|
var stats = new Stats(); |
|
stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom |
|
document.body.appendChild( stats.dom ); |
|
|
|
|
|
var cb = function () { |
|
stats.begin(); |
|
drawHiddenCanvas(hCtx); |
|
drawCanvas(ctx); |
|
stats.end(); |
|
}; |
|
|
|
|
|
imageText(hCtx); |
|
p.canvasImg(); |
|
|
|
|
|
drawCustom(); |
|
d3.timer(cb); |
|
|
|
|
|
d3.select(window.parent.document.querySelector("iframe")) |
|
.style("height", "620px"); |
|
</script> |
|
</body> |