2015-08-20 86 views
0

我正在開發一種將文本從數據庫呈現到畫布上特定座標的映射軟件。目標是呈現的文本不會彼此踩踏(不重疊),但仍然沿着它應該顯示的座標。這個想法是,如果渲染的文本重疊,程序可能會選擇以某個角度顯示它。目前,我通過下面的代碼繪製文本:Javascript在canvas中呈現文本並避免文本碰撞

create_point:function(x,y,stitle){ 
     var canvas = document.getElementById('text-layer'); 
     var context = canvas.getContext('2d'); 
     context.fillText(stitle,x,y); // text and position 
     context.save(); 
} 

在這個任何想法?

在此先感謝:-)

回答

2

有趣的益智心態!

問題

您已經繪製座標(文字標籤)從數據庫中偶爾2個或更多座標是如此之近,他們在一起的文本標籤相交(導致其文本標籤不可讀)。

一種解決方案

對於每個新的文本標籤要在地圖上繪製:

  1. 假設每個新的文本標籤是在地圖上的頂側要繪製座標。測試新標籤是否會覆蓋任何現有標籤。

    • 如果不發生覆蓋,請在頂端繪製(並且您已完成此標籤)。
    • 如果頂側會導致覆蓋,繼續步驟2
  2. 重複步驟#1假設新的標籤將是對右側的地圖座標的

  3. 假設新標籤位於地圖座標的底部的上,重複第1步。

  4. 重複步驟1,假定新標籤位於地圖座標的左側

如果以上所有4個步驟都失敗,則不能在不覆蓋現有標籤的情況下繪製該文本標籤。

鑑於失敗,您必須決定是否爲用戶提供您的文本標籤信息。

這些選項浮現腦海:

  • 在地圖上繪製一個小標記,用戶可以將鼠標懸停在並查看與文本標籤信息的彈出提示。這是處理不適合頁面的信息的常見方式。

  • 在地圖上繪製一個小標記,將用戶引用到包含文本標籤信息的單獨圖例。

  • 用箭頭線在地圖上繪製一個小標記,該箭頭線將用戶引導到在地圖上繪製但距地圖座標更遠的文本標籤。

  • 不要包含這個新標籤!這個新標籤可能不像現有地圖標籤那麼重要,因此可能會被忽略。您可以通過按重要性排序地圖數據庫來輕鬆實現此目的。

這裏是演示說明了這一解決方案

enter image description here

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>

+0

就問這樣[這](HTTP類似的問題://gamedev.stackexchange .com/questions/110044/how-are-loot-box-labels-placed)昨晚。我猜這是他們使用的同樣類型的算法,除了新的職位是動態生成的。我會玩這個。 –