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();
}
};
显示效果:
(二)多边形拖动
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();
}
};
显示效果:
二、坐标变换
(一)坐标的平移、旋转与缩放
方法 | 描述 |
---|---|
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);
效果显示:
(二)、错切
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);
显示效果: