var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
var fontSize=12;
var fontFace='verdana';
var dotRadius=3;
var legendX=350;
var legendY=0;
var legendYincrement=10;
var labels=[];
var nextId=0;
ctx.textAlign='left';
ctx.textBaseline='top';
ctx.font='10px arial';
ctx.strokeRect(legendX-5,0,cw-legendX+5,ch);
ctx.fillText('Other labels',legendX-3,legendY+2);
legendY+=legendYincrement;
ctx.fillText('(Color Coded)',legendX-3,legendY+2);
legendY+=legendYincrement;
var label=addLabel('Label #0',cw/2,ch/2,fontSize,fontFace,dotRadius);
drawLabel(label);
$("#canvas").mousedown(function(e){handleMouseDown(e);});
//
function addLabel(text,dotX,dotY,fontsize,fontface,dotRadius){
var font=fontsize+'px '+fontface;
ctx.font=font;
var w=ctx.measureText(text).width;
var h=fontsize*1.286;
var label={
id:nextId++,
text:text,
x:dotX-w/2,
y:dotY-dotRadius-h,
w:w,
h:h,
offsetY:0,
font:font,
isColliding:false,
dotRadius:dotRadius,
dotX:dotX,
dotY:dotY,
};
labels.push(label);
// try to position this new label in a non-colliding position
var positions=[
{ x:dotX-w/2, y:dotY-dotRadius-h }, // N
{ x:dotX+dotRadius, y:dotY-h/2 }, // E
{ x:dotX-w/2, y:dotY+dotRadius }, // S
{ x:dotX-dotRadius-w, y:dotY-h/2 }, // W
];
for(var i=0;i<positions.length;i++){
var p=positions[i];
label.x=p.x;
label.y=p.y;
label.isColliding=thisLabelCollides(label);
if(!label.isColliding){ break; }
}
//
return(label);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
var x=parseInt(e.clientX-offsetX);
var y=parseInt(e.clientY-offsetY);
var label=addLabel('Label #'+nextId,x,y,fontSize,fontFace,dotRadius)
drawLabel(label);
}
//
function drawLabel(label){
ctx.textAlign='left';
ctx.textBaseline='top';
if(label.isColliding){
legendY+=legendYincrement;
ctx.beginPath();
ctx.arc(legendX,legendY,3,0,Math.PI*2);
ctx.fillStyle=randomColor();
ctx.fill();
ctx.font='10px arial';
ctx.fillText(label.text,legendX+5,legendY-5);
}else{
ctx.font=label.font;
ctx.fillStyle='black';
ctx.fillText(label.text,label.x,label.y)
ctx.strokeRect(label.x,label.y,label.w,label.h);
}
ctx.beginPath();
ctx.arc(label.dotX,label.dotY,label.dotRadius,0,Math.PI*2);
ctx.fill();
}
//
function thisLabelCollides(r1){
for(var i=0;i<labels.length;i++){
var r2=labels[i];
if(r1.id==r2.id || r2.isColliding){continue;}
var collides=(!(
r1.x > r2.x+r2.w ||
r1.x+r1.w < r2.x ||
r1.y > r2.y+r2.h ||
r1.y+r1.h < r2.y
));
if(collides){return(true);}
}
return(false);
}
//
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click on the canvas to add more map labels.</h4>
<canvas id="canvas" width=450 height=300></canvas>
就問這樣[這](HTTP類似的問題://gamedev.stackexchange .com/questions/110044/how-are-loot-box-labels-placed)昨晚。我猜這是他們使用的同樣類型的算法,除了新的職位是動態生成的。我會玩這個。 –