HTML5游戏开发(二)
一、绘制表面的保存与恢复
Canvas绘制环境对象的另外一个关键功能就是可以对绘图表面自身进行保存与恢复。
可以使用getImageData()与putImageData()方法来操作图像。
Canvas元素是采用“立即模式”来绘制的,这意味着它会立刻将你所指定的内容绘制在canvas上。某些绘图系统,比如SVG,则会维护一份所绘图形对象的列表。这些绘图系统被叫做“保留模式”绘图系统。
立即模式的绘制系统适合制作“绘画应用程序”。
(一)绘制参考线
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>H5Canvas</title>
<style>
body{
background:#4F4B43;
}
#canvas{
margin: 10px;
padding: 10px;
background: #82A36F;
/*thin定义细的边框 inset将外部阴影 (outset) 改为内部阴影*/
border: thin inset #EDE176;
}
</style>
</head>
<body>
<canvas id='canvas' width="600" height="300"></canvas>
<script type="text/javascript" src="js/surface.js" ></script>
</body>
</html>
JS脚本:
var canvas=document.querySelector("#canvas"),
drawingSurfaceImageData={},
context=canvas.getContext('2d'),
dragging=false;
//坐标转换 将window的坐标转换为Canvas的坐标
function windowToCanvas(canvas, x, y) {
//返回canvas的大小与位置
var bbox = canvas.getBoundingClientRect();
//返回x,y值
return { x: x - bbox.left * (canvas.width / bbox.width),
y: y - bbox.top * (canvas.height / bbox.height)
};
}
//绘制水平线
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 drawGuidelines(x, y) {
//设置线的颜色
context.strokeStyle = 'rgba(0,0,230,0.8)';
context.lineWidth = 0.5;
//绘制水平线
drawVerticalLine(x);
//绘制垂直线
drawHorizontalLine(y);
}
(二)添加鼠标事件
/**
* 保存画布内容
*/
function saveDrawingSurface(){
//复制画布区域
drawingSurfaceImageData=context.getImageData(0,0,canvas.width,canvas.height);
}
//恢复画布内容
function restoreDrawingSurface(){
//将保存数据恢复
context.putImageData(drawingSurfaceImageData,0,0);
}
/**
* 当鼠标按下保存画布内容
*/
canvas.onmousedown=function(e){
dragging=true;
saveDrawingSurface();
}
canvas.onmousemove=function(e){
var loc=windowToCanvas(canvas,e.clientX,e.clientY);
if(dragging){
//恢复保存的画布,移动也必须恢复,否则会绘制多条线
restoreDrawingSurface();
//绘制参考线
drawGuidelines(loc.x,loc.y);
}
}
/**
* 鼠标抬起恢复画布
* @param {Object} e
*/
canvas.onmouseup=function(e){
restoreDrawingSurface();
}
显示效果:
二、Canvas中使用HTML元素
注意必须将控件放在canvas元素的外面,因为浏览器不支持canvas中嵌入任何元素。
(一)弹跳的小球
1、绘制网格背景
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>弹跳的小球</title>
<style>
body {
background: #2B334A;
}
#canvas {
margin-left: 10px;
margin-top: 10px;
background: #8EAACC;
border: thin solid #546963;
}
#glasspane {
position: absolute;
left: 50px;
top: 50px;
padding: 0px 20px 10px 10px;
background: rgba(0, 0, 0, 0.3);
border: thin solid rgba(0, 0, 0, 0.6);
color: #eeeeee;
font-family: Droid Sans, Arial, Helvetica, sans-serif;
font-size: 12px;
cursor: pointer;
box-shadow: rgba(0,0,0,0.5) 5px 5px 20px;
}
#glasspane h2 {
font-weight: bold;
}
#glasspane .title {
font-size: 2em;
color: #F5645F;
}
#glasspane a:hover {
color: #52433E;;
}
#glasspane a {
text-decoration: none;
color: #994545;
font-family: "微软雅黑";
font-size: 2.5em;
}
#glasspane p {
margin: 10px;
color: #FFB8A6;
font-size: 12pt;
font-family: Palatino, Arial, Helvetica, sans-serif;
}
</style>
</head>
<body>
<div id='glasspane'>
<h2 class='title'>弹跳的小球</h2>
<p>一百个球的反弹</p>
<a id='startButton'>开始</a>
</div>
<canvas id='canvas' width='600' height='400'>
</canvas>
<script src='js/canvashtml.js'></script>
</body>
</html>
js脚本
var context = document.getElementById('canvas').getContext('2d'),
startButton = document.getElementById('startButton'),
glasspane = document.getElementById('glasspane'),
paused = true, //暂停
circles = [];
drawGrid(context, '#FFE8A6', 10, 10);
/**
* 绘制网格
* @param {Object} context
* @param {Object} color
* @param {Object} stepx
* @param {Object} stepy
*/
function drawGrid(context, color, stepx, stepy) {
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();
}
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();
}
}
2、初始化100个小球对象
//初始化小球对象数组
(function init() {
for(var i = 0; i < 100; ++i) {
circles[i] = {
x: 100,
y: 100,
velocityY: 3 * Math.random(), //随机位置
velocityX: 3 * Math.random(), //
radius: 50 * Math.random(), //半径
color: 'rgba(' + (Math.random() * 255).toFixed(0) + ', ' +
(Math.random() * 255).toFixed(0) + ', ' +
(Math.random() * 255).toFixed(0) + ', 1.0)' //随机颜色
};
}
})();
/**
* 小球的坐标处理,进行变化但位置始终在画布中
* 碰到边缘进行反弹
* @param {Object} circle
*/
function adjustPosition(circle) {
//将宽度限定在画布的宽度与0之间
if (circle.x + circle.velocityX + circle.radius > context.canvas.width ||
circle.x + circle.velocityX - circle.radius < 0)
circle.velocityX = -circle.velocityX;
//将高度限定在画布的高度与0之间
if (circle.y + circle.velocityY + circle.radius > context.canvas.height ||
circle.y + circle.velocityY - circle.radius < 0)
circle.velocityY= -circle.velocityY;
circle.x += circle.velocityX;
circle.y += circle.velocityY;
}
3、使小球定时运动
/**
* 周期发生
*/
setInterval(function() {
if (!paused) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
drawGrid(context, 'lightgray', 10, 10);
circles.forEach(function(circle) {
context.beginPath();
context.arc(circle.x, circle.y, circle.radius, 0, Math.PI*2, false);
context.fillStyle = circle.color;
context.fill();
adjustPosition(circle);
});
}
}, 1000 / 60);
4、添加小球控制事件
/**
* 控制按钮
* @param {Object} e
*/
startButton.onclick = function(e) {
//取消其它事件
e.preventDefault();
//停止事件的传播
e.stopPropagation();
paused = ! paused;
startButton.innerText = paused ? '开始' : '停止';
};
显示效果:
(二)局部放大
1、加载图像
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>弹跳的小球</title>
<style>
body {
background: #4D3348;
}
#canvas {
margin-left: 20px;
margin-right: 0;
margin-bottom: 20px;
border: thin solid #294075;
cursor: crosshair;
padding: 0;
}
#controls {
margin: 20px 0px 20px 20px;
}
#rubberbandDiv {
position: absolute;
border: 3px solid #49868C;
cursor: crosshair;
display: none;/*不显示*/
}
</style>
</head>
<body>
<div id='controls'>
<input type='button' id='resetButton' value='重置' />
</div>
<!-- 橡皮筋-->
<div id='rubberbandDiv'></div>
<canvas id='canvas' width='600' height='400'>
</canvas>
<script src='js/canvasscale.js'></script>
</body>
</html>
js脚本
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
rubberbandDiv = document.getElementById('rubberbandDiv'),
resetButton = document.getElementById('resetButton'),
image = new Image(),//图片对象
mousedown = {},//鼠标按下对象
rubberbandRectangle = {},//橡皮筋对象
dragging = false;
image.src = 'img/scale.jpg';
/**
* 加载图像
*/
image.onload = function() {
context.drawImage(image, 0, 0, canvas.width, canvas.height);
};
2、添加橡皮筋方法与事件
//橡皮筋开始方法
function rubberbandStart(x, y) {
//获取鼠标x坐标
mousedown.x = x;
//获取鼠标y坐标
mousedown.y = y;
rubberbandRectangle.left = mousedown.x;
rubberbandRectangle.top = mousedown.y;
//移动橡皮筋
moveRubberbandDiv();
//显示橡皮筋
showRubberbandDiv();
dragging = true;
}
/**
* 橡皮筋 ---伸缩
* @param {Object} x
* @param {Object} y
*/
function rubberbandStretch(x, y) {
rubberbandRectangle.left = x < mousedown.x ? x : mousedown.x;
rubberbandRectangle.top = y < mousedown.y ? y : mousedown.y;
//选取图像宽度,为移动后坐标减去 点击时坐标
rubberbandRectangle.width = Math.abs(x - mousedown.x),
//选取图像高度,为移动后坐标减去 点击时坐标
rubberbandRectangle.height = Math.abs(y - mousedown.y);
//移动橡皮筋
moveRubberbandDiv();
//重置橡皮筋
resizeRubberbandDiv();
};
//移动橡皮筋
function moveRubberbandDiv() {
rubberbandDiv.style.top = rubberbandRectangle.top + 'px';
rubberbandDiv.style.left = rubberbandRectangle.left + 'px';
}
//重置橡皮筋
function resizeRubberbandDiv() {
rubberbandDiv.style.width = rubberbandRectangle.width + 'px';
rubberbandDiv.style.height = rubberbandRectangle.height + 'px';
}
//显示橡皮筋
function showRubberbandDiv() {
//显示橡皮筋
rubberbandDiv.style.display = 'inline';
}
//隐藏橡皮筋
function hideRubberbandDiv() {
rubberbandDiv.style.display = 'none';
}
//重置橡皮筋大小--设为0
function resetRubberbandRectangle() {
rubberbandRectangle = {
top: 0,
left: 0,
width: 0,
height: 0
};
}
/**
* 鼠标按下事件
*/
canvas.onmousedown = function(e) {
var x = e.x || e.clientX,
y = e.y || e.clientY;
//取消所有事件
e.preventDefault();
橡皮筋开始--主要获取起始坐标
rubberbandStart(x, y);
};
/**
* 鼠标移动事件
* @param {Object} e
*/
window.onmousemove = function(e) {
var x = e.x || e.clientX,
y = e.y || e.clientY;
e.preventDefault();
if(dragging) {
rubberbandStretch(x, y);
}
}
/**
* 鼠标抬起事件
* @param {Object} e
*/
window.onmouseup = function(e) {
e.preventDefault();
}
3、添加图像放大方法与事件
/**
* 橡皮筋 结束 图像放大对象
*/
function rubberbandEnd() {
//获取画布大小
var bbox = canvas.getBoundingClientRect();
try {
//图像放大绘制
context.drawImage(canvas,
rubberbandRectangle.left - bbox.left,
rubberbandRectangle.top - bbox.top,
rubberbandRectangle.width,
rubberbandRectangle.height,
0, 0, canvas.width, canvas.height);
} catch(e) {
}
//重置橡皮筋
resetRubberbandRectangle();
//
rubberbandDiv.style.width = 0;
rubberbandDiv.style.height = 0;
//隐藏橡皮筋
hideRubberbandDiv();
//设为不可拖拽
dragging = false;
}
/**
* 鼠标抬起事件
* @param {Object} e
*/
window.onmouseup = function(e) {
e.preventDefault();
rubberbandEnd();
}
4、重置事件
/**
* 重置事件
* @param {Object} e
*/
resetButton.onclick = function(e) {
//清除画布
context.clearRect(0, 0, context.canvas.width,
context.canvas.height);
//重绘图像
context.drawImage(image, 0, 0, canvas.width, canvas.height);
};
显示效果: