var drawGrid = function(w, h, id) {
var canvas = document.getElementById(id);
var ctx = canvas.getContext('2d');
ctx.canvas.width = w;
ctx.canvas.height = h;
for (x=0;x<=w;x+=20) {
for (y=0;y<=h;y+=20) {
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.stroke();
}
}
};
drawGrid(800, 400, "grid");
<canvas id="grid"></canvas>
#1 楼
Quill已经很好地加快了代码的速度,因此,我将重点介绍您现在拥有的代码。给我这个对象!
(不是如何获得它)
/>您的
drawGrid
函数采用了该函数希望自己找到的DOM元素的id
。但是,这不是一个好习惯。相反,您应该传递DOM元素本身。如果使用得当,这也可以大大提高代码的效率。要详细了解为什么它是不好的做法,请这样考虑:如果在找到元素之前需要对元素进行一些特定的检查,您已准备好将其提供给函数,应该如何将准备好的元素传递给函数?
不要让函数担心如何获取所需的内容;只需提供所需的内容即可。
获取我的上下文信息!现在就扔掉吧!
var ctx = canvas.getContext('2d');
每次调用函数时都会创建一个。假设此代码中将进行更多绘制,为什么不将画布和上下文都放在全局范围内呢?找到上下文,将其放在变量中,然后在每次调用它时在函数末尾销毁它没有意义。
询问上下文所在的画布。然后,询问画布的位置。
var canvas = document.getElementById(id);
...
ctx.canvas.width = w;
ctx.canvas.height = h;
这绝对是零意义。首先,使用画布获取
ctx
。然后,使用ctx
通过访问其属性再次获取canvas
。为什么不仅仅使用您刚定义的canvas
?总是有另一种方式
是的,当前形成网格的方式非常慢。正如Quill所示,还有其他选择:
将其渲染为SVG(Quill已经显示了此内容)
获取单个图像作为握把的一部分,然后复制面食您所需区域周围的图像。
绘制正方形。
我没有测试这些,所以我不知道它们提供多少速度提升(如果有)。但是,请随时尝试其中的一些。
评论
\ $ \ begingroup \ $
谢谢。您能否阐明您的第一点“给我对象”。您是在说我应该完全使用JavaScript来创建canvas元素还是在函数定义中硬编码canvas id?我想使它尽可能通用,以重用相同的功能在同一网站上创建其他画布。不鼓励吗?谢谢
\ $ \ endgroup \ $
–荒原
15年12月22日在10:58
\ $ \ begingroup \ $
顺便说一句,我可能应该澄清一下,我正在做一个数学网站,以说明/模拟一些数学概念供学习者练习。
\ $ \ endgroup \ $
–荒原
15年12月22日在11:00
\ $ \ begingroup \ $
@Wasteland我是说您应该将已经找到的JavaScript对象传递给函数,而不仅仅是ID。
\ $ \ endgroup \ $
– SirPython
15年12月22日在22:44
\ $ \ begingroup \ $
过多的图纸也无助于提高性能
\ $ \ endgroup \ $
– Deian
16年7月19日在19:00
\ $ \ begingroup \ $
我可能是不正确的,但是我相信您应该在每帧请求上下文,因为上下文在下一帧无效。不过,这可能是我正在使用的其他系统。
\ $ \ endgroup \ $
–埃里克·布莱德
16年7月20日在5:25
#2 楼
尝试在画布上使用SVG对象:<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="smallGrid" width="8" height="8" patternUnits="userSpaceOnUse">
<path d="M 8 0 L 0 0 0 8" fill="none" stroke="gray" stroke-width="0.5" />
</pattern>
<pattern id="grid" width="80" height="80" patternUnits="userSpaceOnUse">
<rect width="80" height="80" fill="url(#smallGrid)" />
<path d="M 80 0 L 0 0 0 80" fill="none" stroke="gray" stroke-width="1" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#smallGrid)" />
</svg>
而将
fill="url(#smallGrid)"
更改为fill="url(#grid)"
会产生以下结果:然后您可以像下面那样将其放入画布中:
var drawGrid = function(w, h, id) {
var canvas = document.getElementById(id);
var ctx = canvas.getContext('2d');
ctx.canvas.width = w;
ctx.canvas.height = h;
var data = '<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"> \
<defs> \
<pattern id="smallGrid" width="8" height="8" patternUnits="userSpaceOnUse"> \
<path d="M 8 0 L 0 0 0 8" fill="none" stroke="gray" stroke-width="0.5" /> \
</pattern> \
<pattern id="grid" width="80" height="80" patternUnits="userSpaceOnUse"> \
<rect width="80" height="80" fill="url(#smallGrid)" /> \
<path d="M 80 0 L 0 0 0 80" fill="none" stroke="gray" stroke-width="1" /> \
</pattern> \
</defs> \
<rect width="100%" height="100%" fill="url(#smallGrid)" /> \
</svg>';
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
}
img.src = url;
}
drawGrid(800, 400, "grid");
<canvas id="grid"></canvas>
当然,您需要尝试调整网格的大小以匹配所需的内容。但是,您会发现此方法要快得多。
评论
\ $ \ begingroup \ $
预先准备数据,将数据blob并对其进行所有处理可能会更快(可能是初始化过程的一部分)。然后,当需要绘制数据时,它应该更快,因为数据将准备就绪。
\ $ \ endgroup \ $
– SirPython
2015年12月22日,0:43
\ $ \ begingroup \ $
谢谢Quill-我对SVG的经验非常有限-可以轻松地用js操作吗?
\ $ \ endgroup \ $
–荒原
15/12/22在11:08
\ $ \ begingroup \ $
SVG与HTML非常相似,它遵循
\ $ \ endgroup \ $
– Quill
15/12/22在11:10
\ $ \ begingroup \ $
我建议将SVG标记移动到类似script标签的位置。这样,您的SVG将不会出现在JS中。如果可以使用ES6,则另一种选择是模板字符串。至少这避免了每行末尾的\。
\ $ \ endgroup \ $
–约瑟夫
2015年12月22日13:00
#3 楼
您多次渲染同一条线。要创建网格,您需要绘制(w / stepSize)垂直线和(h / stepSize)水平线。
总计:( w + h)/ stepSize行
您使用了两个嵌套循环,并绘制了:(w / stepSize)*(h / stepSize)=(w * h)/(stepSize * stepSize)行
为您介绍一些示例,请参见以下示例:
对于w = 1000,h = 1000,stepSize = 20。
您的函数将绘制2500条线,其中只有100条线
对于w = 800,h = 400,stepSize = 20。
您的函数将绘制800条线,而只有60条线就足够。
在此外,您应该将canvas调整大小放在该函数之外,并且在程序开始时仅执行一次!
// the canvas logic should be done once somewhere else
var w = 800;
var h = 400;
// grid step
var step = 20;
var canvasElementId = 'grid';
var canvas = document.getElementById(canvasElementId);
// this is how you resize the canvas
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
// the render logic should be focusing on the rendering
var drawGrid = function(ctx, w, h, step) {
ctx.beginPath();
for (var x=0;x<=w;x+=step) {
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
}
// set the color of the line
ctx.strokeStyle = 'rgb(255,0,0)';
ctx.lineWidth = 1;
// the stroke will actually paint the current path
ctx.stroke();
// for the sake of the example 2nd path
ctx.beginPath();
for (var y=0;y<=h;y+=step) {
ctx.moveTo(0, y);
ctx.lineTo(w, y);
}
// set the color of the line
ctx.strokeStyle = 'rgb(20,20,20)';
// just for fun
ctx.lineWidth = 5;
// for your original question - you need to stroke only once
ctx.stroke();
};
drawGrid(ctx, w, h, step);
<canvas id="grid"></canvas>
评论
\ $ \ begingroup \ $
欢迎使用代码审查!恭喜您写出了一个很好的答案,请随时坚持:-)
\ $ \ endgroup \ $
–桅杆
16年7月19日在20:11
\ $ \ begingroup \ $
我读过的第一个好答案
\ $ \ endgroup \ $
–多米尼克
20/11/29在9:52
#4 楼
您无缘无故地重复运行三个语句。尝试以下操作:var drawGrid = function(w, h, id) {
var canvas = document.getElementById(id);
var ctx = canvas.getContext('2d');
ctx.canvas.width = w;
ctx.canvas.height = h;
for (x = 0; x <= w; x += 20) {
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
for (y = 0; y <= h; y += 20) {
ctx.moveTo(0, y);
ctx.lineTo(w, y);
}
}
ctx.stroke();
};
drawGrid(800, 400, "grid");
注意,我将内部for循环的前3条语句移到了哪里。差异很大。
评论
\ $ \ begingroup \ $
我真的不明白-为什么您需要嵌套循环?
\ $ \ endgroup \ $
– Deian
16年7月20日在20:09
\ $ \ begingroup \ $
@deian内循环是绘制垂直线。根据此处其他人的建议,您可以将ctx.stroke()移至循环之外。
\ $ \ endgroup \ $
–将
16-09-17在22:10
#5 楼
一个有趣的问题,赏金显然应该去死。似乎没有人注意到
x
和y
是全局变量。 除此之外,我将使用
line
函数(速度稍慢,但阅读起来更直观)。//Draw the line on the context, caller needs to worry about `stroke()`
function pencilLine( ctx, fromX, fromY, toX, toY ){
ctx.moveTo( fromX, fromY );
ctx.lineTo( toX, toY );
}
评论
\ $ \ begingroup \ $
为什么不在函数中也包含ctx.stroke()?
\ $ \ endgroup \ $
– 200_success
16年7月20日在16:45
\ $ \ begingroup \ $
因为如果您绘制200条线,那么您就不想进行200划。
\ $ \ endgroup \ $
– konijn
16年7月20日在16:48
\ $ \ begingroup \ $
我倾向于用铅笔在那些打算大量使用的例程之前加上前缀,以表明它们不会在行中出现。
\ $ \ endgroup \ $
– konijn
16年7月20日在16:48
\ $ \ begingroup \ $
@konijn x,y的荣誉当然应该是本地变量!
\ $ \ endgroup \ $
– Deian
16年7月20日在20:08
评论
您是否尝试过在画布上使用背景图片?您应该能够使用重复的SVG(可变大小)背景。不,我没有。我会尝试看看区别。
3秒真是疯狂!电脑是什么,那是我的即时信息。