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