了解canvas clip()如何影响多个动态创建的形状

时间:2014-11-26 14:47:35

标签: javascript html5 canvas

我已经在我的第一个真正的HTML5画布项目上工作了一段时间,而且我似乎无法按照我的意愿来排列。我最近遇到了context.clip()函数,它似乎有所帮助,但还有其他意想不到的后果,所以我想我会把问题放在那里。

我的项目是六边形瓷砖地图,边框代表大陆和旅行路线。这是我的地图的好照片:main map

我注意到的是,如果我在绘制每个十六进制之前使用clip()函数,那么内部浅绿色的六边形似乎不会偏离中心。以下是特写比较:compare

问题是,当我使用这个clip()函数时,我似乎丢失了我的边框/路由覆盖。我的代码绘制了所有的六边形,然后绘制了粗边框,这样它们就不会被绘制在上面。 clipping kills my borders

我不是百分百肯定我明白这里发生了什么。为什么clip()会杀死我的边界?我的整个代码列表都在这个github repo:https://github.com/boknows/hex-map-game上,但我将在这里列出两个主要功能。 drawHexGrid()进行数学运算以放置每个单独的十六进制,drawHex()使用正确的填充绘制十六进制,drawHexBorders()绘制后面的边框/路径。

HexagonGrid.prototype.drawHexGrid = function (rows, cols, originX, originY, isDebug) {
this.canvasOriginX = originX;
this.canvasOriginY = originY;
this.rows = rows;
this.cols = cols;
var currentHexX;
var currentHexY;
var debugText = "";
var offsetColumn = false;
var hexNum = 1;

for (var col = 0; col < cols; col++) {
    for (var row = 0; row < rows; row++) {
        if (!offsetColumn) {
            currentHexX = (col * this.side) + originX;
            currentHexY = (row * this.height) + originY;
        } else {
            currentHexX = col * this.side + originX;
            currentHexY = (row * this.height) + originY + (this.height * 0.5);
        }
        if (isDebug) {
            debugText = hexNum;
            hexNum++;
        }
        if(map.data[row][col].type=="land"){  
            this.drawHex(currentHexX, currentHexY, "#99CC66", debugText, false, map.data[row][col].owner);
        }else if(map.data[row][col].type=="water"){
            this.drawHex(currentHexX, currentHexY, "#3333FF", "", false, map.data[row][col].owner);
        }else if(map.data[row][col].type=="forest"){
            this.drawHex(currentHexX, currentHexY, "#009900", debugText, false, map.data[row][col].owner);
        }else if(map.data[row][col].type=="desert"){
            this.drawHex(currentHexX, currentHexY, "#F5E8C1", debugText, false, map.data[row][col].owner);
        }else if(map.data[row][col].type=="mountains"){
            this.drawHex(currentHexX, currentHexY, "#996600", debugText, false, map.data[row][col].owner);
        }   
    }
    offsetColumn = !offsetColumn;

}
var offsetColumn = false;
for (var col = 0; col < cols; col++) { //Draw borders separately so they don't get overlapped by other graphics. 
    for (var row = 0; row < rows; row++) {
        if (!offsetColumn) {
            currentHexX = (col * this.side) + originX;
            currentHexY = (row * this.height) + originY;
        } else {
            currentHexX = col * this.side + originX;
            currentHexY = (row * this.height) + originY + (this.height * 0.5);
        }
        this.drawHexBorders(currentHexX, currentHexY);
    }
    offsetColumn = !offsetColumn;
}
};

HexagonGrid.prototype.drawHex = function (x0, y0, fillColor, debugText, highlight, highlightColor, owner) {  
this.context.font="bold 12px Helvetica";
this.owner = owner;
this.context.strokeStyle = "#000000";
this.context.lineWidth = 1;
this.context.lineCap='round';

this.context.restore();
var tile = this.getSelectedTile(x0 + this.width - this.side, y0);
var numberOfSides = 6,
size = this.radius,
Xcenter = x0 + (this.width / 2),
Ycenter = y0 + (this.height / 2);
this.context.beginPath();
this.context.lineWidth = 1;
this.context.moveTo (Xcenter +  size * Math.cos(0), Ycenter +  size *  Math.sin(0));          
for (var i = 1; i <= numberOfSides;i += 1) {
    this.context.lineTo (Xcenter + size * Math.cos(i * 2 * Math.PI / numberOfSides), Ycenter + size * Math.sin(i * 2 * Math.PI / numberOfSides));
}

if(typeof(map.data[tile.row][tile.column]) != "undefined"){
    if (fillColor && highlight == false && map.data[tile.row][tile.column].type =="land") {
        this.context.fillStyle = map.data[tile.row][tile.column].color;
    }else{
        this.context.fillStyle = fillColor;
    }
}


if (highlight == true){
    this.context.fillStyle = highlightColor;
}
this.context.fill();
this.context.closePath();
this.context.save();
this.context.clip();
this.context.lineWidth *= 2;
this.context.stroke();


if(map.data[tile.row][tile.column].type != "water"){
    //Draw smaller hex inside bigger hex - v2
    var numberOfSides = 6,
    size = this.radius*0.7,
    Xcenter = x0 + (this.width / 2),
    Ycenter = y0 + (this.height / 2);
    this.context.fillStyle = fillColor;
    this.context.strokeStyle = map.data[tile.row][tile.column].color;
    this.context.beginPath();
    this.context.lineWidth = .5;
    this.context.moveTo (Xcenter +  size * Math.cos(0), Ycenter +  size *  Math.sin(0));          
    for (var i = 1; i <= numberOfSides;i += 1) {
        this.context.lineTo (Xcenter + size * Math.cos(i * 2 * Math.PI / numberOfSides), Ycenter + size * Math.sin(i * 2 * Math.PI / numberOfSides));
    }
    this.context.fill();
    this.context.closePath();
    this.context.stroke();

    //if defensive boost active, draw grey dotted hex inside of owners colored hex.
    var index = 0;
    for(var i=0;i<map.dataProp.users.length;i++){
        if(map.dataProp.users[i]==map.data[tile.row][tile.column].owner){
            index = i;
        }
    }
    var defTrigger = false;
    for(var i=0;i<map.dataProp.turnModifiers[index].length;i++){
        if(map.dataProp.turnModifiers[index][i].type=="defensiveBoost"){
            defTrigger = true;
        }
    }
    if(defTrigger == true){
        var numberOfSides = 6,
        size = this.radius-12,
        Xcenter = x0 + (this.width / 2),
        Ycenter = y0 + (this.height / 2);
        this.context.strokeStyle = "#929292"
        this.context.beginPath();
        this.context.lineWidth = 5;
        this.context.moveTo (Xcenter +  size * Math.cos(0), Ycenter +  size *  Math.sin(0));          
        for (var i = 1; i <= numberOfSides;i += 1) {
            this.context.lineTo (Xcenter + size * Math.cos(i * 2 * Math.PI / numberOfSides), Ycenter + size * Math.sin(i * 2 * Math.PI / numberOfSides));
        }
        this.context.fill();
        this.context.closePath();
        this.context.stroke();
    }


    //Print number of units
    this.context.textAlign="center"; 
    this.context.textBaseline = "middle";
    this.context.font = 'bold 13pt Arial';
    //Code for contrasting text with background color
    /*var clr = getContrastYIQ(map.data[tile.row][tile.column].color); //contrast against player color 
    var clr = getContrastYIQ(fillColor); //contrast against land color (fillColor)
    this.context.fillStyle = clr;
    */
    this.context.fillStyle = "#000000";
    this.context.fillText(map.data[tile.row][tile.column].units, x0 + (this.width / 2) , y0 + (this.height / 2));
    this.context.fillStyle = "";
}
};

HexagonGrid.prototype.drawHexBorders = function (x0, y0) {  
var tile = this.getSelectedTile(x0 + this.width - this.side, y0);
if(map.data[tile.row][tile.column].s != ""){
    this.context.beginPath();
    this.context.lineWidth = 5;
    this.context.strokeStyle=map.data[tile.row][tile.column].s;
    this.context.moveTo(x0 + this.side, y0 + this.height);
    this.context.lineTo(x0 + this.width - this.side, y0 + this.height);
    this.context.stroke();
}
if(map.data[tile.row][tile.column].n != ""){

    this.context.beginPath();
    this.context.lineWidth = 5;
    this.context.strokeStyle=map.data[tile.row][tile.column].n;
    this.context.moveTo(x0 + this.side, y0);
    this.context.lineTo(x0 + this.width - this.side, y0);
    this.context.stroke();
}
if(map.data[tile.row][tile.column].ne != ""){
    this.context.beginPath();
    this.context.lineWidth = 5;
    this.context.strokeStyle=map.data[tile.row][tile.column].ne;
    this.context.moveTo(x0 + this.side, y0);
    this.context.lineTo(x0 + this.width, y0 + (this.height / 2));
    this.context.stroke();
}
if(map.data[tile.row][tile.column].se != ""){
    this.context.beginPath();
    this.context.lineWidth = 5;
    this.context.strokeStyle=map.data[tile.row][tile.column].se;
    this.context.moveTo(x0 + this.width, y0 + (this.height / 2));
    this.context.lineTo(x0 + this.side, y0 + this.height);
    this.context.stroke();
}
if(map.data[tile.row][tile.column].sw != ""){
    this.context.beginPath();
    this.context.lineWidth = 5;
    this.context.strokeStyle=map.data[tile.row][tile.column].sw;
    this.context.moveTo(x0 + this.width - this.side, y0 + this.height);
    this.context.lineTo(x0, y0 + (this.height/2));
    this.context.stroke();
}
if(map.data[tile.row][tile.column].nw != ""){
    this.context.beginPath();
    this.context.lineWidth = 5;
    this.context.strokeStyle=map.data[tile.row][tile.column].nw;
    this.context.moveTo(x0, y0 + (this.height/2));
    this.context.lineTo(x0 + this.width - this.side, y0);
    this.context.stroke();
}
};

//Recusivly step up to the body to calculate canvas offset.
HexagonGrid.prototype.getRelativeCanvasOffset = function() {
var x = 0, y = 0;
var layoutElement = this.canvas;
var bound = layoutElement.getBoundingClientRect();
if (layoutElement.offsetParent) {
    do {
        x += layoutElement.offsetLeft;
        y += layoutElement.offsetTop;
    } while (layoutElement = layoutElement.offsetParent);

    return { x: bound.left, y: bound.top };
}
}

1 个答案:

答案 0 :(得分:1)

免责声明:我没有查看您的代码 - 您发布了大量代码! (Stackoverflow鼓励您发布一个简化代码段来说明您的问题)。

但你的问题可能是由于笔画的完成方式吗?

笔画是半内部和外部完成的。在他们定义的路径之外的一半。因此,如果你划动一个矩形然后clearRect同一个矩形,你仍然会看到部分笔划 - 你会看到笔划的半外部分。

// draw a stroked rect
context.strokeRect(10,10,30,30);

// clear the same rect
context.clearRect(10,10,30,30);

// the outside half of the stroke is still visible

相反,裁剪将阻止绘制笔画的半外部分。也许你缺少的十六进制边框是没有绘制的半外部笔划。