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);
顯示效果: