HTML5游戏开发(四)
一、线段
(一)网格绘制
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>线段</title>
<style>
body {
background: #283c3c;
}
#canvas {
background: #282828;
border: thin solid #FFFFCC;
}
</style>
</head>
<body>
<canvas id='canvas' width='600' height='500'>
</canvas>
<script src="js/line.js"></script>
</body>
</html>
JS脚本:
var context=document.querySelector("#canvas").getContext("2d");
//绘制网格
function drawGrid(context,color,stepx,stepy){
context.save();
context.strokeStyle=color;
context.lineWidth=0.5;
//绘制竖线
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();
context.closePath();
}
//绘制横线
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.closePath();
}
context.restore();
}
drawGrid(context,'lightgray',10,10);
显示效果:
(二)坐标轴绘制
<!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='600' height='500'>
</canvas>
<script src="js/line.js"></script>
</body>
</html>
javascript脚本
var context=document.querySelector("#canvas").getContext("2d");
var AXIS_MARGIN = 50, //定义轴边距
AXIS_ORIGIN = { x: AXIS_MARGIN, y: canvas.height-AXIS_MARGIN }, //定义原点
AXIS_TOP = AXIS_MARGIN, //顶部边距
AXIS_RIGHT = canvas.width-AXIS_MARGIN, //右边距
HORIZONTAL_TICK_SPACING = 10, //水平间隔
VERTICAL_TICK_SPACING = 10, //垂直间隔
AXIS_WIDTH = AXIS_RIGHT - AXIS_ORIGIN.x, //轴的宽度
AXIS_HEIGHT = AXIS_ORIGIN.y - AXIS_TOP, //轴的高度
NUM_VERTICAL_TICKS = AXIS_HEIGHT / VERTICAL_TICK_SPACING, //垂直标记数
NUM_HORIZONTAL_TICKS = AXIS_WIDTH / HORIZONTAL_TICK_SPACING,//水平标记数
TICK_WIDTH = 10, //标记宽度
TICKS_LINEWIDTH = 0.5, //标记线宽度
TICKS_COLOR = 'navy', //标记颜色
AXIS_LINEWIDTH = 1.0, //轴线粗细
AXIS_COLOR = 'blue'; //轴颜色
//绘制轴
function drawAxes() {
context.save();
context.strokeStyle = AXIS_COLOR;
context.lineWidth = AXIS_LINEWIDTH;
drawHorizontalAxis();
drawVerticalAxis();
context.lineWidth = 0.5;
context.lineWidth = TICKS_LINEWIDTH;
context.strokeStyle = TICKS_COLOR;
drawVerticalAxisTicks();
drawHorizontalAxisTicks();
context.restore();
}
//绘制水平轴
function drawHorizontalAxis() {
context.beginPath();
context.moveTo(AXIS_ORIGIN.x, AXIS_ORIGIN.y);
context.lineTo(AXIS_RIGHT, AXIS_ORIGIN.y)
context.stroke();
}
//绘制垂直轴
function drawVerticalAxis() {
context.beginPath();
context.moveTo(AXIS_ORIGIN.x, AXIS_ORIGIN.y);
context.lineTo(AXIS_ORIGIN.x, AXIS_TOP);
context.stroke();
}
//绘制水平标记
function drawVerticalAxisTicks() {
var deltaY;
for (var i=1; i < NUM_VERTICAL_TICKS; ++i) {
context.beginPath();
if (i % 5 === 0) deltaX = TICK_WIDTH;
else deltaX = TICK_WIDTH/2;
context.moveTo(AXIS_ORIGIN.x - deltaX,
AXIS_ORIGIN.y - i * VERTICAL_TICK_SPACING);
context.lineTo(AXIS_ORIGIN.x + deltaX,
AXIS_ORIGIN.y - i * VERTICAL_TICK_SPACING);
context.stroke();
}
}
//绘制垂直标记
function drawHorizontalAxisTicks() {
var deltaY;
for (var i=1; i < NUM_HORIZONTAL_TICKS; ++i) {
context.beginPath();
if (i % 5 === 0) deltaY = TICK_WIDTH;
else deltaY = TICK_WIDTH/2;
context.moveTo(AXIS_ORIGIN.x + i * HORIZONTAL_TICK_SPACING,
AXIS_ORIGIN.y - deltaY);
context.lineTo(AXIS_ORIGIN.x + i * HORIZONTAL_TICK_SPACING,
AXIS_ORIGIN.y + deltaY);
context.stroke();
}
}
drawAxes();
显示效果:
(三)橡皮筋线条绘制
1、基本功能
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>橡皮筋绘制</title>
<style>
body {
background: #283c3c;
color: #fff0f0;
}
#canvas {
background: #f0fff0;
border: thin solid #FFFFCC;
}
</style>
</head>
<body>
线条颜色:
<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>
参考线: <input id='guidewireCheckbox' type='checkbox' checked/>
<input id='eraseAllButton' type='button' value='擦除'/>
<canvas id='canvas' width='600' height='500'>
</canvas>
<script src="js/rubberbands.js"></script>
</body>
</html>
JS脚本
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
eraseAllButton = document.getElementById('eraseAllButton'),
strokeStyleSelect = document.getElementById('strokeStyleSelect'),
guidewireCheckbox = document.getElementById('guidewireCheckbox'),
drawingSurfaceImageData,//绘制表面图像数据
mousedown = {},//鼠标按下
rubberbandRect = {},//橡皮筋
dragging = false,//是否拖拽
guidewires = guidewireCheckbox.checked;//参考线
//-----------------------------------------1、基本功能
//绘制网格
function drawGrid(color, stepx, stepy) {
context.save()
context.strokeStyle = color;
context.lineWidth = 0.5;
context.clearRect(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(x, y) {
var bbox = canvas.getBoundingClientRect();
return { x: x - bbox.left * (canvas.width / bbox.width),
y: y - bbox.top * (canvas.height / bbox.height) };
}
//显示网格
context.strokeStyle = strokeStyleSelect.value;
drawGrid('lightgray', 10, 10);
2、绘制橡皮筋
//-------------------------------------------2、绘制橡皮筋
//保存图像
function saveDrawingSurface() {
drawingSurfaceImageData = context.getImageData(0, 0,
canvas.width,
canvas.height);
}
//恢复图像
function restoreDrawingSurface() {
context.putImageData(drawingSurfaceImageData, 0, 0);
}
//更新线条颜色,并获取绘制坐标
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;
context.save();
context.strokeStyle = 'red';
context.restore();
}
//绘制线条
function drawRubberbandShape(loc) {
context.beginPath();
context.moveTo(mousedown.x, mousedown.y);
context.lineTo(loc.x, loc.y);
context.stroke();
}
//更新线条
function updateRubberband(loc) {
updateRubberbandRectangle(loc);
drawRubberbandShape(loc);
}
3、绘制参考线
//--------------------------------------3、绘制参考线
//水平对考线
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();
}
4、添加绘制事件
//-----------------------------------------4、添加绘制事件
//鼠标按下
canvas.onmousedown = function (e) {
var loc = windowToCanvas(e.clientX, e.clientY);
//禁用其它事件
e.preventDefault(); //防止光标变化
//保存图像
saveDrawingSurface();
mousedown.x = loc.x;
mousedown.y = loc.y;
dragging = true;
};
//鼠标移动
canvas.onmousemove = function (e) {
var loc;
if (dragging) {
e.preventDefault();
//转换坐标
loc = windowToCanvas(e.clientX, e.clientY);
//恢复图像,如果没有,则参考线会出现重影
restoreDrawingSurface();
//绘制路径
updateRubberband(loc);
if(guidewires) {
//显示参考线
drawGuidewires(loc.x, loc.y);
}
}
};
//鼠标抬起
canvas.onmouseup = function (e) {
loc = windowToCanvas(e.clientX, e.clientY);
restoreDrawingSurface();
updateRubberband(loc);
dragging = false;
};
5、添加控制事件
//---------------------------------------5、添加控制事件
eraseAllButton.onclick = function (e) {
//清除画布
context.clearRect(0, 0, canvas.width, canvas.height);
//重新绘制网格
drawGrid('lightgray', 10, 10);
//并保存画布
saveDrawingSurface();
};
//改变线条颜色
strokeStyleSelect.onchange = function (e) {
context.strokeStyle = strokeStyleSelect.value;
};
//是否启用参考线
guidewireCheckbox.onchange = function (e) {
guidewires = guidewireCheckbox.checked;
};
显示效果:
(四)虚线绘制
<!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='600' height='500'>
</canvas>
<script src="js/dashed.js"></script>
</body>
</html>
JS脚本
var context = document.getElementById('canvas').getContext('2d'),
//获取CanvasRenderingContext2D原型对象明moveTo
moveToFunction = CanvasRenderingContext2D.prototype.moveTo;
//设置系统 CanvasRenderingContext2D属性
CanvasRenderingContext2D.prototype.lastMoveToLocation = {};
//重写系统moveTo方法,主要是为了获取移动的属性值
CanvasRenderingContext2D.prototype.moveTo = function (x, y) {
moveToFunction.apply(context, [x,y]);
this.lastMoveToLocation.x = x;
//赋值给新加属性对象
this.lastMoveToLocation.y = y;
};
//新加绘制虚线方法
CanvasRenderingContext2D.prototype.dashedLineTo = function (x, y, dashLength) {
//如果长度未定义,默认为5
dashLength = dashLength === undefined ? 5 : dashLength;
//设置开始坐标为 取得的moveTo的坐标
var startX = this.lastMoveToLocation.x;
var startY = this.lastMoveToLocation.y;
//参数减去原坐标,用于计算,虚线段坐标值
var deltaX = x - startX;
var deltaY = y - startY;
//计算虚线段的点数
var numDashes = Math.floor(Math.sqrt(deltaX * deltaX + deltaY * deltaY) / dashLength);
//绘制虚线
for (var i=0; i < numDashes; ++i) {
//设用系统类的moveTo函数与lineTo函数不断绘制线 其中使得求2的模运算,即可绘出不同的间隔线
this[ i % 2 === 0 ? 'moveTo' : 'lineTo' ]
(startX + (deltaX / numDashes) * i, startY + (deltaY / numDashes) * i);
}
//绘制线段,如果有多个线,起点矫正
this.moveTo(x, y);
};
//定义线粗细
context.lineWidth = 3;
//定义线颜色
context.strokeStyle = 'blue';
//定义线的起点
context.moveTo(20, 20);
//定义线的终点
context.dashedLineTo(context.canvas.width-20, 20);
//绘制右竖线
context.dashedLineTo(context.canvas.width-20, context.canvas.height-20);
//绘制底竖线
context.dashedLineTo(20, context.canvas.height-20);
//绘制左竖线
context.dashedLineTo(20, 20);
//绘制斜线
context.dashedLineTo(context.canvas.width-20, context.canvas.height-20);
context.stroke();
显示效果:
==注意:重写系统的函数方式,以及虚线算法。==
二、圆弧与圆形
(一)橡皮筋方式绘圆
1、基础功能
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>虚线</title>
<style>
body {
background: #283c3c;
color: #6495ED;
}
#canvas {
background: #f0fff0;
border: thin solid #FFFFCC;
}
</style>
</head>
<body>
<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>
<br> 线粗细:
<select id='lineWidthSelect'>
<option value='1.0'>1.0</option>
<option value='2.0'>2.0</option>
<option value='3.0'>3.0</option>
<option value='4.0'>4.0</option>
<option value='5.0'>5.0</option>
<option value='6.0'>6.0</option>
</select>
是否填充 <input id='fillCheckbox' type='checkbox' checked/> 参考线:
<input id='guidewireCheckbox' type='checkbox' checked/>
<input id='eraseAllButton' type='button' value='擦除所有' />
</div>
<canvas id='canvas' width='600' height='500'>
</canvas>
<script src="js/RubberbandRound.js"></script>
</body>
</html>
JS脚本
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
eraseAllButton = document.getElementById('eraseAllButton'),
strokeStyleSelect = document.getElementById('strokeStyleSelect'),
fillStyleSelect = document.getElementById('fillStyleSelect'),
lineWidthSelect = document.getElementById('lineWidthSelect'),
fillCheckbox = document.getElementById('fillCheckbox'),
guidewireCheckbox = document.getElementById('guidewireCheckbox'),
drawingSurfaceImageData, //绘制橡皮筋数据
mousedown = {},
rubberbandRect = {},//橡皮筋对象
dragging = false,//是否可以拖拽
guidewires = true;//是否启用参考线
//-------------------------1、 基础功能,绘制网格与坐标转换
//绘制背景网格
function drawGrid(color, stepx, stepy) {
context.save()
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(x, y) {
var 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;
context.lineWidth = lineWidthSelect.value;
drawGrid('lightgray', 10, 10);
2、橡皮筋功能
//---------------------------2、橡皮筋功能
//保存橡皮筋
function saveDrawingSurface() {
drawingSurfaceImageData = context.getImageData(0, 0,
canvas.width,
canvas.height);
}
//恢复橡皮筋
function restoreDrawingSurface() {
context.putImageData(drawingSurfaceImageData, 0, 0);
}
//更新橡皮筋形状
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) {
var angle,
radius;
if (mousedown.y === loc.y) {
//计算半径,如果鼠标按下的y坐标与原坐标相同
//则取本坐标减去按下的x坐标作为半径
radius = Math.abs(loc.x - mousedown.x);
}
else {
//通过失量计算半径 通过反正切值
angle = Math.atan(rubberbandRect.height/rubberbandRect.width);
//sin正弦
radius = rubberbandRect.height / Math.sin(angle);
}
//绘制路径
context.beginPath();
//圆形绘制
context.arc(mousedown.x, mousedown.y, radius, 0, Math.PI*2, false);
context.stroke();
//如果选中了填充复选框,则进行填充
if (fillCheckbox.checked)
context.fill();
}
//更新橡皮筋
function updateRubberband(loc) {
updateRubberbandRectangle(loc);
drawRubberbandShape(loc);
}
3、参考线绘制
//----------------------------3、参考线绘制
//横线
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();
}
4、绘制事件
//--------------------------4、绘制事件
//鼠标按下
canvas.onmousedown = function (e) {
var loc = windowToCanvas(e.clientX, e.clientY);
e.preventDefault();
saveDrawingSurface();
mousedown.x = loc.x;
mousedown.y = loc.y;
dragging = true;
};
//鼠标移动
canvas.onmousemove = function (e) {
var loc;
if (dragging) {
e.preventDefault();
loc = windowToCanvas(e.clientX, e.clientY);
restoreDrawingSurface();
updateRubberband(loc);
if(guidewires) {
drawGuidewires(loc.x, loc.y);
}
}
};
//鼠标抬符起
canvas.onmouseup = function (e) {
loc = windowToCanvas(e.clientX, e.clientY);
restoreDrawingSurface();
updateRubberband(loc);
dragging = false;
};
5、控制事件
//-------------------------5、控制事件
//擦除所有
eraseAllButton.onclick = function (e) {
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;
};
//绘制线的粗细
lineWidthSelect.onchange = function (e) {
context.lineWidth = lineWidthSelect.value;
};
//是否显示参考线
guidewireCheckbox.onchange = function (e) {
guidewires = guidewireCheckbox.checked;
};
显示效果:
(二)绘制圆弧路径
参数 | 描述 |
---|---|
x1 | 弧的起点的 x 坐标 |
y1 | 弧的起点的 y 坐标 |
x2 | 弧的终点的 x 坐标 |
y2 | 弧的终点的 y 坐标 |
r | 弧的半径 |
<!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='600' height='200'>
</canvas>
<script src="js/RoundRect.js"></script>
</body>
</html>
JS脚本
var context = document.getElementById('canvas').getContext('2d');
//绘制圆角矩弧 参数: 坐标 大小 半径
function roundedRect(cornerX, cornerY, width, height, cornerRadius) {
if (width > 0) context.moveTo(cornerX + cornerRadius, cornerY);
else context.moveTo(cornerX - cornerRadius, cornerY);
//使用arcTo
//创建弧 角1
context.arcTo(cornerX + width, cornerY,
cornerX + width, cornerY + height,
cornerRadius);
//创建弧 角2
context.arcTo(cornerX + width, cornerY + height,
cornerX, cornerY + height,
cornerRadius);
//创建弧 角3
context.arcTo(cornerX, cornerY + height,
cornerX, cornerY,
cornerRadius);
//创建弧 角4
if (width > 0) {
context.arcTo(cornerX, cornerY,
cornerX + cornerRadius, cornerY,
cornerRadius);
}
else {
context.arcTo(cornerX, cornerY,
cornerX - cornerRadius, cornerY,
cornerRadius);
}
}
//绘制圆角矩形并填充
function drawRoundedRect(strokeStyle, fillStyle, cornerX, cornerY, width, height, cornerRadius) {
context.beginPath();
//绘制圆角矩形
roundedRect(cornerX, cornerY, width, height, cornerRadius);
//线条样式
context.strokeStyle = strokeStyle;
//填充样式
context.fillStyle = fillStyle;
context.stroke();
context.fill();
}
//函数调用进行绘制
drawRoundedRect('blue', 'yellow', 50, 40, 100, 100, 10);
drawRoundedRect('purple', 'green', 275, 40, -100, 100, 20);
drawRoundedRect('red', 'white', 300, 140, 100, -100, 30);
drawRoundedRect('white', 'blue', 525, 140, -100, -100, 40);
显示效果: