HTML5实例教程——创意画板

在HTML5备受期待和瞩目的今天,愈来愈多的人已经感觉到它带来的无限魅力与震撼力,许多的技术人员、设计者、互联网爱好者们纷纷加入了HTML5的研究与设计中。javascript

首先我先为你们介绍一下一个功能很强大的HTML5在线绘画应用,它还拥有多种笔刷和滤镜,具备相似于photoshop的图层功能,可调节透明度隐藏等,还有渐变、油漆桶、拾色器、选择工具,你们必定会为此感到惊讶吧。html

clipboard.png

但这样复杂的应用并无使用flash实现,在canvas标记尚未出现以前,要想实现复杂的网页应用,或者直接在网页上进行绘图,只能借助于第三方的插件,好比Flash或Java,而如今,借助于canvas标记,咱们能够实现图像显示和处理了,那么如今就让我抛砖引玉,讲解一下个人一些开发思路吧。html5

想要制做一个简单的画板并非太难,但我建议您掌握必定的canvas基础和javascript基础,这样更便于理解和学习本教程。而若是你canvas技术比较好的话,你必定会以为本教程又长又啰嗦,可是教程不可能顾及到全部的阅读者,因此麻烦你跳过你了解的部分,只关注重要的部分就行了。java

首先,我讲解一下个人开发思路。咱们须要在页面中添加一个canvas标记做为咱们的画布,也就是咱们未来要绘画的画板。因为须要用户使用鼠标点击、滑动、释放鼠标等操做来实现绘画,因此咱们也必需要使用鼠标的几个基本的监听事件mousemove、mouseup、mousedown。web

document.addEventListener('mousemove', mouseMove, false);
document.addEventListener('mousedown', mouseDown, false);
document.addEventListener('mouseup', mouseUp, false);

为了使绘画出来的线条更流畅,兼顾性能问题,咱们能够采用setInterval来设置监听事件的时间间隔。 setInterval(函数名,1000/60); 其中1000/60为时间间隔。编程

setInterval(loop, 1000 / 60);
function loop() {
    $pos_display.innerHTML='你当前鼠标的位置为('+pos.x+','+pos.y+')';
    if (isMouseDown) draw(context);}

loop为循环执行的函数。canvas

固然,你也能够采用requestAnimationFrame(若是不了解该属性能够自行百度^_^)。这取决于你的习惯。数组

那么如今咱们须要获取用户鼠标点击的位置,在这里咱们须要区分pageX,clientX,offsetX,layerX等概念 ,这里有篇文章讲解,你能够看看http://www.funnyhao.com/pagex-clientx-offsetx-layerx-of-those-things/服务器

因为咱们如今画布直接放在页面上左上部,padding和margin都为0,所以咱们直接用clientX和clientY便可.当用户第一次点击鼠标时,咱们设置isMouseDown为true,开启绘画模式。websocket

function mouseDown(e) {
    isMouseDown = true;
}

获取了用户点击的位置后,咱们在约定的时间间隔后(1/60秒)再次获取用户所在的位置,并进行更新

function loop() {
    if (isMouseDown) draw(context);//绘制鼠标点击位置
}
function mouseMove(e) {
    pos.x=e.clientX;//设置x坐标
    pos.y=e.clientY;//设置y坐标
    $pos_display.innerHTML='你当前点击鼠标的位置为('+pos.x+','+pos.y+')';//更新当前鼠标点击的位置
}

接下来咱们就能够绘制了

function draw(ctx) {
    ctx.save();//保存当前绘图状态
    ctx.fillStyle = DEFAULT_BRUSH_COLOR;//设置填充的背景颜色
    ctx.lineWidth =DEFAULT_BRUSH_SIZE;  //设置画笔的大小
    ctx.lineCap = "round"; //设置线条,让线条边缘更圆滑
    ctx.beginPath();
    ctx.arc(pos.x,pos.y,DEFAULT_BRUSH_SIZE,0,Math.PI * 2,true);
    /****
    *context.arc(x, y, radius, startAngle, endAngle, anticlockwise)
    *参数 x,y表示圆心
    *radius半径
    *startAngle起始弧度
    *endAngle终止弧度
    *anticlockwise是否为逆时针方向
    ***/
    ctx.fill();//填充绘画路径
    ctx.restore();//恢复绘画状态
}

彷佛这样的大功告成了。看这里的演示代码:DEMO1(http://runjs.cn/detail/gxeeyocw)

clipboard.png

当咱们画画时,若是绘制笔移动的较快的时候,就会发现出现了断断续续的状况,这是怎么回事呢?原来咱们只设置了一个点每过1/60秒就更新一下位置,当咱们绘图时若是画笔移动的速度够快时绘制的不够密集,绘制的点久不能链接起来,从而引发断续的现象。

可能会有些人说能够设置时间间隔更小,好比设置为1/1000秒,也就是将页面中的代码

setInterval(loop, 1000 / 60);

改成

setInterval(loop, 1000 / 1000);

甚至无穷小,这样不就解决了吗。可是相信不少人都不会推荐这样的方法,由于这不只仅会影响到页面的效率,并且也没有从根本上解决问题,setinterval调用间隔的时间每每会有诸多限制,因此这样的方法是行不通的。

要让线连贯起来最简单的方法:那就用线连起来吧。(旁白:废话,⊙﹏⊙b汗)咱们知道两点肯定一条直线,因此只要咱们肯定两个点的坐标便可。亦即每一个时间间隔单位,咱们获取一次当前点的坐标就行了。而后使用canvas的moveTo函数移动下一个点,记录当前点坐标和上一个点的坐标,并使用canvas的lineTo函数将线连起来,而后不要忘了用stroke函数绘制出来,具体看这里的代码:DEMO2(http://runjs.cn/detail/r52qaltg)。

clipboard.png

咱们经过表格比较一下这两种方案的区别:

clipboard.png

表格中明显看出方案一都是孤立的点,而方案二每一个点都会有两种状态,将这两种状态下的点连起来就会造成衔接的较好的效果。

由于基础的内容在上面已经讲述了,因此在这里我也不重复了,须要注意的是当前点与上一个点重复时须要作一下处理,不然页面没法绘制出来。

if(pos.x==next_pos.x&&pos.y==next_pos.y){
    ctx.arc(pos.x,pos.y,DEFAULT_BRUSH_SIZE/1.7,0,Math.PI * 2,true);
    ctx.fill();//填充绘画路径}
else{
    ctx.moveTo(pos.x,pos.y);
    ctx.lineTo(next_pos.x,next_pos.y);
    ctx.stroke();
}

为了方便讲解,我这里采用的都是面向过程的方法,在比较大的应用中,咱们要尽量采用面向对象的方法,好处是不言而喻的,不只能让代码条理清晰,更有较好的扩展性,方便二次开发和模块复用。使用面向对象方法的代码请查看这里(这里会使用了point函数类,覆盖了set和update等方法)请查看DEMO3_1(http://runjs.cn/detail/gvfyrswu)。

研究技术的时候,咱们须要触类旁通,显然如今的方法仍是不够完善。能不能将全部的点都记录下来,由于每一个时间间隔单位,都会损失掉不少的点,为了让画出来的图更加圆滑,咱们要将全部的点都记录下来,而且效率又能获得优化,我在这里提出一个解决方案。

用数组记录下全部的路径,而后用堆栈的push方法将点添加到数组中,为了达到更好的效率,咱们能够采用一维数组,分别用两个数组记录横坐标和纵坐标,具体的实现我就不贴代码了,你们有余力的话能够看成一个小小的做业,参照个人这个页面例子本身编写代码实现,页面中会有代码注释的。

咱们实现了绘制功能,咱们还须要对绘制的图片进行擦除。不要尝试采用transparent或者rgba(x,x,x,0)这样的颜色值绘制,由于这样页面便不会绘制出任何东西,最实用的方法就是绘制背景颜色,若是背景是图片,那就重绘背景图片,而后就原来的内容的其余部分绘制到画布中。具体查看demo3_2(http://runjs.cn/detail/jywf4qv1

clipboard.png

那若是咱们要实现蜡笔的效果。要怎么处理呢,若是咱们将蜡笔画放大后看就会知道那是一些很小的分散颗粒状大小的粒子,这样咱们就有了思路了。咱们仍是沿用DEMO3的例子,在其基础上进行开发,须要注意的一点是粒子的分布问题,如何才能将粒子均匀的分布呢,不知道大伙们这么久没学数学是否是都将知识还给老师了。这里咱们会用到一些基本的数学知识, 具体思路请看下图,

clipboard.png

请参照源代码

draw: function(ctx) {
        var v = this.subtract(this._latest);//当前点与下一个点的距离的横纵坐标
        var s = Math.ceil(this.size / 2);       //算出粒子的单位长度
        var stepNum = Math.floor(v.length() / s) + 1;   //算出步长  v.length()为斜线长度
        v.normalize(s);//当前点与下一个点的

        var sep = 1.5; // 分割数  控制画笔的浓密程度  关键所在
        //粒子的大小 根据画笔描绘的速度(画笔的停留时间)进行调整
        var dotSize = sep * Math.min(this.inkAmount / this._latestStrokeLength * 3, 1);
        var dotNum = Math.ceil(this.size * sep);
        var range = this.size / 2;
        var i, j, p, r, c, x, y;
        $whitemode_display.innerHTML="绘制的画笔颜色是"+brush_color;
        ctx.save();
        ctx.fillStyle = currentColor;
        $pos_display.innerHTML='你上一点鼠标的位置为('+this.x+','+this.y+').你当前鼠标的位置为('+this._latest.x+','+this._latest.y+')';//更新当前鼠标点击的位置
        ctx.beginPath();
        if(wmode=="擦除模式"){
            ctx.strokeStyle=brush_color;
            ctx.lineWidth =DEFAULT_BRUSH_SIZE;
            ctx.lineCap = "round";
            ctx.beginPath();
            p = this._latest;//获取下一个点位置
            ctx.moveTo(this.x,this.y);
            ctx.lineTo(p.x,p.y);
            ctx.stroke();
        }
        else{
            for (i = 0; i < dotNum; i++) {
                for (j = 0; j < stepNum; j++) {
                    p = this._latest.add(v.scale(j));
                    r = random(range);
                    c = random(Math.PI * 2);
                    w = random(dotSize, dotSize / 2);
                    h = random(dotSize, dotSize / 2);
                    x = p.x + r * Math.sin(c) - w / 2;
                    y = p.y + r * Math.cos(c) - h / 2;
                    ctx.rect(x, y, w, h);//边缘不要太平滑,不要使用arc
                }
            }
        }
        ctx.fill();
        ctx.restore();
    }

  });

进行分析比较。思路的重点是在必定间隔后对粒子进行随机分散排布,并能处理在画笔移动的比较快的时候的的绘制问题。为了获得更好的展现效果,咱们通常还会控制透明度进行调整。

处理完这些后,咱们若是喜欢这样的图片,还可使用图片导出功能,方法也挺简单。去掉监听事件,使用canvas的toDataURL内置函数,而后展现到新打开的窗口中。

咱们就能够运行一下源代码看看带擦出功能和图片导出功能的实际效果如何:

clipboard.png

舒适提示:在键盘上输入p键能够导出图片,图片导出功能因为在新窗口中打开,请使用全屏预览模式并容许窗口弹出。

其实咱们还能够有更多变化,只要你去构想,去思考。不少时候你都要去尝试,每每屡次尝试才会有新的ideas。接下来咱们但是作出如下特殊的画笔,好比说钢笔效果,边缘会比较笔触比较重的。钢笔效果须要将画笔尺寸调小,减弱抽丝的效果,而且边缘的锯齿会比较明显,因此须要作一下阴影模糊处理,让其过渡更平滑。

具体源码看这里:demo5(http://runjs.cn/detail/df5u6cb5

clipboard.png

以前曾在这里见过一个毛笔画网站,因此也模拟了一下毛笔的效果,毛笔的特色是笔触比较大,当收笔较快时边缘要凹凸不平,笔尖写字锋棱易出。当收笔有停顿时则会圆润而浑厚。有个相似的线上应用,你们能够去研究研究:http://www.theshodo.com/Write

![clipboard.pn
clipboard.png
C)

![图片上传中...]

根据这些特色,我给你们提供一个DEMO源码,是在钢笔效果的基础上作些小小的调整的。

若是你们还以为这样的效果是否还能够添加点特效什么的,对,能够作杂点斑点的效果,还有墨水过多而流下的效果。

具体能够参看DEMO6(http://runjs.cn/detail/ully3puv)和DEMO7。

clipboard.png

clipboard.png

源码我就不进行分析了,留待你们本身去研究,将画笔颜色调为黑色就差很少能够模拟出毛笔的效果了。在我看来,技术的专研不是一味的让别人教你,而是让你本身去领悟的。只有那样你才真正学到技术,领略到不同的乐趣。

说些设计之外的东西,设计、编程都须要有本身的思想和灵魂,真正让别人也能感觉到你的思路,而这一切都须要磨练,须要触类旁通。不该该知足于现状,我在这里只列举了其中一些效果,我相信还有不少效果能够实现,好比说相似于这样的喷雾效果,铅笔字效果,艺术画效果,等等。既然说HTML5创意画板,那就要尝试脱离这个画板的束缚,学到更多的东西,好比说你能够用这个画板作什么,能够作一个记事本、涂鸦工具、处理和分享图片,个性签名,你还能够作一些小游戏,涂鸦类的游戏,你画我猜(须要采用websocket实现服务器端双向通讯)等等,甚至能够作一些canvas动画,这些基础上作些修改和调整,彻底是能够实现的。有了目标和思路,那就沿着这个方向去学习,我相信你必定会有所收获的。

此次的HTML5绘图教程就到这里,你们还能够尝试为此添加更多的个性化的功能,同时欢迎你们留言提问或者提出批评建议。

源码下载:

runJS: http://runjs.cn/detail/spxs2kxq

微盘:http://vdisk.weibo.com/s/otSnZ

百度网盘:http://pan.baidu.com/share/link?shareid=194573&uk=3744164386

扩展阅读:

在线毛笔画板:http://www.theshodo.com/Write

来自deviants的在线画板:http://sta.sh/muro/

若是想查看以前的FLASH版本,能够点击这里http://www.inzrb.com/blog/?page_id=211

via sina udc

相关文章
相关标签/搜索