HTML5游戏开发(八)
一、剪辑区域
剪辑区域它是在canvas之中由路径所定义的一块区域,浏览器会将所有的绘图操作都限制在本区域内执行。在默认情况下,剪辑区域的大小与canvas一致。除非你通过创建路径并调用cavas绘图环境对象的clip()方法来显示地设定剪辑区域,否则默认的剪辑区域不会影响canvas之中所绘制的内容。然而,一旦设置好剪辑区域,那么你在canvas之中绘制的所有内容都将局限在该区域内,这意味着在剪辑区域以外进行绘制是没有任何效果的。
1.图像擦除
(1)基本功能
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图像剪辑</title>
<style>
#canvas{
background-color: #99CC99;
border: solid sandybrown;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
<script type="text/javascript" src="js/clip.js" ></script>
</body>
</html>
JS脚本
var canvas = document.querySelector("#canvas"),
context = canvas.getContext('2d'),
lastX, //擦除起始坐标X
lastY, //擦除起始坐标Y
drawingSurfaceImageData,
dragging = false,
ERASER_SHADOW_COLOR = 'rgb(0,0,0)',
ERASER_SHADOW_STYLE = 'blue',
ERASER_STROKE_STYLE = 'rgb(0,0,255)',
ERASER_SHADOW_OFFSET = -5,
ERASER_SHADOW_BLUR = 20,
//设定直径
eraserWidth = 20;
ERASER_LINE_WIDTH = 1, //擦除线粗细
mousedown = {};
//-------------------·1、基本功能
//调用初始化方法
draw();
function draw() {
drawBackground();
drawRect();
}
//绘制矩形
function drawRect() {
context.fillStyle = "sandybrown";
context.fillRect((canvas.width - 100) / 2, (canvas.height - 100) / 2, 100, 100);
}
//绘制背景
function drawBackground() {
context.fillStyle = "#99CC99";
context.fillRect(0, 0, canvas.width, canvas.height);
}
//坐标转换
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)
};
}
(2)
//-----------------------------2、绘制擦除
//绘制插除路径
function setDrawPathForEraser(loc) {
//路径绘制
context.beginPath();
context.arc(loc.x, loc.y,
eraserWidth / 2,
0, Math.PI * 2, false);
//剪辑
context.clip();
}
//设置剪辑区域
function setErasePathForEraser() {
//路径绘制
context.beginPath();
//绘制圆形
context.arc(lastX, lastY,
eraserWidth / 2 + ERASER_LINE_WIDTH, 0, Math.PI * 2, false);
//进行剪辑,实际剪辑的是绘制的圆形
context.clip();
}
//擦除结果
function eraseLast() {
//保存原有画布,可注释此代码屯restore查看效果
//总是在上一次剪辑的区域内进行操作
//因为调用clip方法:将会把剪辑区域设置为当前剪辑区域与当前路径的交集。
context.save();
//设置剪辑区域
setErasePathForEraser();
//非常重要的代码:重绘背景
drawBackground();
//恢复原画布
context.restore();
}
//设置擦除属性
function setEraserAttributes() {
//路径粗细
context.lineWidth = ERASER_LINE_WIDTH;
//阴影
context.shadowColor = ERASER_SHADOW_STYLE;
context.shadowOffsetX = ERASER_SHADOW_OFFSET;
context.shadowOffsetY = ERASER_SHADOW_OFFSET;
context.shadowBlur = ERASER_SHADOW_BLUR;
//路径样式
context.strokeStyle = ERASER_STROKE_STYLE;
}
//给制擦除
function drawEraser(loc) {
context.save();
//设置擦除属性
setEraserAttributes();
//设置擦除路径
setDrawPathForEraser(loc);
//绘制跃升径
context.stroke();
context.restore();
}
(3)事件处理
//-----------------------------3、事件处理
canvas.onmousedown = function(e) {
var loc = windowToCanvas(e.clientX, e.clientY);
//禁止默认事件
e.preventDefault();
mousedown.x = loc.x;
mousedown.y = loc.y;
lastX = loc.x;
lastY = loc.y;
//设置拖拽状态
dragging = true;
};
//鼠标移动
canvas.onmousemove = function(e) {
var loc;
//如果拖拽可用
if(dragging) {
//阻止默认行为
e.preventDefault();
//获取画布坐示
loc = windowToCanvas(e.clientX, e.clientY);
//擦除
eraseLast();
//
drawEraser(loc);
//获取最后坐标
lastX = loc.x;
lastY = loc.y;
}
};
canvas.onmouseup = function(e) {
loc = windowToCanvas(e.clientX, e.clientY);
eraseLast();
dragging = false;
};
显终效果:
2.伸缩动画
(1)基本功能
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>伸缩动画</title>
<style>
#canvas{
background-color: #99CC99;
border: solid sandybrown;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
<script type="text/javascript" src="js/Telescoping.js" ></script>
</body>
</html>
JS脚本
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
context.lineWidth = 0.5;
context.font = '50pt Comic-sans';
drawText();
//----------------------1、基本功能
//文本绘制
function drawText() {
context.save();
//阴影
context.shadowColor = 'rgba(100, 100, 150, 0.8)';
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 10;
context.fillStyle = 'cornflowerblue';
context.fillText('跟我学动画', 20, 160);
context.strokeStyle = 'yellow';
context.strokeText('跟我学动画', 20, 160);
context.restore();
}
(2)设置剪辑
//-------------------------2、设置剪辑
//设置剪辑区域
function setClippingRegion(radius) {
context.beginPath();
context.arc(canvas.width/2, canvas.height/2,
radius, 0, Math.PI*2, false);
context.clip();
}
//背景添充
function fillCanvas() {
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.3)";
context.fillRect(0, 0, canvas.width, canvas.height);
}
(3)动画处理
//-----------------------3、动画处理
//动画调用
function endAnimation(loop) {
//停止动画
clearInterval(loop);
//设置时间间隔为1秒
setTimeout( function (e) {
//清空画布
context.clearRect(0, 0, canvas.width, canvas.height);
//绘制文本
drawText();
}, 1000);
}
function drawAnimationFrame(radius) {
//设定剪辑区域
setClippingRegion(radius);
//绘制背景
fillCanvas();
//绘制文字
drawText();
}
function animate() {
var radius = canvas.width/2,
loop;
//动画播放,并返回loop值,作为停止的参数
loop = window.setInterval(function() {
radius -= canvas.width/100;
//背景绘制
fillCanvas();
//当半径大于0时
if (radius > 0) {
context.save();
//绘制动画
drawAnimationFrame(radius);
context.restore();
}
else {
//停止动画
endAnimation(loop);
}
}, 16);
};
(4)事件处理
//-------------------4、事件处理
canvas.onmousedown = function (e) {
animate();
};
最终效果:
三、文本
属性 | 描述 |
---|---|
font | 设置或返回文本内容的当前字体属性 |
textAlign | 设置或返回文本内容的当前对齐方式 |
textBaseline | 设置或返回在绘制文本时使用的当前文本基线 |
方法 | 描述 |
---|---|
fillText() | 在画布上绘制“被填充的”文本 |
strokeText() | 在画布上绘制文本(无填充) |
measureText() | ==返回==包含指定文本宽度的对象 |
1.绘制坐标轴文字
(1)基本功能
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
background: #eeeeee;
}
#canvas {
background: #ffffff;
cursor: pointer;
margin-left: 10px;
margin-top: 10px;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<canvas id='canvas' width='650' height='450'>
Canvas not supported
</canvas>
<script src='js/axis.js'></script>
</body>
</html>
JS脚本
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
HORIZONTAL_AXIS_MARGIN = 50, //水平轴空白
VERTICAL_AXIS_MARGIN = 50, //垂直轴空白
//原点
AXIS_ORIGIN = { x: HORIZONTAL_AXIS_MARGIN,
y: canvas.height-VERTICAL_AXIS_MARGIN },
AXIS_TOP = VERTICAL_AXIS_MARGIN, //顶部
AXIS_RIGHT = canvas.width-HORIZONTAL_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,
//标签间空白
SPACE_BETWEEN_LABELS_AND_AXIS = 20;
//----------------------------1、基本功能
context.font = '13px Arial';
//设置阴影
context.shadowColor = 'rgba(100, 140, 230, 0.8)';
context.shadowOffsetX = 3;
context.shadowOffsetY = 3;
context.shadowBlur = 5;
//绘制坐标
function drawAxes() {
context.save();
context.lineWidth = 1.0;
context.fillStyle = 'rgba(100, 140, 230, 0.8)';
context.strokeStyle = 'navy';
//绘制水平轴
drawHorizontalAxis();
//绘制垂直轴
drawVerticalAxis();
context.lineWidth = 0.5;
context.strokeStyle = 'navy';
context.strokeStyle = 'darkred';
//绘制垂直刻度
drawVerticalAxisTicks();
//绘制水平刻度
drawHorizontalAxisTicks();
context.restore();
}
//绘制刻度
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();
}
}
//绘制水平轴
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();
}
(2)标签绘制
//绘制标签
function drawAxisLabels() {
context.fillStyle = 'blue';
//绘制标记
drawHorizontalAxisLabels();
drawVerticalAxisLabels();
}
//绘制水平标签
function drawHorizontalAxisLabels() {
context.textAlign = 'center';
context.textBaseline = 'top';
for (var i=0; i <= NUM_HORIZONTAL_TICKS; ++i) {
if (i % 5 === 0) {
//刻度绘制
context.fillText(i,
AXIS_ORIGIN.x + i * HORIZONTAL_TICK_SPACING,
AXIS_ORIGIN.y + SPACE_BETWEEN_LABELS_AND_AXIS);
}
}
}
//绘制垂直标签
function drawVerticalAxisLabels() {
context.textAlign = 'right';
context.textBaseline = 'middle';
for (var i=0; i <= NUM_VERTICAL_TICKS; ++i) {
if (i % 5 === 0) {
//绘制刻度
context.fillText(i,
AXIS_ORIGIN.x - SPACE_BETWEEN_LABELS_AND_AXIS,
AXIS_ORIGIN.y - i * VERTICAL_TICK_SPACING);
}
}
}
//绘制轴
drawAxes();
//绘制刻度与标签
drawAxisLabels();
显示效果:
2.绘制弧形文本
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>弧形文本</title>
<style>
body {
background: #eeeeee;
}
#canvas {
background: #ffffff;
margin-left: 10px;
margin-top: 10px;
box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<canvas id='canvas' width='650' height='450'>
</canvas>
<script type="text/javascript" src="js/arctext.js"></script>
</body>
</html>
JS脚本:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
CENTROID_RADIUS = 10, //中心点半径
CENTROID_STROKE_STYLE = 'rgba(0, 0, 0, 0.5)', //中心点路径样式
CENTROID_FILL_STYLE ='rgba(80, 190, 240, 0.6)', //中心点填充色
TEXT_FILL_STYLE = 'rgba(100, 130, 240, 0.5)', //文本样式式
TEXT_STROKE_STYLE = 'rgba(200, 0, 0, 0.7)', //文本路径样式
TEXT_SIZE = 64, //文本字体大小
//圆形参数
circle = { x: canvas.width/2,
y: canvas.height/2,
radius: 180
};
//绘制弧形文本
function drawCircularText(string, startAngle, endAngle) {
var radius = circle.radius,
angleDecrement = (startAngle - endAngle)/(string.length-1),
angle = parseFloat(startAngle),
index = 0,
character;
context.save();
context.fillStyle = TEXT_FILL_STYLE;
context.strokeStyle = TEXT_STROKE_STYLE;
//设置字体
context.font = TEXT_SIZE + 'px Lucida Sans';
//循环绘制
while (index < string.length) {
//获取字符
character = string.charAt(index);
//保存画布,目的是让其坐标重新开始
context.save();
context.beginPath();
//移动
context.translate(
circle.x + Math.cos(angle) * radius,
circle.y - Math.sin(angle) * radius);
//旋转
context.rotate(Math.PI/2 - angle);
//绘制文本
context.fillText(character, 0, 0);
//绘制文本边线
context.strokeText(character, 0, 0);
//角度变化
angle -= angleDecrement;
index++;
//恢复画布
context.restore();
}
context.restore();
}
//绘制中心点
function drawCentroid() {
context.beginPath();
context.save();
context.strokeStyle = CENTROID_STROKE_STYLE;
context.fillStyle = CENTROID_FILL_STYLE;
context.arc(circle.x, circle.y, CENTROID_RADIUS, 0, Math.PI*2, false);
context.stroke();
context.fill();
context.restore();
}
//阴影
context.shadowOffsetX = 2;
context.shadowOffsetY = 2;
context.shadowBlur = 5;
//文本居中对齐
context.textAlign = 'center';
//基线居中
context.textBaseline = 'middle';
//中心点绘制
drawCentroid();
//绘制弧形文本
drawCircularText("这是一个弧形文本示例", Math.PI*2, Math.PI/8);
最终效果:
3.绘制文本编辑器
(1)创建文本光标类
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>简单文本编辑</title>
<style>
body {
background: #eeeeee;
}
#canvas {
background: #ffffff;
margin-left: 10px;
margin-top: 10px;
box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<canvas id='canvas' width='650' height='450'>
</canvas>
<script type="text/javascript" src="js/textedit.js"></script>
</body>
</html>
JS脚本
//-----------------------------1、文本光标类
//绘制光标文本
TextCursor = function (fillStyle, width) {
this.fillStyle = fillStyle || 'rgba(0, 0, 0, 0.7)';
this.width = width || 2;
this.left = 0;
this.top = 0;
};
//使用原型方法添加方法
TextCursor.prototype = {
//获取高度
getHeight: function (context) {
var w = context.measureText('W').width;
return w + w/6;
},
//创建路径
createPath: function (context) {
context.beginPath();
context.rect(this.left, this.top,
this.width, this.getHeight(context));
},
//绘制光标
draw: function (context, left, bottom) {
context.save();
this.left = left;
this.top = bottom - this.getHeight(context);
this.createPath(context);
context.fillStyle = this.fillStyle;
//填充当前路径
context.fill();
context.restore();
},
//擦除
erase: function (context, imageData) {
//恢复数据
context.putImageData(imageData, 0, 0,
this.left, this.top,
this.width, this.getHeight(context));
}
};
(2)文本行类
//----------------------2、文本行类
//文本行类
TextLine = function (x, y) {
this.text = '';
this.left = x;
this.bottom = y;
this.caret = 0; //字符长度
};
//使用原型方式添加方法
TextLine.prototype = {
//插入文本
insert: function (text) {
var first = this.text.slice(0, this.caret),
last = this.text.slice(this.caret);
first += text;
this.text = first;
this.text += last;
this.caret += text.length;
},
//获取字符x坐标
getCaretX: function (context) {
var s = this.text.substring(0, this.caret),
//获取文本宽度
w = context.measureText(s).width;
return this.left + w;
},
//移除前面字符
removeCharacterBeforeCaret: function () {
if (this.caret === 0)
return;
//截取文本
this.text = this.text.substring(0, this.caret-1) +
this.text.substring(this.caret);
//改变字符长度
this.caret--;
},
//移除最后字符
removeLastCharacter: function () {
this.text = this.text.slice(0, -1);
},
//获取字宽
getWidth: function(context) {
return context.measureText(this.text).width;
},
//获取字高
getHeight: function (context) {
var h = context.measureText('W').width;
return h + h/6;
},
//绘制字符
draw: function(context) {
context.save();
context.textAlign = 'start';
context.textBaseline = 'bottom';
context.strokeText(this.text, this.left, this.bottom);
context.fillText(this.text, this.left, this.bottom);
context.restore();
},
//擦除字符
erase: function (context, imageData) {
context.putImageData(imageData, 0, 0);
}
};
(3)
//----------------------------3、段落类
//段落类
Paragraph = function (context, left, top, imageData, cursor) {
this.context = context;
this.drawingSurface = imageData; //画布
this.left = left;
this.top = top;
this.lines = []; //行数,用于保存所有文本数据的集合
this.activeLine = undefined; //激活行
this.cursor = cursor;
this.blinkingInterval = undefined; //行高
};
//使用原型方式添加方法
Paragraph.prototype = {
//
isPointInside: function (loc) {
var c = this.context;
c.beginPath();
c.rect(this.left, this.top,
this.getWidth(), this.getHeight());
return c.isPointInPath(loc.x, loc.y);
},
//获取高
getHeight: function () {
var h = 0;
//读取所有行
this.lines.forEach( function (line) {
h += line.getHeight(this.context);
});
return h;
},
//获取宽
getWidth: function () {
var w = 0,
widest = 0;
//读取每行字符宽度
this.lines.forEach( function (line) {
w = line.getWidth(this.context);
if (w > widest) {
widest = w;
}
});
return widest;
},
//绘制文本
draw: function () {
this.lines.forEach( function (line) {
line.draw(this.context);
});
},
//擦除文本
erase: function (context, imageData) {
context.putImageData(imageData, 0, 0);
},
//添加行
addLine: function (line) {
this.lines.push(line);
this.activeLine = line;
this.moveCursor(line.left, line.bottom);
},
//插入文本
insert: function (text) {
this.erase(this.context, this.drawingSurface);
this.activeLine.insert(text);
var t = this.activeLine.text.substring(0, this.activeLine.caret),
w = this.context.measureText(t).width;
//移除光标
this.moveCursor(this.activeLine.left + w,
this.activeLine.bottom);
//绘制文本
this.draw(this.context);
},
//光标闪烁
blinkCursor: function (x, y) {
var self = this,
BLINK_OUT = 200, //间隔时长
BLINK_INTERVAL = 900; //间隔周期
//光标闪烁周期
this.blinkingInterval = setInterval( function (e) {
cursor.erase(context, self.drawingSurface);
//设置绘制时间
setTimeout( function (e) {
cursor.draw(context, cursor.left,
cursor.top + cursor.getHeight(context));
}, BLINK_OUT);
}, BLINK_INTERVAL);
},
//移动并关闭光标
moveCursorCloseTo: function (x, y) {
var line = this.getLine(y);
if (line) {
line.caret = this.getColumn(line, x);
this.activeLine = line;
this.moveCursor(line.getCaretX(context),
line.bottom);
}
},
//移动光标到指定位置
moveCursor: function (x, y) {
this.cursor.erase(this.context, this.drawingSurface);
this.cursor.draw(this.context, x, y);
if ( ! this.blinkingInterval)
this.blinkCursor(x, y);
},
//移动线
moveLinesDown: function (start) {
for (var i=start; i < this.lines.length; ++i) {
line = this.lines[i];
line.bottom += line.getHeight(this.context);
}
},
//产生新行
newline: function () {
var textBeforeCursor = this.activeLine.text.substring(0, this.activeLine.caret),
textAfterCursor = this.activeLine.text.substring(this.activeLine.caret),
height = this.context.measureText('W').width +
this.context.measureText('W').width/6,
bottom = this.activeLine.bottom + height,
activeIndex,
line;
this.erase(this.context, this.drawingSurface); // Erase paragraph
this.activeLine.text = textBeforeCursor; // Set active line's text
line = new TextLine(this.activeLine.left, bottom); // Create a new line
line.insert(textAfterCursor); // containing text after cursor
activeIndex = this.lines.indexOf(this.activeLine); // Splice in new line
this.lines.splice(activeIndex+1, 0, line);
this.activeLine = line; // New line is active with
this.activeLine.caret = 0; // caret at first character
activeIndex = this.lines.indexOf(this.activeLine); // Starting at the new line...
for(var i=activeIndex+1; i < this.lines.length; ++i) { //...loop over remaining lines
line = this.lines[i];
line.bottom += height; //移动到另一行
}
this.draw();
this.cursor.draw(this.context, this.activeLine.left, this.activeLine.bottom);
},
//获取行
getLine: function (y) {
var line;
for (i=0; i < this.lines.length; ++i) {
line = this.lines[i];
if (y > line.bottom - line.getHeight(context) &&
y < line.bottom) {
return line;
}
}
return undefined;
},
//获取列
getColumn: function (line, x) {
var found = false,
before,
after,
closest,
tmpLine,
column;
tmpLine = new TextLine(line.left, line.bottom);
tmpLine.insert(line.text);
while ( ! found && tmpLine.text.length > 0) {
before = tmpLine.left + tmpLine.getWidth(context);
tmpLine.removeLastCharacter();
after = tmpLine.left + tmpLine.getWidth(context);
if (after < x) {
closest = x - after < before - x ? after : before;
column = closest === before ?
tmpLine.text.length + 1 : tmpLine.text.length;
found = true;
}
}
return column;
},
//激活行是否为最后行
activeLineIsOutOfText: function () {
return this.activeLine.text.length === 0;
},
//激活行是否为最后行
activeLineIsTopLine: function () {
return this.lines[0] === this.activeLine;
},
//移动到上一行
moveUpOneLine: function () {
var lastActiveText, line, before, after;
lastActiveLine = this.activeLine;
lastActiveText = '' + lastActiveLine.text;
activeIndex = this.lines.indexOf(this.activeLine);
this.activeLine = this.lines[activeIndex - 1];
this.activeLine.caret = this.activeLine.text.length;
//复制行内容
this.lines.splice(activeIndex, 1);
//移动光标
this.moveCursor(
this.activeLine.left + this.activeLine.getWidth(this.context),
this.activeLine.bottom);
//激活行
this.activeLine.text += lastActiveText;
for (var i=activeIndex; i < this.lines.length; ++i) {
line = this.lines[i];
line.bottom -= line.getHeight(this.context);
}
},
//退格键
backspace: function () {
var lastActiveLine,
activeIndex,
t, w;
this.context.save();
//如果激活行为0
if (this.activeLine.caret === 0) {
if ( ! this.activeLineIsTopLine()) {
//擦除内容
this.erase(this.context, this.drawingSurface);
//移动到上一行
this.moveUpOneLine();
this.draw();
}
}
else {
// 激活文本
this.context.fillStyle = "red";
this.context.strokeStyle = "red";;
//擦除文本
this.erase(this.context, this.drawingSurface);
//移除光标前的字
this.activeLine.removeCharacterBeforeCaret();
//复制文本
t = this.activeLine.text.slice(0, this.activeLine.caret),
//获取文本宽度
w = this.context.measureText(t).width;
//移动光标
this.moveCursor(this.activeLine.left + w,
this.activeLine.bottom);
//绘制内容
this.draw(this.context);
context.restore();
}
}
};
(4)基本功能
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
GRID_STROKE_STYLE = 'lightgray',
GRID_HORIZONTAL_SPACING = 10,
GRID_VERTICAL_SPACING = 10,
drawingSurfaceImageData,
//光标
cursor = new TextCursor(),
paragraph;
//--------------------------------4、基本功能
//设置字符样式
cursor.fillStyle = "red";
cursor.strokeStyle = "red";
//路径粗细
context.lineWidth = 2.0;
setFont();
//保存数据
saveDrawingSurface();
//坐标转换
function windowToCanvas(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
return { x: x - bbox.left * (canvas.width / bbox.width),
y: y - bbox.top * (canvas.height / bbox.height)
};
}
(5)保存画布数据
//-------------------------5、保存画布
//保存画布数据
function saveDrawingSurface() {
drawingSurfaceImageData = context.getImageData(0, 0,
canvas.width,
canvas.height);
}
//--------------------------设置文本
function setFont() {
context.font ='48px 宋体';
}
(6)鼠标事件处理
//----------------------6、鼠标事件处理
//鼠标按下
canvas.onmousedown = function (e) {
var loc = windowToCanvas(canvas, e.clientX, e.clientY),
fontHeight,
line;
//光标擦除
cursor.erase(context, drawingSurfaceImageData);
saveDrawingSurface();
if (paragraph && paragraph.isPointInside(loc)) {
paragraph.moveCursorCloseTo(loc.x, loc.y);
}
else {
fontHeight = context.measureText('W').width,
fontHeight += fontHeight/6;
paragraph = new Paragraph(context, loc.x, loc.y - fontHeight,
drawingSurfaceImageData,
cursor);
paragraph.addLine(new TextLine(loc.x, loc.y));
}
};
(7)键盘事件处理
//---------------------------7、键盘事件处理
//按键按下
document.onkeydown = function (e) {
if (e.keyCode === 8 || e.keyCode === 13) {
//阻止退格与回车
e.preventDefault();
}
if (e.keyCode === 8) { // 退格键
//进行退格处理,删除前字符
paragraph.backspace();
}
else if (e.keyCode === 13) { //回车键
//输出换行
paragraph.newline();
}
}
//按键释放
document.onkeypress = function (e) {
//将获取的值转为字符串
var key = String.fromCharCode(e.which);
//如果按下的不是空格,ctrl,alt键
if (e.keyCode !== 8 && !e.ctrlKey && !e.metaKey) {
//阻止默认行为
e.preventDefault();
//设置字符样式
context.fillStyle = "red";
context.strokeStyle = "red";
//插入字符
paragraph.insert(key);
}
}
显示效果: