我目前正在尝试创建在Canvas HTML-Element上绘制的画笔。它们最终应该像Photoshop-Brushes一样工作:点击 - 移动 - 沿着鼠标移动释放结果。
我已经实现了一个非常原始的画笔,但是这很快变得非常慢,here is a JSFiddle。尝试绘制多个笔划或很长的笔划,您会看到捕获点的距离变得更大。可能这种行为是通过清除整个画布并迭代所有保存的点而产生的,但我不太确定什么是更好的方法。这是主要的绘图代码:
// while moving the mouse, each captured point gets saved into
// a multi-dimensional array of strokes; then, at each event
// (mousemove, mousedown and mouseup) this function is called
PrimitiveBrush.prototype._draw = function () {
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height)
for (var i = 0, l = this.strokes.length; i < l; i++) {
this.ctx.moveTo(this.strokes[i][0].x, this.strokes[i][0].y);
for (var j = 1, m = this.strokes[i].length; j < m; j++) {
var point = this.strokes[i][j];
this.ctx.lineTo(point.x, point.y);
}
}
this.ctx.stroke();
};
有什么想法吗?
答案 0 :(得分:2)
我看到你是从mousemove缓存点并在requestAnimationFrame中绘制它们。
尼斯!
将鼠标移动捕捉与绘图分开,从而使绘图与屏幕刷新很好地协调。
有人建议重构:
因此,不要在this.strokes中绘制每个点数组,只需绘制最后一个点数组。
为了获得更好的性能,只需在最后一个点数组中绘制未绘制的点。
(可选)(在此结束时):清除画布并重新绘制所有点,以获得更清晰的最终绘图。
示例代码和演示:http://jsfiddle.net/m1erickson/wAGcL/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// get reference to canvas and save canvas offsets
var canvas = document.getElementById('drawing');
var offsetX=canvas.offsetLeft;
var offsetY=canvas.offsetTop;
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
/**
* @param {CanvasRenderingContext2D} context
*/
function PrimitiveBrush(context) {
if (!(context instanceof CanvasRenderingContext2D)) {
throw new Error('No 2D rendering context given!');
}
this.ctx = context;
this.strokes = [];
this.strokeIndex=0;
this.workingStrokes=[];
this.lastLength=0;
this.isTouching = false;
// init context
this.ctx.strokeStyle = '#f00';
this.ctx.lineWidth = '3';
this.ctx.lineCap = this.ctx.lineJoin = 'round';
// start the drawing loop
this._draw();
}
/**
* Begins a new stroke
* @param {MouseEvent} event
*/
PrimitiveBrush.prototype.start = function (event) {
var x=event.clientX-offsetX;
var y=event.clientY-offsetY;
this.workingStrokes=[{x:x,y:y}];
this.strokes.push(this.workingStrokes);
this.lastLength=1;
this.isTouching = true;
};
/**
* Moves the current position of our brush
* @param {MouseEvent} event
*/
PrimitiveBrush.prototype.move = function (event) {
if(!this.isTouching){return;}
var x=event.clientX-offsetX;
var y=event.clientY-offsetY;
this.workingStrokes.push({x:x,y:y});
};
/**
* Stops a stroke
* @param {MouseEvent} event
*/
PrimitiveBrush.prototype.end = function (event, foo) {
this.move(event);
this.isTouching = false;
};
PrimitiveBrush.prototype._draw = function () {
requestAnimationFrame(this._draw.bind(this));
// save the current length quickly (it's dynamic)
var length=this.workingStrokes.length;
// return if there's no work to do
if(length<=this.lastLength){return;}
var startIndex=this.lastLength-1;
this.lastLength=length;
var pt0=this.workingStrokes[startIndex];
this.ctx.beginPath();
this.ctx.moveTo(pt0.x,pt0.y);
for(var j=startIndex;j<this.lastLength;j++){
var pt=this.workingStrokes[j];
this.ctx.lineTo(pt.x,pt.y);
}
this.ctx.stroke();
};
// Set up brush to listen to events
var brush = new PrimitiveBrush(canvas.getContext('2d'));
canvas.addEventListener('mousedown', brush.start.bind(brush));
canvas.addEventListener('mousemove', brush.move.bind(brush));
canvas.addEventListener('mouseup', brush.end.bind(brush));
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="drawing" width=300 height=300></canvas>
</body>
</html>
也只是一个性能点:调用makePoint会降低性能。
答案 1 :(得分:1)
以下两种方法可以实现更快的线条绘制:
方法1:(推荐)
如果您需要或希望能够绘制很长的线条,您可以随时分开并进行传输以减少重绘的点数。
方法2:
当你绘制线条末端时,质量稍低一些,但它会在鼠标向上固定:
原理的一个非常简短的例子(伪代码):
var lastX, lastY, isDown = false, ...
canvas.onmousedown = function(e) {
var pos = getXY(e); // get mouse position somehow
isDown = true;
lastX = pos.x;
lastY = pos.y:
]
canvas.onmousemove = function(e) {
if (!isDown) return;
var pos = getXY(e); // get mouse position somehow
drawLine(lastX, lastY, pos.x, pos.y);
points.push(pos); // store point somewhere
lastX = pos.x;
lastY = pos.y:
]
等
您可以立即修复当前代码中的另一件事是在beginPath()
方法中插入_draw()
。只是这样做会提高速度,因为现在发生的是你将每个点累积到当前路径,并且对于每个新点,你绘制所有前一行的所有路径向量,并重绘以及重绘的点。循环(这意味着每个新点都有重绘的指数量。)
PrimitiveBrush.prototype._draw = function () {
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height)
this.ctx.beginPath(); // insert here
for (var i = 0, l = this.strokes.length; i < l; i++) {
this.ctx.moveTo(this.strokes[i][0].x, this.strokes[i][0].y);
for (var j = 1, m = this.strokes[i].length; j < m; j++) {
var point = this.strokes[i][j];
this.ctx.lineTo(point.x, point.y);
}
}
this.ctx.stroke();
};
但它不会解决主要问题 - 考虑使用上述两种方法中的一种。
希望这有帮助。