canvas绘画板,canvas画布代码

  canvas绘画板,canvas画布代码

  最近,一个类似像素风格的画板应该在项目中实现。可以在小像素网格中擦除,可以改变方框的颜色,可以擦除各种图形。这样一个小项目,看似简单,却包含了相当多的东西。

  绘制像素格子

  我们先定义像素点阵类。

  Pixel=函数(option){ this . x=option . x;this . y=option . y;this . shape=option . shape;this . size=option . size 8;}x和Y代表中心点的坐标。一开始,我是这样做的,先定义路径。

  create path:function(CTX){ if(this . shape=== circle ){ this . create circle(CTX);} else if(this . shape=== rect ){ this . create rect(CTX);} else { this . create circle(CTX);}},create circle:function(CTX){ var radius=this . size/2;ctx.arc(this.x,this.y,半径,0,数学。PI * 2);},create rect:function(CTX){ var points=this . get points();points.forEach(function (point,i) { ctx[i==0?moveTo : lineTo](point.x,point . y);}) ctx.lineTo(点数[0]。x,点数[0]。y);},像素网格支持圆形和矩形。定义好路径后,画出来。

  draw:function(CTX){ CTX . save();CTX . line width=this . line width;CTX . stroke style=this . stroke style;CTX . fill style=this . fill style;CTX . begin path();this . create path(CTX);CTX . stroke();if(this . is fill){ CTX . fill();} CTX . restore();}然后通过循环批量创建像素网格:

  for(var I=stepX . 5;i canvas.widthI=stepX){ for(var j=stepY . 5;j canvas.heightj=stepY){ var Pixel=new Pixel({ x:I,y: j,shape: circle })box . push(Pixel);pixel . draw(CTX);}}这看起来很完美,但是有一个巨大的死亡。每一个像素都被画回到上下文中,画布的状态每次都会改变。这样做会导致渲染性能很差,因为像素很多。如果画布很大,性能很堪忧,画板上有一些操作,那么频繁的改变画布的状态是不合适的。

  所以,正确的做法是:要定义好所有的路径,最好是一批画成画布;

  //定义像素的位置为(var i=stepX .5i canvas.widthI=stepX){ for(var j=stepY . 5;j canvas.heightj=stepY){ var Pixel=new Pixel({ x:I,y: j,shape: circle })box . push(Pixel);} }//批量绘制console.time(time )。CTX . begin path();for(var c=0;c box.lengthc){ var circle=box[c];ctx.moveTo(circle.x 3,circle . y);circle . create path(CTX);} CTX . close path();CTX . stroke();console . time end( time );

  可以看出渲染效率很快,canvas的状态变化尽可能小,因为每次上下文的状态变化,canvas都会被重画,这是一个全局状态。

  像素网格交互

  项目的要求是在画布上按下鼠标移动就可以擦除像素,这里面包含了两个知识点,一个是如何获取鼠标移动路径上的像素网格,一个是性能问题,因为我们要求的要求是画8万个点。别的不说,光是循环就要几十上百毫秒,更别说画图渲染了。我们先来看第一个问题:

  获取鼠标移动路径下的网格

  看到这个问题,我们很容易想到写一个函数,让鼠标的位置包含那个网格,然后在每次鼠标移动的时候更新位置计算。这样可以满足要求,但是鼠标移动太快就不行了,每个点的位置都可以计算出来,效果会不一致。让我们想另一种方法。我们可以清楚地知道鼠标路径的起点和终点。我们将整个绘制路径想象成线段,因此问题变成了线段与原始路径相交的算法。线段是画笔的粗细,线段的路径是鼠标移动的路径,与它们相交的圆是需要改变样式的网格。代码的转换如下:

  函数sqr(x) { return x * x }函数dist2(p1,p2){ return sqr(P1 . x-p2 . x)sqr(P1 . y-p2 . y)}函数distToSegmentSquared(p,v,w) { var l2=dist2(v,w);if (l2==0)返回dist2(p,v);var t=((p . x-v . x)*(w . x-v . x)(p . y-v . y)*(w . y-v . y))/L2;if (t 0)返回dist2(p,v);if (t 1)返回dist2(p,w);return dist2(p,{ x: v.x t * (w.x - v.x),y:v . y t *(w . y-v . y)});}/* * * * @描述计算线段是否与圆相交* @param {x: num,y: num} p中心点* @param {x: num,y: num} v线段起点* @param {x: num,y: num} w线段终点*/函数变形(p,v,w var minX=Math.min(v.x,w . x)-offset;var maxX=Math.max(v.x,w.x)偏移量;var minY=Math.min(v.y,w . y)-offset;var maxY=Math.max(v.y,w.y)偏移量;if((p . x minX p . x maxX)(p . y minY p . y maxY)){返回数。MAX _ VALUE} return math . sqrt(distToSegmentSquared(p,v,w));}具体逻辑就不赘述了。你可以自己看代码。然后通过得到的相交网格,删除盒子里的数据,重新渲染,就可以看到效果了。

  用同样的方法,我们可以制作染色效果,然后我们可能会实现一个画布像素画板的小演示。但要做出染色效果,必须用第一种画法。每个像素必须是一个对象,因为每个对象的状态是独立的。不过性能方面不用担心,像素也不多,基本没有停滞感。实现效果一般如下:

  最近有点懒。就是这样。以后有时间再加一个上传图片,图片像素,导出的功能。

  这就是本文的全部内容。希望对大家的学习和支持有帮助。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: