我有一个简单的Canvas绘图应用程序。 有时 lineTo()命令会产生一条坐标较少的线条,并且图形有很多边缘:
我正在使用最新的firefox,是因为连接错误还是计算机出现问题?有工作吗? 这是我的代码:JS FIDDLE
beginPath();
moveTo(this.X, this.Y);
lineTo(e.pageX , e.pageY );
strokeStyle = "rgb(0,0,0)";
ctx.lineWidth=3;
stroke();
答案 0 :(得分:4)
它尽可能快地响应。您的浏览器将尽可能快地提供事件,但不能保证能够跟踪您移动鼠标。很多都与客户端计算机上的负载有关。
编辑 - here is a modified fiddle展示了一些可能会让它变得更好的方法。该版本保留了一个单独的“点”队列,每50毫秒绘制一个新点。这使得“mousemove”处理程序只需要记录事件中的点坐标,并且当鼠标快速移动时,绘图代码可以使用一个画布更新来执行一堆点。它仍然不完美。
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var width = window.innerWidth;
var height = window.innerHeight;
canvas.height = height;
canvas.width = width;
canvas.addEventListener('mousedown', function(e) {
this.down = true;
points.setStart(e.pageX, e.pageY);
}, 0);
canvas.addEventListener('mouseup', function() {
this.down = false;
}, 0);
canvas.addEventListener('mousemove', function(e) {
if (this.down) {
points.newPoint(e.pageX, e.pageY);
}
}, 0);
var points = function() {
var queue = [], qi = 0;
var ctx = canvas.getContext('2d');
function clear() {
queue = [];
qi = 0;
}
function setStart(x, y) {
clear();
newPoint(x, y);
}
function newPoint(x, y) {
queue.push([x, y]);
}
function tick() {
var k = 20; // adjust to limit points drawn per cycle
if (queue.length - qi > 1) {
ctx.beginPath();
if (qi === 0)
ctx.moveTo(queue[0][0], queue[0][1]);
else
ctx.moveTo(queue[qi - 1][0], queue[qi - 1][1]);
for (++qi; --k >= 0 && qi < queue.length; ++qi) {
ctx.lineTo(queue[qi][0], queue[qi][1]);
}
ctx.strokeStyle = "rgb(0,0,0)";
ctx.lineWidth = 3;
ctx.stroke();
}
}
setInterval(tick, 50); // adjust cycle time
return {
setStart: setStart,
newPoint: newPoint
};
}();
答案 1 :(得分:2)
您可以使用基数样条线来平滑这样的线条:
由于浏览器能够以多快的速度响应事件(mousemove
),因此@Pointy已经解释了原因。有一个名为Pointer Lock API的API可能有助于解决此问题,因为它更低级别,但是现在我们需要使用算法来平滑因此而出现分段的行。
除了平滑之外,还有细节平滑,缩小点,锥度等可用于改善效果的东西。
但在这种特殊情况下,您可以使用我作为画布扩展的以下函数。只需称呼它:
ctx.curve(myPointArray, tension, segments);
ctx.stroke();
数组包含您的x和y点,如[x1, y1, x2, y2, ... xn, yn
。
tension
的典型值为0.5。 segments
(默认为16)是可选的。
张力越大,曲线越圆。分段是阵列中每个点之间的分辨率。对于绘图应用程序,值5可能正常工作(结果点较少)。
为了使其更好地工作,您可以在绘制原始线条的单独画布上注册您的点。在鼠标向上处理具有此功能的行并将其绘制到主画布,然后清除绘图画布。
此功能经过高度优化 - 它还会返回已处理的点,以便您可以存储结果,而不是每次都重新处理。
/**
* curve() by Ken Fyrstenberg (c) 2013 Epistemex
* See Code Project for full source:
* http://www.codeproject.com/Tips/562175/Draw-Smooth-Lines-on-HTML5-Canvas
*/
CanvasRenderingContext2D.prototype.curve = function(pts, ts, nos) {
nos = (typeof numOfSegments === 'undefined') ? 16 : nos;
var _pts = [], res = [], // clone array
x, y, // our x,y coords
t1x, t2x, t1y, t2y, // tension vectors
c1, c2, c3, c4, // cardinal points
st, st2, st3, st23, st32, // steps
t, i, l = pts.length,
pt1, pt2, pt3, pt4;
_pts.push(pts[0]); //copy 1. point and insert at beginning
_pts.push(pts[1]);
_pts = _pts.concat(pts);
_pts.push(pts[l - 2]); //copy last point and append
_pts.push(pts[l - 1]);
this.moveTo(pts[0], pts[1])
for (i = 2; i < l; i+=2) {
pt1 = _pts[i];
pt2 = _pts[i+1];
pt3 = _pts[i+2];
pt4 = _pts[i+3];
// calc tension vectors
t1x = (pt3 - _pts[i-2]) * ts;
t2x = (_pts[i+4] - pt1) * ts;
t1y = (pt4 - _pts[i-1]) * ts;
t2y = (_pts[i+5] - pt2) * ts;
for (t = 0; t <= nos; t++) {
// pre-calc steps
st = t / nos;
st2 = st * st;
st3 = st2 * st;
st23 = st3 * 2;
st32 = st2 * 3;
// calc cardinals
c1 = st23 - st32 + 1;
c2 = st32 - st23;
c3 = st3 - 2 * st2 + st;
c4 = st3 - st2;
res.push(c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x);
res.push(c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y);
} //for t
} //for i
l = res.length;
for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]);
return res;
} //func ext
有关基数样条曲线的实现,请参见this answer。