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();
    }
};

显示效果:

HTML5移动游戏开发高级编程 源码 html5游戏开发_HTML5移动游戏开发高级编程 源码

(二)多边形拖动

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();
   }  
};

显示效果:

HTML5移动游戏开发高级编程 源码 html5游戏开发_HTML5移动游戏开发高级编程 源码_02

二、坐标变换

(一)坐标的平移、旋转与缩放

方法

描述

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);

效果显示:

HTML5移动游戏开发高级编程 源码 html5游戏开发_ide_03

(二)、错切

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);

显示效果:

HTML5移动游戏开发高级编程 源码 html5游戏开发_ide_04