HTML5遊戲開發(六)

HTML5遊戲開發(六)

一、多邊形繪製

(一)多邊形旋轉

1、基本功能

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>拖動與旋轉多邊形</title>
        <style>
            body {
                background: #eeeeee;
            }
            #dragDiv {
                position: absolute;
                left: 25px;
                top: 50px;
            }
            #controls {
                position: absolute;
                left: 25px;
                top: 25px;
            }
            #canvas {
                background: #ffffff;
                cursor: crosshair;
                margin-left: 10px;
                margin-top: 10px;
                -webkit-box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5);
                -moz-box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5);
                box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5);
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='850' height='500'>
     </canvas>
        <div id='controls'>
            邊框顏色:
            <select id='strokeStyleSelect'>
                <option value='red'></option>
                <option value='green'></option>
                <option value='blue'></option>
                <option value='orange'></option>
                <option value='cornflowerblue' selected>菊藍</option>
                <option value='goldenrod'> 鮮黃</option>
                <option value='navy'>深藍</option>
                <option value='purple'>紫色</option>
            </select>
            填充色:
            <select id='fillStyleSelect'>
                <option value='rgba(255,0,0,0.5)'>半透明 紅</option>
                <option value='green'></option>
                <option value='rgba(0,0,255,0.5)'>半透明 藍</option>
                <option value='orange'></option>
                <option value='rgba(100,140,230,0.5)'>半透明 菊藍</option>
                <option value='goldenrod' selected>鮮黃</option>
                <option value='navy'>深藍</option>
                <option value='purple'>紫色</option>
            </select>
            邊數:
            <select id='sidesSelect'>
                <option value=4 select>4</option>
                <option value=6>6</option>
                <option value=8>8</option>
                <option value=10>10</option>
                <option value=12>12</option>
                <option value=20>20</option>
            </select>
            起始角度:
            <select id='startAngleSelect'>
                <option value=0 select>0</option>
                <option value=22.5>22.5</option>
                <option value=45>45</option>
                <option value=67.5>67.5</option>
                <option value=90>90</option>
            </select>
            填充: <input id='fillCheckbox' type='checkbox' checked/>
            <input id='eraseAllButton' type='button' value='擦除所有' />
        </div>
        <div id='dragDiv'>
            旋轉: <input type='checkbox' id='rotatingCheckbox' />
        </div>
        <script src='js/polygon.js'></script>
    </body>
</html>

JS腳本

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    eraseAllButton = document.getElementById('eraseAllButton'),
    strokeStyleSelect = document.getElementById('strokeStyleSelect'),
    startAngleSelect = document.getElementById('startAngleSelect'),
    fillStyleSelect = document.getElementById('fillStyleSelect'),
    fillCheckbox = document.getElementById('fillCheckbox'),
    dragCheckbox = document.getElementById('dragCheckbox'),
    sidesSelect = document.getElementById('sidesSelect'),

    CENTROID_RADIUS = 10, //中心圓點
    CENTROID_STROKE_STYLE = 'rgba(0, 0, 0, 0.8)', //中心圓點樣式
    CENTROID_FILL_STYLE = 'rgba(255, 255, 255, 0.2)', //中心圓點填充色 
    CENTROID_SHADOW_COLOR = 'rgba(255, 255, 255, 0.4)', //中心圓點陰影色

    DEGREE_RING_MARGIN = 35, //數字標記間隔
    TRACKING_RING_MARGIN = 55, //標記線間隔
    DEGREE_ANNOTATIONS_FILL_STYLE = 'rgba(0, 0, 230, 0.8)', //數字標記樣式
    DEGREE_ANNOTATIONS_TEXT_SIZE = 11, //數字標記
    DEGREE_OUTER_RING_MARGIN = DEGREE_RING_MARGIN, //外圓間距
    TICK_WIDTH = 10, //標記線寬度
    TICK_LONG_STROKE_STYLE = 'rgba(100, 140, 230, 0.9)', //標記線長線樣式
    TICK_SHORT_STROKE_STYLE = 'rgba(100, 140, 230, 0.7)', //標記線短線樣式

    TRACKING_RING_STROKING_STYLE = 'rgba(100, 140, 230, 0.3)', //標記線樣式
    drawingSurfaceImageData, //圖形數據

    mousedown = {}, //光標
    rubberbandRect = {}, //橡皮筋

    dragging = false, //是否可拖拽
    draggingOffsetX, //拖拽偏移X
    draggingOffsetY, //拖拽偏移Y

    sides = 8, //默認邊數
    startAngle = 0, //默認開始角度

    guidewires = true, //參考線

    rotating = false, //是否可旋轉
    rotatingLockEngaged = false, //是否鎖定旋轉
    rotatingLockAngle, //旋轉角度
    polygonRotating, //多邊形旋轉

    polygons = []; //多邊形

//-----------------------1、基本功能
//背景網格
function drawGrid(color, stepx, stepy) {
    context.save()

    context.shadowColor = undefined;
    context.shadowBlur = 0;
    context.shadowOffsetX = 0;
    context.shadowOffsetY = 0;

    context.strokeStyle = color;
    context.fillStyle = '#ffffff';
    context.lineWidth = 0.5;
    context.fillRect(0, 0, context.canvas.width, context.canvas.height);

    for(var i = stepx + 0.5; i < context.canvas.width; i += stepx) {
        context.beginPath();
        context.moveTo(i, 0);
        context.lineTo(i, context.canvas.height);
        context.stroke();
    }

    for(var i = stepy + 0.5; i < context.canvas.height; i += stepy) {
        context.beginPath();
        context.moveTo(0, i);
        context.lineTo(context.canvas.width, i);
        context.stroke();
    }

    context.restore();
}
//座標轉換
function windowToCanvas(e) {
    var x = e.x || e.clientX,
        y = e.y || e.clientY,
        bbox = canvas.getBoundingClientRect();

    return {
        x: x - bbox.left * (canvas.width / bbox.width),
        y: y - bbox.top * (canvas.height / bbox.height)
    };
}
context.strokeStyle = strokeStyleSelect.value;
context.fillStyle = fillStyleSelect.value;

drawGrid('lightgray', 10, 10);

context.shadowOffsetX = 2;
context.shadowOffsetY = 2;
context.shadowBlur = 4;

context.textAlign = 'center';
context.textBaseline = 'middle';

2、保存與恢復畫布

//--------------------------2、保存與恢復畫布
function saveDrawingSurface() {
    drawingSurfaceImageData = context.getImageData(0, 0,
        canvas.width,
        canvas.height);
}

function restoreDrawingSurface() {
    context.putImageData(drawingSurfaceImageData, 0, 0);
}

3、創建多邊形對象

//-------------------------3、創建多邊形對象
//定義多邊形點
var Point = function(x, y) {
    this.x = x;
    this.y = y;
};
/**
 * 多邊形對象
 * @param {Object} centerX 當前X座標
 * @param {Object} centerY 當前Y座標
 * @param {Object} radius  半徑
 * @param {Object} sides   邊數
 * @param {Object} startAngle  開始角度
 * @param {Object} strokeStyle 邊線樣式
 * @param {Object} fillStyle   填充樣式
 * @param {Object} filled      是否填充
 */
var Polygon = function(centerX, centerY, radius, sides, startAngle, strokeStyle, fillStyle, filled) {
    this.x = centerX;
    this.y = centerY;
    this.radius = radius;
    this.sides = sides;
    this.startAngle = startAngle;
    this.strokeStyle = strokeStyle;
    this.fillStyle = fillStyle;
    this.filled = filled;
};
//多邊形重原型方式重構
Polygon.prototype = {
    //獲取多邊形座標方法
    getPoints: function() {
        var points = [],
            angle = this.startAngle || 0;

        for(var i = 0; i < this.sides; ++i) {
            points.push(new Point(this.x + this.radius * Math.sin(angle),
                this.y - this.radius * Math.cos(angle)));
            angle += 2 * Math.PI / this.sides;
        }
        return points;
    },
    //創建路徑
    createPath: function(context) {
        var points = this.getPoints();
        context.beginPath();
        context.moveTo(points[0].x, points[0].y);
        //根據邊數繪製路徑
        for(var i = 1; i < this.sides; ++i) {
            context.lineTo(points[i].x, points[i].y);
        }
        context.closePath();
    },
    //繪製路徑方法,主要用於保存與恢復原有畫布內容
    stroke: function(context) {
        context.save();
        this.createPath(context);
        context.strokeStyle = this.strokeStyle;
        context.stroke();
        context.restore();
    },
    //填充路徑方法
    fill: function(context) {
        context.save();
        this.createPath(context);
        context.fillStyle = this.fillStyle;
        context.fill();
        context.restore();
    },
    //移動
    move: function(x, y) {
        this.x = x;
        this.y = y;
    },
};

4、橡皮筋繪製

//-------------------------4、橡皮筋繪製
//更新橡皮筋
function updateRubberbandRectangle(loc) {
    rubberbandRect.width = Math.abs(loc.x - mousedown.x);
    rubberbandRect.height = Math.abs(loc.y - mousedown.y);

    if(loc.x > mousedown.x) rubberbandRect.left = mousedown.x;
    else rubberbandRect.left = loc.x;

    if(loc.y > mousedown.y) rubberbandRect.top = mousedown.y;
    else rubberbandRect.top = loc.y;
}
//使用橡皮筋繪製多邊形
function drawRubberbandShape(loc, sides, startAngle) {
    //繪製多邊形
    var polygon = new Polygon(mousedown.x, mousedown.y,
        rubberbandRect.width,
        parseInt(sidesSelect.value),
        (Math.PI / 180) * parseInt(startAngleSelect.value),
        context.strokeStyle,
        context.fillStyle,
        fillCheckbox.checked);
    //繪製多邊形                 
    drawPolygon(polygon);

    if(!dragging) {
        //將多邊形放入數組
        polygons.push(polygon);
    }
}
//橡皮筋更新
function updateRubberband(loc, sides, startAngle) {
    updateRubberbandRectangle(loc);
    drawRubberbandShape(loc, sides, startAngle);
}

5、參考線

//--------------------------------5、參考線
//水平參考線
function drawHorizontalLine(y) {
    context.beginPath();
    context.moveTo(0, y + 0.5);
    context.lineTo(context.canvas.width, y + 0.5);
    context.stroke();
}
//垂直參考線
function drawVerticalLine(x) {
    context.beginPath();
    context.moveTo(x + 0.5, 0);
    context.lineTo(x + 0.5, context.canvas.height);
    context.stroke();
}
//繪製參考線
function drawGuidewires(x, y) {
    context.save();
    context.strokeStyle = 'rgba(0,0,230,0.4)';
    context.lineWidth = 0.5;
    drawVerticalLine(x);
    drawHorizontalLine(y);
    context.restore();
}

6、繪製多邊形

//----------------------------6、繪製多邊形
//繪製所有多邊形
function drawPolygons() {
    polygons.forEach(function(polygon) {
        polygon.stroke(context);
        if(polygon.filled) {
            polygon.fill(context);
        }
    });
}
//繪製中心點
function drawCentroid(polygon) {
    context.beginPath();
    context.save();
    context.strokeStyle = CENTROID_STROKE_STYLE;
    context.fillStyle = CENTROID_FILL_STYLE;
    context.shadowColor = CENTROID_SHADOW_COLOR;
    context.arc(polygon.x, polygon.y, CENTROID_RADIUS, 0, Math.PI * 2, false);
    context.stroke();
    context.fill();
    context.restore();
}
//繪製中心點指示線
function drawCentroidGuidewire(loc, polygon) {
    var angle = Math.atan((loc.y - polygon.y) / (loc.x - polygon.x)),
        radius, endpt;

    radius = polygon.radius + TRACKING_RING_MARGIN;
    angle = angle - rotatingLockAngle;

    if(loc.x >= polygon.x) {
        endpt = {
            x: polygon.x + radius * Math.cos(angle),
            y: polygon.y + radius * Math.sin(angle)
        };
    } else {
        endpt = {
            x: polygon.x - radius * Math.cos(angle),
            y: polygon.y - radius * Math.sin(angle)
        };
    }

    context.save();
    context.beginPath();
    context.moveTo(polygon.x, polygon.y);
    context.lineTo(endpt.x, endpt.y);
    context.stroke();

    context.beginPath();
    context.arc(endpt.x, endpt.y, 5, 0, Math.PI * 2, false);
    context.stroke();
    context.fill();

    context.restore();
}
//繪製外圓
function drawDegreeOuterDial(polygon) {
    context.strokeStyle = 'rgba(0, 0, 0, 0.1)';
    context.arc(polygon.x, polygon.y,
        polygon.radius + DEGREE_OUTER_RING_MARGIN,
        0, Math.PI * 2, true);
}
//繪製數字標記
function drawDegreeAnnotations(polygon) {
    var radius = polygon.radius + DEGREE_RING_MARGIN;

    context.save();
    context.fillStyle = DEGREE_ANNOTATIONS_FILL_STYLE;
    context.font = DEGREE_ANNOTATIONS_TEXT_SIZE + 'px Helvetica';

    for(var angle = 0; angle < 2 * Math.PI; angle += Math.PI / 8) {
        context.beginPath();
        context.fillText((angle * 180 / Math.PI).toFixed(0),
            polygon.x + Math.cos(angle) * (radius - TICK_WIDTH * 2),
            polygon.y + Math.sin(angle) * (radius - TICK_WIDTH * 2));
    }
    context.restore();
}
//繪製數字刻度 
function drawDegreeDialTicks(polygon) {
    var radius = polygon.radius + DEGREE_RING_MARGIN,
        ANGLE_MAX = 2 * Math.PI,
        ANGLE_DELTA = Math.PI / 64;
    context.save();
    for(var angle = 0, cnt = 0; angle < ANGLE_MAX; angle += ANGLE_DELTA, ++cnt) {
        context.beginPath();

        if(cnt % 4 === 0) {
            context.moveTo(polygon.x + Math.cos(angle) * (radius - TICK_WIDTH),
                polygon.y + Math.sin(angle) * (radius - TICK_WIDTH));
            context.lineTo(polygon.x + Math.cos(angle) * (radius),
                polygon.y + Math.sin(angle) * (radius));
            context.strokeStyle = TICK_LONG_STROKE_STYLE;
            context.stroke();
        } else {
            context.moveTo(polygon.x + Math.cos(angle) * (radius - TICK_WIDTH / 2),
                polygon.y + Math.sin(angle) * (radius - TICK_WIDTH / 2));
            context.lineTo(polygon.x + Math.cos(angle) * (radius),
                polygon.y + Math.sin(angle) * (radius));
            context.strokeStyle = TICK_SHORT_STROKE_STYLE;
            context.stroke();
        }
        context.restore();
    }
}
//繪製數字標記
function drawDegreeTickDial(polygon) {
    context.save();
    context.strokeStyle = 'rgba(0, 0, 0, 0.1)';
    context.beginPath();
    context.arc(polygon.x, polygon.y, polygon.radius + DEGREE_RING_MARGIN - TICK_WIDTH, 0, Math.PI * 2, false);
    context.stroke();
    context.restore();
}
//繪製跟蹤數字
function drawTrackingDial(polygon) {
    context.save();
    context.shadowColor = 'rgba(0, 0, 0, 0.7)';
    context.shadowOffsetX = 3,
        context.shadowOffsetY = 3,
        context.shadowBlur = 6,
        context.strokeStyle = TRACKING_RING_STROKING_STYLE;
    context.beginPath();
    context.arc(polygon.x, polygon.y, polygon.radius +
        TRACKING_RING_MARGIN, 0, Math.PI * 2, false);
    context.stroke();
    context.restore();
}
//繪製旋轉數字
function drawRotationAnnotations(loc) {
    drawCentroid(polygonRotating);
    drawCentroidGuidewire(loc, polygonRotating);

    drawTrackingDial(polygonRotating);
    drawDegreeOuterDial(polygonRotating);
    context.fillStyle = 'rgba(100, 140, 230, 0.1)';
    context.fill();

    context.beginPath();
    drawDegreeOuterDial(polygonRotating);
    context.stroke();

    drawDegreeDialTicks(polygonRotating);
    drawDegreeTickDial(polygonRotating);
    drawDegreeAnnotations(polygonRotating);
}
//重繪
function redraw() {
    context.clearRect(0, 0, canvas.width, canvas.height);
    drawGrid('lightgray', 10, 10);
    drawPolygons();
}

7、繪製多邊形

//---------------------7、繪製多邊形
//繪製多邊形,參數爲多邊形與角度
function drawPolygon(polygon, angle) {
    var tx = polygon.x,
        ty = polygon.y;
    context.save();
    context.translate(tx, ty);
    if(angle) {
        context.rotate(angle);
    }

    polygon.x = 0;
    polygon.y = 0;

    polygon.createPath(context);
    context.stroke();

    if(fillCheckbox.checked) {
        context.fill();
    }

    context.restore();

    polygon.x = tx;
    polygon.y = ty;
}
//獲取多邊形
function getSelectedPolygon(loc) {
    for(var i = 0; i < polygons.length; ++i) {
        var polygon = polygons[i];

        polygon.createPath(context);
        if(context.isPointInPath(loc.x, loc.y)) {
            startDragging(loc);
            draggingOffsetX = loc.x - polygon.x;
            draggingOffsetY = loc.y - polygon.y;
            return polygon;
        }
    }
    return undefined;
}
//停止多邊形旋轉
function stopRotatingPolygon(loc) {
    angle = Math.atan((loc.y - polygonRotating.y) /
            (loc.x - polygonRotating.x)) -
        rotatingLockAngle;

    polygonRotating.startAngle += angle;

    polygonRotating = undefined;
    rotatingLockEngaged = false;
    rotatingLockAngle = 0;
}
//停止拖拽
function startDragging(loc) {
    saveDrawingSurface();
    mousedown.x = loc.x;
    mousedown.y = loc.y;
}

8、事件處理

//---------------------------------8、事件處理
canvas.onmousedown = function(e) {
    var loc = windowToCanvas(e),
        angle,
        radius,
        trackingRadius;

    e.preventDefault(); 
    //啓用選轉
    if(rotating) {
        //如果polygonRotating獲取到了值,則結束旋轉
        if(polygonRotating) {
            //停止旋轉
            stopRotatingPolygon(loc);
            //重繪新產生的多邊形
            redraw();
            //重置繪製樣式,實際是恢復樣式,因爲在重繪時,進行了改變爲儀表盤中心點的填充色
            context.strokeStyle = strokeStyleSelect.value;
            context.fillStyle = fillStyleSelect.value;
        }
        //獲了多邊形座標
        polygonRotating = getSelectedPolygon(loc);

        if(polygonRotating) {
            //繪製數字標記
            drawRotationAnnotations(loc);

            //按下時,記住原有的旋轉角度
            if(!rotatingLockEngaged) {
                rotatingLockEngaged = true;
                //獲取旋轉角度
                rotatingLockAngle = Math.atan((loc.y - polygonRotating.y) /
                    (loc.x - polygonRotating.x));
            }
        }
    } else {
        //開始拖拽,獲取多邊形座標
        startDragging(loc);
        //拖拽中--改變狀態,使鼠標移動獲取角度
        dragging = true;
    }
};
//鼠標移動事件
canvas.onmousemove = function(e) {
    var loc = windowToCanvas(e),
        //計算半徑
        radius = Math.sqrt(Math.pow(loc.x - dragging.x, 2) +
            Math.pow(loc.y - dragging.y, 2)),
        angle;

    e.preventDefault();
    //旋轉
    if(rotatingLockEngaged) {
        //獲取角度
        angle = Math.atan((loc.y - polygonRotating.y) /
                (loc.x - polygonRotating.x)) -
            rotatingLockAngle;
        //不斷重繪,在進行重繪時,會改變多邊形的填充色,注意:繪製完畢進行恢復
        redraw();
        //繪製多邊形 
        drawPolygon(polygonRotating, angle);
        //繪製旋轉數字標記
        drawRotationAnnotations(loc);
    } else if(dragging) {
        //普通繪製
        restoreDrawingSurface();
        //更新橡皮筋
        updateRubberband(loc, sides, startAngle);
        //啓用參考線
        if(guidewires) {
            drawGuidewires(mousedown.x, mousedown.y);
        }
    }
};
//鼠標釋放事件
canvas.onmouseup = function(e) {
    //座標轉換
    var loc = windowToCanvas(e);
    dragging = false;
    //可旋轉
    if(!rotating) {
        restoreDrawingSurface();
        //更新橡皮筋
        updateRubberband(loc);
    }
};

9、控制事件處理

//----------------------------------------9、控制事件處理
//擦除所有
eraseAllButton.onclick = function(e) {
    //清空多邊形數組
    polygons.length = 0;
    context.clearRect(0, 0, canvas.width, canvas.height);
    drawGrid('lightgray', 10, 10);
    saveDrawingSurface();
};
//邊框顏色事件
strokeStyleSelect.onchange = function(e) {
    context.strokeStyle = strokeStyleSelect.value;
};
//可填充顏色事件
fillStyleSelect.onchange = function(e) {
    context.fillStyle = fillStyleSelect.value;
};
//開始旋轉
function startRotating() {
    //改變鼠標光標指針 爲小手
    canvas.style.cursor = 'pointer';
    rotating = true;
}
//停止旋轉
function stopRotating() {
    //改變鼠標光標指針 爲十字
    canvas.style.cursor = 'crosshair';
    rotating = false;
    polygonRotating = undefined;
    rotatingLockEngaged = false;
    rotatingLockAngle = 0;
    //清除畫布
    context.clearRect(0, 0, canvas.width, canvas.height);
    //繪製網格
    drawGrid('lightgray', 10, 10);
    //繪製旋轉後的多邊形
    drawPolygons();
}
//可旋轉事件
rotatingCheckbox.onchange = function(e) {
    if(rotatingCheckbox.checked) {
        //開始選轉
        startRotating();
    } else {
        //停止旋轉
        stopRotating();
    }
};

顯示效果:
iamge

(二)多邊形拖動

1、畫布保存與恢復

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>多邊形拖動</title>
        <style>
            body {
                background: #283c3c;
            }

            #canvas {
                background: #f0fff0;
                border: thin solid #FFFFCC;
            }

            #dragDiv {
                position: absolute;
                left: 25px;
                top: 25px;
            }
        </style>
    </head>
    <body>
        <div id='dragDiv'>
            拖拽: <input type='checkbox' id='editCheckbox' />
        </div>
        <canvas id='canvas' width='600' height='500'>
    </canvas>
        <script src="js/dragPolygon.js"></script>
    </body>
</html>

JS腳本:

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    editCheckbox = document.getElementById('editCheckbox'),
    drawingSurfaceImageData,
    mousedown = {},
    rubberbandRect = {},
    dragging = false,
    draggingOffsetX,
    draggingOffsetY,
    sides = 5,          //邊數
    startAngle = 0,
    guidewires = true,
    editing = false,
    polygons = [];
 //顏色設置   
context.strokeStyle = "#dcb478";
context.fillStyle = "#b48c50";
//陰影設置
context.shadowOffsetX = 2;
context.shadowOffsetY = 2;
context.shadowBlur = 4;
//座標轉換
function windowToCanvas(x, y) {
   var bbox = canvas.getBoundingClientRect();
   return { x: x - bbox.left * (canvas.width  / bbox.width),
            y: y - bbox.top  * (canvas.height / bbox.height)
          };
}

 //-------------------1、畫布保存與恢復   
function saveDrawingSurface() {
   drawingSurfaceImageData = context.getImageData(0, 0,
                             canvas.width,
                             canvas.height);
}

function restoreDrawingSurface() {
   context.putImageData(drawingSurfaceImageData, 0, 0);
}

2、多邊形繪製

//--------------------------------2、多邊形繪製
//定義多邊形點
var Point = function(x, y) {
    this.x = x;
    this.y = y;
};
/**
 * 多邊形對象
 * @param {Object} centerX 當前X座標
 * @param {Object} centerY 當前Y座標
 * @param {Object} radius  半徑
 * @param {Object} sides   邊數
 * @param {Object} startAngle  開始角度
 * @param {Object} strokeStyle 邊線樣式
 * @param {Object} fillStyle   填充樣式
 * @param {Object} filled      是否填充
 */
var Polygon = function(centerX, centerY, radius, sides, startAngle, strokeStyle, fillStyle, filled) {
    this.x = centerX;
    this.y = centerY;
    this.radius = radius;
    this.sides = sides;
    this.startAngle = startAngle;
    this.strokeStyle = strokeStyle;
    this.fillStyle = fillStyle;
    this.filled = filled;
};
//多邊形重原型方式重構
Polygon.prototype = {
    //獲取多邊形座標方法
    getPoints: function() {
        var points = [],
            angle = this.startAngle || 0;

        for(var i = 0; i < this.sides; ++i) {
            points.push(new Point(this.x + this.radius * Math.sin(angle),
                this.y - this.radius * Math.cos(angle)));
            angle += 2 * Math.PI / this.sides;
        }
        return points;
    },
    //創建路徑
    createPath: function(context) {
        var points = this.getPoints();
        context.beginPath();
        //繪製起點
        context.moveTo(points[0].x, points[0].y);
        //根據邊數繪製路徑
        for(var i = 1; i < this.sides; ++i) {
            context.lineTo(points[i].x, points[i].y);
        }
        context.closePath();
    },
    //繪製路徑方法,主要用於保存與恢復原有畫布內容
    stroke: function(context) {
        context.save();
        this.createPath(context);
        context.strokeStyle = this.strokeStyle;
        context.stroke();
        context.restore();
    },
    //填充路徑方法
    fill: function(context) {
        context.save();
        this.createPath(context);
        context.fillStyle = this.fillStyle;
        context.fill();
        context.restore();
    },
    //移動
    move: function(x, y) {
        this.x = x;
        this.y = y;
    },
};

//繪製多邊形
function drawPolygon(polygon) {
   context.beginPath();
   polygon.createPath(context);
   polygon.stroke(context);
   polygon.fill(context);

}

3、橡皮筋繪製

//------------------------3、橡皮筋繪製
function updateRubberbandRectangle(loc) {
   rubberbandRect.width = Math.abs(loc.x - mousedown.x);
   rubberbandRect.height = Math.abs(loc.y - mousedown.y);

   if (loc.x > mousedown.x) rubberbandRect.left = mousedown.x;
   else                     rubberbandRect.left = loc.x;

   if (loc.y > mousedown.y) rubberbandRect.top = mousedown.y;
   else                     rubberbandRect.top = loc.y;
} 

function drawRubberbandShape(loc, sides, startAngle) {
   var polygon = new Polygon(mousedown.x, mousedown.y,
                     rubberbandRect.width, 
                     sides, //對象的邊數         
                     (Math.PI / 180) * startAngle,
                     context.strokeStyle,
                     context.fillStyle,
                     true);
   drawPolygon(polygon);

   if (!dragging) {
      polygons.push(polygon);
   }
}

function updateRubberband(loc, sides, startAngle) {
   //更新橡皮筋
   updateRubberbandRectangle(loc);
   //繪製多邊形
   drawRubberbandShape(loc, sides, startAngle);
}

//--------------------------------------4、繪製參考線
function drawHorizontalLine (y) {
   context.beginPath();
   context.moveTo(0,y+0.5);
   context.lineTo(context.canvas.width,y+0.5);
   context.stroke();
}

function drawVerticalLine (x) {
   context.beginPath();
   context.moveTo(x+0.5,0);
   context.lineTo(x+0.5,context.canvas.height);
   context.stroke();
}

function drawGuidewires(x, y) {
   context.save();
   context.strokeStyle = 'rgba(0,0,230,0.4)';
   context.lineWidth = 0.5;
   drawVerticalLine(x);
   drawHorizontalLine(y);
   context.restore();
}
//繪製所有多邊形
function drawPolygons() {
   polygons.forEach( function (polygon) {
      drawPolygon(polygon);
   });
}

4、繪製參考線

//--------------------------------------4、繪製參考線
function drawHorizontalLine (y) {
   context.beginPath();
   context.moveTo(0,y+0.5);
   context.lineTo(context.canvas.width,y+0.5);
   context.stroke();
}

function drawVerticalLine (x) {
   context.beginPath();
   context.moveTo(x+0.5,0);
   context.lineTo(x+0.5,context.canvas.height);
   context.stroke();
}

function drawGuidewires(x, y) {
   context.save();
   context.strokeStyle = 'rgba(0,0,230,0.4)';
   context.lineWidth = 0.5;
   drawVerticalLine(x);
   drawHorizontalLine(y);
   context.restore();
}
//繪製所有多邊形
function drawPolygons() {
   polygons.forEach( function (polygon) {
      drawPolygon(polygon);
   });
}

5、事件處理

//-----------------------------------------5、事件處理
//--------------------------------拖拽
function startDragging(loc) {
  saveDrawingSurface();
  mousedown.x = loc.x;
  mousedown.y = loc.y;
}

function startEditing() {
   canvas.style.cursor = 'pointer';
   editing = true;
}

function stopEditing() {
   canvas.style.cursor = 'crosshair';
   editing = false;
}

//鼠標按下
canvas.onmousedown = function (e) {
   var loc = windowToCanvas(e.clientX, e.clientY);
   e.preventDefault(); 
   //當拖拽複選框被選中時
   if (editing) {
     //遍歷所有多邊形
     polygons.forEach( function (polygon) {
        //創建多邊形路徑
        polygon.createPath(context);
        //查看路徑是否包含當前的座標
        if (context.isPointInPath(loc.x, loc.y)) {
           startDragging(loc);
           //找到對象指向--數組中的對象進行引用
           dragging = polygon;
           //設置偏移座標
           draggingOffsetX = loc.x - polygon.x;
           draggingOffsetY = loc.y - polygon.y;
           return;
        }
     });
   }
   else {
     //開始拖拽
     startDragging(loc);
     dragging = true;
   }
};
//移動鼠標
canvas.onmousemove = function (e) {
   var loc = windowToCanvas(e.clientX, e.clientY);
   e.preventDefault(); 
   //如果選 中了拖拽,並且新對象不爲空
   if (editing && dragging) {
      //獲取新座標
      dragging.x = loc.x - draggingOffsetX;
      dragging.y = loc.y - draggingOffsetY;
      //重置畫布
      context.clearRect(0, 0, canvas.width, canvas.height);
      //繪製所有多邊形 
      drawPolygons();
   }
   else {
     if (dragging) {
        //恢復畫布
        restoreDrawingSurface();
        //繪製新多邊形
        updateRubberband(loc, sides, startAngle);

        //繪製參考線
        if (guidewires) {
           drawGuidewires(mousedown.x, mousedown.y);
        }
     }
   }
};
//釋放鼠標
canvas.onmouseup = function (e) {
   var loc = windowToCanvas(e.clientX, e.clientY);
   //拖拽狀態設置
   dragging = false;

   if (editing) {

   }else{
     restoreDrawingSurface();
     updateRubberband(loc,sides);
   }
};

6、控制事件

//-------------------------------------6、控制事件
//是否拖拽
editCheckbox.onchange = function (e) {
   if (editCheckbox.checked) {
      startEditing();
   }
   else {
      stopEditing();
   }  
};

顯示效果:
image

二、座標變換

(一)座標的平移、旋轉與縮放

方法 描述
scale() 縮放當前繪圖至更大或更小
rotate() 旋轉當前繪圖
translate() 重新映射畫布上的 (0,0) 位置
transform() 替換繪圖的當前轉換矩陣
setTransform() 將當前轉換重置爲單位矩陣。然後運行 transform()
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>平移</title>
        <style>
            body {
                background: #283c3c;
            }
            #canvas {
                background: #f0fff0;
                border: thin solid #FFFFCC;
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='400' height='500'>
    </canvas>
        <script src="js/moveTranslateScale.js"></script>
    </body>
</html>

JS腳本:

var canvas = document.querySelector("#canvas"),
    context = canvas.getContext('2d'),
    drawX = (canvas.width - 100) / 2,
    drawY = (canvas.height - 100) / 2,
    drawingSurfaceImageData,
    WIDTH = 100,
    HEIGH = 100;

//繪製正方形
function drawRect(x, y) {
    context.strokeRect(x, y, WIDTH, HEIGH);
    context.fillRect(x, y, WIDTH, HEIGH);
    context.stroke();
    context.fill();
}
//進行平移、縮放、旋轉
function drawRectMoveScaleRotate(moveX,moveY) {
    randomColor();
    //平移:translate 進行偏移 參數爲:偏移量
    context.translate(moveX, moveY);

    randomColor();
    //縮放:scale 參數爲縮放倍數
    context.scale(2,2);

    randomColor();
    //旋轉:rotate 參數爲:角度或弧度 ,旋轉45度
    //其中圓心爲:translate 偏移值
    context.rotate(45*Math.PI/180);
    //再次繪製新的矩形 將看到效果
    randomColor();
}
//隨機顏色
function randomColor(){
    var r=Math.floor(Math.random()*255);
    var g=Math.floor(Math.random()*255);
    var b=Math.floor(Math.random()*255);
    context.fillStyle="rgba("+r+", "+g+", "+b+",0.8)";
    setTimeout(function(){},1000);
    //再次繪製新的矩形 將看到效果
    drawRect(10, 10);
}
drawRectMoveScaleRotate(150,150);

效果顯示:
image

(二)、錯切

context.transform(a,b,c,d,e,f);
添加一個新的變換矩陣
context.transform(a,b,c,d,e,f);
重置並創建新的變換矩陣

參數 描述
a 水平縮放繪圖
b 水平傾斜繪圖
c 垂直傾斜繪圖
d 垂直縮放繪圖
e 水平移動繪圖
f 垂直移動繪圖

setTransform() 重置並創建新的變換矩陣
注意:==只會影響 transform() 方法調用之後的繪圖==
繪製:平行四邊形

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>平移</title>
        <style>
            body {
                background: #283c3c;
            }
            #canvas {
                background: #f0fff0;
                border: thin solid #FFFFCC;
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='500' height='150'>
    </canvas>
        <script src="js/transform.js"></script>
    </body>
</html>

JS腳本

var canvas = document.querySelector("#canvas"),
    context = canvas.getContext('2d'),
    drawX = (canvas.width - 100) / 2,
    drawY = (canvas.height - 100) / 2,
    drawingSurfaceImageData,
    WIDTH = 100,
    HEIGH = 100;

    context.fillStyle="#49868C";
//繪製正方形
function drawRect(x, y) {
    context.strokeRect(x, y, WIDTH, HEIGH);
    context.fillRect(x, y, WIDTH, HEIGH);
    context.stroke();
    context.fill();
}
//繪製平行四邊形:參數 水平縮放、水平傾斜、垂直傾斜、垂直縮放、水平移動、垂直移動
context.transform(1,0,0.5,1,0,0)
drawRect(50, 20);
//在原有基礎上再次進行變換,添加變換矩陣
context.fillStyle="rgba(20,50,60,0.3)";
context.transform(1,0,0.5,1,0,0)
drawRect(50, 20);

//創建一個正方形,先恢復原來變換
context.fillStyle="#008000";
context.setTransform(1,0,0,1,0,0);
drawRect(280, 20);

//重置變換矩陣,變爲一個平行4邊形
context.fillStyle="rgba(50,60,100,0.3)";
context.setTransform(1,0,0.5,1,0,0);
drawRect(280, 20);
//只是在原基礎上進行了變換,進行重置後再進行變換,而不是繼續變換
context.fillStyle="rgba(100,100,200,0.3)";
context.setTransform(1,0,0.5,1,0,0);
drawRect(290, 20);

顯示效果:
image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章