所以首选大波浪进度图必威手机官网:,谈到图

canvas图形绘制之星空、噪点与烟雾效果

2016/06/07 · HTML5 · 1 评论 · Canvas

原文出处: 张鑫旭(@张鑫旭)   

一直以来都没有对Canvas进行深入的学习,工作中的项目也基本上没有涉及到Canvas的技术知识,所以这次前端小组选择研究方向,就选择了Canvas作为其中的一个方向。

基于HTML5 Canvas的网页画板实现教程,html5canvas

HTML5的功能非常强大,尤其是Canvas的应用更加广泛,Canvas画布上面不仅可以绘制任意的图形,而且可以实现多种多样的动画,甚至是一些交互式的应用,比如网页网版。这次我们要来看的就是一款基于HTML5 Canvas的网页画板,在这里仅对一些关键性的代码进行记录,大家也可以下载全部源代码研究。

必威手机官网 1

你也可以再这里查看在线演示

下面我们来简单地分析一下实现这个HTML5网页画板的原理及代码,代码由HTML以及Javascript组成,主要还是Javascript代码。

HTML5 Canvas玩转酷炫大波浪进度图效果实例(附demo),html5canvas

本文介绍了HTML5 Canvas玩转酷炫大波浪进度图效果,具体如下:

必威手机官网 2

如上图所见,本文就是要实现上面那种效果。

由于最近AlloyTouch要写一个下拉刷新的酷炫loading效果。所以首选大波浪进度图。

首先要封装一下大波浪图片进度组件。基本的原理是利用Canvas绘制矢量图和图片素材合成出波浪特效。

了解quadraticCurveTo

quadraticCurveTo() 方法通过使用表示二次贝塞尔曲线的指定控制点,向当前路径添加一个点。

JavaScript 语法:

context.quadraticCurveTo(cpx,cpy,x,y);

参数值

cpx 贝塞尔控制点的 x 坐标

cpy 贝塞尔控制点的 y 坐标

x 结束点的 x 坐标

y 结束点的 y 坐标

如:

ctx.moveTo(20,20);
ctx.quadraticCurveTo(20,100,200,20);
ctx.stroke();

通过上面代码可以绘制一条二次贝塞尔曲线,具体原理效果看下图:

必威手机官网 3

尝试绘制波浪

var waveWidth = 300,
    offset = 0,
    waveHeight = 8,
    waveCount = 5,
    startX = -100,
    startY = 208,
    progress = 0,
    progressStep = 1,
    d2 = waveWidth / waveCount,
    d = d2 / 2,
    hd = d / 2,
    c = document.getElementById("myCanvas"),
    ctx = c.getContext("2d");

function tick() {
    offset -= 5;
    progress += progressStep;
    if (progress > 220 || progress < 0) progressStep *= -1;

    if (-1 * offset === d2) offset = 0;
    ctx.clearRect(0, 0, c.width, c.height);
    ctx.beginPath();
    var offsetY = startY - progress;
    ctx.moveTo(startX - offset, offsetY);

    for (var i = 0; i < waveCount; i++) {
        var dx = i * d2;
        var offsetX = dx + startX - offset;
        ctx.quadraticCurveTo(offsetX + hd, offsetY + waveHeight, offsetX + d, offsetY);
        ctx.quadraticCurveTo(offsetX + hd + d, offsetY - waveHeight, offsetX + d2, offsetY);
    }
    ctx.lineTo(startX + waveWidth, 300);
    ctx.lineTo(startX, 300);
    ctx.fill();

    requestAnimationFrame(tick);
}

tick();

可以看到无限运动的波浪:

必威手机官网 4

这里需要主要,绘制的区域要比Canvas大来隐藏摇摆校正的图像,上面使用了200200的Canvas。

大家把代码clone下来可以试试把它绘制到一个大的Canvas上就可以明白。

这里通过if (-1  offset === d2) offset = 0;来实现无限循环。

d2就是一个波峰+波谷的长度。一个波峰+一个波谷之后又开始同样的生命周期和从0开始一样,所以可以重置为0。

了解globalCompositeOperation

globalCompositeOperation 属性说明了绘制到画布上的颜色是如何与画布上已有的颜色组合起来的。

绘制大波浪进度图会用到:

ctx.globalCompositeOperation = "destination-atop";

destination-atop意义:画布上已有的内容只会在它和新图形重叠的地方保留。新图形绘制于内容之后。

当然,globalCompositeOperation还有很多选项,这里不一一列举,大家可以试试设置其他的属性来调整出很酷炫的叠加特效。

整体实现

var img = new Image();
function tick() {
    ...
    ...
    ctx.fill();
    ctx.globalCompositeOperation = "destination-atop";
    ctx.drawImage(img, 0, 0);
    requestAnimationFrame(tick);
}

img.onload = function () {
    tick();
};

img.src = "asset/alloy.png";

为了代码简单直接,这里免去了封装一个加载器的代码,直接通过new Image来设置src来加载图片。

 在绘制完矢量图之后,设置globalCompositeOperation,然后再绘制企鹅图片,绘制顺序不能搞错。

最后

实例下载:demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持帮客之家。

Canvas玩转酷炫大波浪进度图效果实例(附demo),html5canvas 本文介绍了HTML5 Canvas玩转酷炫大波浪进度图效果,具体如下: 如上图所见,...

前面的话

  上一篇博客介绍了canvas基础用法,本文将更进一步,介绍canvas的图形处理和进阶用法

 

一、三合一

三个效果合成一篇文章。

有多个小伙伴问我,为何不开个公众号,现在都是移动时代,你博客文章写好后,公众号再复制一份,花不了多长时间,同时传播方便迅速,打赏方便快捷,显然低成本高收益。

从眼前来看,似乎确实如此。

但是,就我个人而言,行为和处事准则总是遵循内心的直觉和大方向的指引。说不上具体的道理,就是觉得,作品的输出源如果不止一个,久远来看,带来的未知损耗一定要大于短期的已知收益。

取巧的事情多慎思而克己,就好比本文内容,实际上,三个不同的canvas效果,直接分3篇来写,凑个文章数,增加点浏览量其实也是无可厚非的。然,想了想,有点不像自己的style,内心真实的自己并不希望自己这么做,于是,就3个效果合体为一篇文章。

拒绝小部分的诱惑,让自己过得更轻松。

本文的3个效果都是源自我最近做的几个真实的项目,是canvas领域基本入门的一些效果。代码我都专门重新梳理了下,必要注释也都加上去了,方便大家的学习。然后,如果你有不懂的地方,请不要来问我,没错,是不要,我并不欢迎你找我来交流,自己一点一点去弄明白。因为,如果连这么基本的canvas效果都不理解,我真的也帮不了你什么。倒不是说腾不出时间,而是腾不出精力,每天微博私信还有邮箱找我的人还挺多,实在应接不暇。

我的主要学习途径是学习了慕课网站的视频教程,并且实现了一些小的例子。附上教程地址:http://www.imooc.com/learn/185(Canvas绘图详解)
下个阶段学习重点就是实现以下倒计时的代码示例。

HTML代码:

<div style="width:530px;margin:10px auto">
    <div id="canvasDiv"></div>
  </div>

HTML代码非常简单,仅仅是构造了一个canvas容器,我们的画板就在这个地方生成。

图形变换

  图形变换是指用数学方法调整所绘形状的物理属性,其实质是坐标变形。所有的变换都依赖于后台的数学矩阵运算。谈到图形变换,不得不得说的三个基本变换方法就是

平移变换:translate(x,y)
旋转变换:rotate(deg)
缩放变换:scale(sx,sy)

【translate()】

  translate(x,y):将坐标原点移动到(x,y)。执行这个变换之后,坐标(0,0)会变成之前由(x,y)表示的点

  平移变换,顾名思义,就是一般的图形位移。比如这里想将位于(100,100)的矩形平移至(200,200)点。那么只要在绘制矩形之前加上context.translate(100,100)即可

  下面结合代码来看看效果

<canvas id="drawing" style="border:1px solid black">
    <p>The canvas element is not supported!</p>
</canvas>
<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d'); 

    context.beginPath();
    context.fillStyle = "#00AAAA";
    context.fillRect(0,0,100,50);

    context.fillStyle = "red";
    context.translate(50,50);
    context.fillRect(0,0,100,50);
}
</script>

  青蓝色矩形通过调用translate()方法后,将矩形向右移动了(50,50),并变成了红色,尺寸不改变

  想把矩形再向右移动(50,50),应该使用translate(50,50),还是从最开始算起,使用traslate(100,100)呢?

<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');
    context.beginPath();
    context.fillStyle = "#00AAAA";
    context.fillRect(0,0,100,50);

    context.fillStyle = "red";
    context.translate(50,50);
    context.fillRect(0,0,100,50);

    context.fillStyle = "lightblue";
    context.translate(50,50);
    context.fillRect(0,0,100,50);
}
</script>

  由结果可知,使用translate(50,50)即可实现,依照translate()的定义,第一次进行translate()变换时,已经变换了坐标系的原点,这次是基于新坐标系又一次地变换

【状态保存】

  由于多次运行变换后,坐标系可能就乱套了,所以最好以最初状态为参照物。这时就需要用到save()和resore()方法

  save()方法可以保存当前环境的状态,并返回某组属性与变换的组合。所有设置都会进入一个栈结构,得以妥善保管

  restore()方法可以在保存设置的栈结构中向前返回一级,恢复之前保存过的路径状态和属性。连续调用save()方法可以把更多设置保存到栈结构中,之后再连续调用restore()方法可以一级一级返回

   如果使用状态保存,则代码如下所示

<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');
    context.beginPath();
    context.fillStyle = "#00AAAA";
    context.fillRect(0,0,100,50);

    context.save();
    context.fillStyle = "red";
    context.translate(50,50);
    context.fillRect(0,0,100,50);
    context.restore();

    context.save();
    context.fillStyle = "lightblue";
    context.translate(100,100);
    context.fillRect(0,0,100,50);
    context.restore();
}
</script>

  这样,每次图形变换,都以(0,0)点为基准,不会造成坐标系的混乱

【rotate()】

  rotate(angle):围绕原点旋转图像angle弧度

  同画圆弧一样,这里的rotate(deg)传入的参数是弧度,不是角度。同时需要注意的是,这个的旋转是以坐标系的原点(0,0)为圆心进行的顺时针旋转。所以,在使用rotate()之前,通常需要配合使用translate()平移坐标系,确定旋转的圆心。即,旋转变换通常搭配平移变换使用的。

  最后一点需要注意的是,Canvas是基于状态的绘制,所以每次旋转都是接着上次旋转的基础上继续旋转,所以在使用图形变换的时候必须搭配save()restore()方法,一方面重置旋转角度,另一方面重置坐标系原点

  下面是一个例子

<canvas id="drawing" style="border:1px solid black">
    <p>The canvas element is not supported!</p>
</canvas>
<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');
    context.beginPath();
    context.fillStyle = "#00AAAA";
    context.fillRect(0,0,50,50);

    context.save();
    context.fillStyle = "red";
    context.rotate(30*Math.PI/180);
    context.fillRect(0,0,50,50);
    context.restore();  

    context.save();
    context.fillStyle = "green";
    context.rotate(60*Math.PI/180);
    context.fillRect(0,0,50,50);
    context.restore(); 
} 
</script>

  由结果可知,元素是围绕原点进行顺时针旋转rotate的

   下面以元素左上角为圆心进行旋转

<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');
    context.beginPath();
    context.fillStyle = "#00AAAA";
    context.fillRect(30,30,50,50);

    context.save();
    context.fillStyle = "red";
    context.translate(30,30);
    context.rotate(30*Math.PI/180);
    context.fillRect(0,0,50,50);
    context.restore();  

    context.save();
    context.fillStyle = "lightgreen";
    context.translate(30,30);
    context.rotate(60*Math.PI/180);
    context.fillRect(0,0,50,50);
    context.restore(); 
}
</script>

  下面以元素中心点为圆心进行旋转,会出现如下所示神奇的效果

<canvas id="drawing" style="border:1px solid black">
    <p>The canvas element is not supported!</p>
</canvas>
<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');
    context.beginPath();
    context.fillStyle = "rgba(255,0,0,0)";
    context.fillRect(45,45,50,50);
    for(var i = 1; i <=10; i++){
      context.save();
      context.fillStyle = "rgba(255,0,0,0.25)";
      context.translate(70,70);
      context.rotate(i*36*Math.PI/180);
      context.fillRect(0,0,50,50);
      context.restore();   
    }
}
</script>

【scale()】

  scale(scaleX,scaleY):缩放图像,在x方向上乘以scaleX,在Y方向上乘以scaleY。scaleX和scaleY的默认值是1

context.scale(2,2)就是对图像放大两倍。看上去简单,实际用起来还是有一些问题的。先来看一段代码

<canvas id="drawing" style="border:1px solid black">
    <p>The canvas element is not supported!</p>
</canvas>
<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');
    context.beginPath();
    context.lineWidth = 3;
    context.strokeRect(10,10,50,50);
    for(var i = 1; i <= 2; i++){
      context.save();
      context.scale(i,i);
      context.strokeRect(20,20,50,50);
      context.restore();
    }
}
</script>

  结果如下,左上角顶点的坐标变了,而是线条的粗细也变了

  因此,对于缩放变换有两点问题需要注意:

  1、缩放时,图形左上角坐标的位置也会对应缩放

  2、缩放时,图像线条的粗细也会对应缩放

 

二、canvas图形效果之旋转星空

必威手机官网 5

图是死的,效果是活的,IE9+浏览器下,您可以狠狠地点击这里:canvas实现的旋转星空效果demo

会看到地球上方会有很多星星在慢慢地绕着地球转啊转,星星在闪啊闪。

像这类密集型canvas效果,一般离不开下面这几个关键字:实例,随机,变化与重绘,requestAnimationFrame。

原理就是:

  1. 先画一个位置透明度随机的静态的星星实例对象;
  2. 有一个可以改变星星位置和透明度的draw方法;
  3. 定时器跑起来,画布不停地清除与绘制,动画效果完成!

原理很简单。

本例子实现的2个难点在于:

  1. 月明星稀
    星星垂直方向实际上是个伪随机,越靠近地球,星星越密集,而越往上,越稀疏。其算法如下:
JavaScript

var getMinRandom = function() { var rand = Math.random(); //
step的大小决定了星星靠近地球的聚拢程度, // step = Math.ceil(2 /
(1 - rand))就聚拢很明显 var step = Math.ceil(1 / (1 - rand)); var
arr = []; for (var i=0; i&lt;step; i++) { arr.push(Math.random());
} return Math.min.apply(null, arr); };

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f4a6436b2b195965046-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f4a6436b2b195965046-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f4a6436b2b195965046-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f4a6436b2b195965046-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f4a6436b2b195965046-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f4a6436b2b195965046-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f4a6436b2b195965046-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f4a6436b2b195965046-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f4a6436b2b195965046-9">
9
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f4a6436b2b195965046-10">
10
</div>
<div class="crayon-num" data-line="crayon-5b8f4a6436b2b195965046-11">
11
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f4a6436b2b195965046-12">
12
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f4a6436b2b195965046-1" class="crayon-line">
var getMinRandom = function() {
</div>
<div id="crayon-5b8f4a6436b2b195965046-2" class="crayon-line crayon-striped-line">
    var rand = Math.random();
</div>
<div id="crayon-5b8f4a6436b2b195965046-3" class="crayon-line">
    // step的大小决定了星星靠近地球的聚拢程度,
</div>
<div id="crayon-5b8f4a6436b2b195965046-4" class="crayon-line crayon-striped-line">
    // step = Math.ceil(2 / (1 - rand))就聚拢很明显
</div>
<div id="crayon-5b8f4a6436b2b195965046-5" class="crayon-line">
    var step = Math.ceil(1 / (1 - rand));
</div>
<div id="crayon-5b8f4a6436b2b195965046-6" class="crayon-line crayon-striped-line">
    var arr = [];
</div>
<div id="crayon-5b8f4a6436b2b195965046-7" class="crayon-line">
    for (var i=0; i&lt;step; i++) {
</div>
<div id="crayon-5b8f4a6436b2b195965046-8" class="crayon-line crayon-striped-line">
        arr.push(Math.random());
</div>
<div id="crayon-5b8f4a6436b2b195965046-9" class="crayon-line">
    }
</div>
<div id="crayon-5b8f4a6436b2b195965046-10" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f4a6436b2b195965046-11" class="crayon-line">
    return Math.min.apply(null, arr);       
</div>
<div id="crayon-5b8f4a6436b2b195965046-12" class="crayon-line crayon-striped-line">
};
</div>
</div></td>
</tr>
</tbody>
</table>

很大概率会返回一个数值偏小的值,于是,就可以有“月明星稀”的分布效果了。
  1. 圆弧轨迹
    其实很简单,我们套用高中时候学的圆方程式就可以了,如下注释截图所述:
    必威手机官网 6这下题目就简单了,已知a,b, 求y相对于x的函数表达式……

下面是我总结的第一阶段的一些学习笔记:

Javascript代码:

首先我们通过一组变量来定义画板的样式,以及一些数据的初始化:

var canvas;
var context;
var canvasWidth = 490;
var canvasHeight = 220;
var padding = 25;
var lineWidth = 8;
var colorPurple = "#cb3594";
var colorGreen = "#659b41";
var colorYellow = "#ffcf33";
var colorBrown = "#986928";
var outlineImage = new Image();
var crayonImage = new Image();
var markerImage = new Image();
var eraserImage = new Image();
var crayonBackgroundImage = new Image();
var markerBackgroundImage = new Image();
var eraserBackgroundImage = new Image();
var crayonTextureImage = new Image();
var clickX = new Array();
var clickY = new Array();
var clickColor = new Array();
var clickTool = new Array();
var clickSize = new Array();
var clickDrag = new Array();
var paint = false;
var curColor = colorPurple;
var curTool = "crayon";
var curSize = "normal";
var mediumStartX = 18;
var mediumStartY = 19;
var mediumImageWidth = 93;
var mediumImageHeight = 46;
var drawingAreaX = 111;
var drawingAreaY = 11;
var drawingAreaWidth = 267;
var drawingAreaHeight = 200;
var toolHotspotStartY = 23;
var toolHotspotHeight = 38;
var sizeHotspotStartY = 157;
var sizeHotspotHeight = 36;
var sizeHotspotWidthObject = new Object();
sizeHotspotWidthObject.huge = 39;
sizeHotspotWidthObject.large = 25;
sizeHotspotWidthObject.normal = 18;
sizeHotspotWidthObject.small = 16;
var totalLoadResources = 8;
var curLoadResNum = 0;

接下来开始准备画布,也就是初始化Canvas对象:

function prepareCanvas()
{
    // Create the canvas (Neccessary for IE because it doesn't know what a canvas element is)
    var canvasDiv = document.getElementById('canvasDiv');
    canvas = document.createElement('canvas');
    canvas.setAttribute('width', canvasWidth);
    canvas.setAttribute('height', canvasHeight);
    canvas.setAttribute('id', 'canvas');
    canvasDiv.appendChild(canvas);
    if(typeof G_vmlCanvasManager != 'undefined') {
        canvas = G_vmlCanvasManager.initElement(canvas);
    }
    context = canvas.getContext("2d"); // Grab the 2d canvas context
    // Note: The above code is a workaround for IE 8 and lower. Otherwise we could have used:
    //     context = document.getElementById('canvas').getContext("2d");

    // Load images
    // -----------
    crayonImage.onload = function() { resourceLoaded(); 
    };
    crayonImage.src = "images/crayon-outline.png";
    //context.drawImage(crayonImage, 0, 0, 100, 100);

    markerImage.onload = function() { resourceLoaded(); 
    };
    markerImage.src = "images/marker-outline.png";

    eraserImage.onload = function() { resourceLoaded(); 
    };
    eraserImage.src = "images/eraser-outline.png";    

    crayonBackgroundImage.onload = function() { resourceLoaded(); 
    };
    crayonBackgroundImage.src = "images/crayon-background.png";

    markerBackgroundImage.onload = function() { resourceLoaded(); 
    };
    markerBackgroundImage.src = "images/marker-background.png";

    eraserBackgroundImage.onload = function() { resourceLoaded(); 
    };
    eraserBackgroundImage.src = "images/eraser-background.png";

    crayonTextureImage.onload = function() { resourceLoaded(); 
    };
    crayonTextureImage.src = "images/crayon-texture.png";

    outlineImage.onload = function() { resourceLoaded(); 
    };
    outlineImage.src = "images/watermelon-duck-outline.png";

    // Add mouse events
    // ----------------
    $('#canvas').mousedown(function(e)
    {
        // Mouse down location
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;

        if(mouseX < drawingAreaX) // Left of the drawing area
        {
            if(mouseX > mediumStartX)
            {
                if(mouseY > mediumStartY && mouseY < mediumStartY + mediumImageHeight){
                    curColor = colorPurple;
                }else if(mouseY > mediumStartY + mediumImageHeight && mouseY < mediumStartY + mediumImageHeight * 2){
                    curColor = colorGreen;
                }else if(mouseY > mediumStartY + mediumImageHeight * 2 && mouseY < mediumStartY + mediumImageHeight * 3){
                    curColor = colorYellow;
                }else if(mouseY > mediumStartY + mediumImageHeight * 3 && mouseY < mediumStartY + mediumImageHeight * 4){
                    curColor = colorBrown;
                }
            }
        }
        else if(mouseX > drawingAreaX + drawingAreaWidth) // Right of the drawing area
        {
            if(mouseY > toolHotspotStartY)
            {
                if(mouseY > sizeHotspotStartY)
                {
                    var sizeHotspotStartX = drawingAreaX + drawingAreaWidth;
                    if(mouseY < sizeHotspotStartY + sizeHotspotHeight && mouseX > sizeHotspotStartX)
                    {
                        if(mouseX < sizeHotspotStartX + sizeHotspotWidthObject.huge){
                            curSize = "huge";
                        }else if(mouseX < sizeHotspotStartX + sizeHotspotWidthObject.large + sizeHotspotWidthObject.huge){
                            curSize = "large";
                        }else if(mouseX < sizeHotspotStartX + sizeHotspotWidthObject.normal + sizeHotspotWidthObject.large + sizeHotspotWidthObject.huge){
                            curSize = "normal";
                        }else if(mouseX < sizeHotspotStartX + sizeHotspotWidthObject.small + sizeHotspotWidthObject.normal + sizeHotspotWidthObject.large + sizeHotspotWidthObject.huge){
                            curSize = "small";                        
                        }
                    }
                }
                else
                {
                    if(mouseY < toolHotspotStartY + toolHotspotHeight){
                        curTool = "crayon";
                    }else if(mouseY < toolHotspotStartY + toolHotspotHeight * 2){
                        curTool = "marker";
                    }else if(mouseY < toolHotspotStartY + toolHotspotHeight * 3){
                        curTool = "eraser";
                    }
                }
            }
        }
        else if(mouseY > drawingAreaY && mouseY < drawingAreaY + drawingAreaHeight)
        {
            // Mouse click location on drawing area
        }
        paint = true;
        addClick(mouseX, mouseY, false);
        redraw();
    });

    $('#canvas').mousemove(function(e){
        if(paint==true){
            addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
            redraw();
        }
    });

    $('#canvas').mouseup(function(e){
        paint = false;
          redraw();
    });

    $('#canvas').mouseleave(function(e){
        paint = false;
    });
}

看起来很复杂,前面主要是初始化canvas的背景图片,后面主要是初始化画笔事件,像click、mouseup、mouseleave等鼠标事件。

下面是draw的主要方法:

function redraw()
{
    // Make sure required resources are loaded before redrawing
    if(curLoadResNum < totalLoadResources){ return; }

    clearCanvas();

    var locX;
    var locY;
    if(curTool == "crayon")
    {
        // Draw the crayon tool background
        context.drawImage(crayonBackgroundImage, 0, 0, canvasWidth, canvasHeight);

        // Purple
        locX = (curColor == colorPurple) ? 18 : 52;
        locY = 19;

        context.beginPath();
        context.moveTo(locX + 41, locY + 11);
        context.lineTo(locX + 41, locY + 35);
        context.lineTo(locX + 29, locY + 35);
        context.lineTo(locX + 29, locY + 33);
        context.lineTo(locX + 11, locY + 27);
        context.lineTo(locX + 11, locY + 19);
        context.lineTo(locX + 29, locY + 13);
        context.lineTo(locX + 29, locY + 11);
        context.lineTo(locX + 41, locY + 11);
        context.closePath();
        context.fillStyle = colorPurple;
        context.fill();    

        if(curColor == colorPurple){
            context.drawImage(crayonImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(crayonImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }

        // Green
        locX = (curColor == colorGreen) ? 18 : 52;
        locY += 46;

        context.beginPath();
        context.moveTo(locX + 41, locY + 11);
        context.lineTo(locX + 41, locY + 35);
        context.lineTo(locX + 29, locY + 35);
        context.lineTo(locX + 29, locY + 33);
        context.lineTo(locX + 11, locY + 27);
        context.lineTo(locX + 11, locY + 19);
        context.lineTo(locX + 29, locY + 13);
        context.lineTo(locX + 29, locY + 11);
        context.lineTo(locX + 41, locY + 11);
        context.closePath();
        context.fillStyle = colorGreen;
        context.fill();    

        if(curColor == colorGreen){
            context.drawImage(crayonImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(crayonImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }

        // Yellow
        locX = (curColor == colorYellow) ? 18 : 52;
        locY += 46;

        context.beginPath();
        context.moveTo(locX + 41, locY + 11);
        context.lineTo(locX + 41, locY + 35);
        context.lineTo(locX + 29, locY + 35);
        context.lineTo(locX + 29, locY + 33);
        context.lineTo(locX + 11, locY + 27);
        context.lineTo(locX + 11, locY + 19);
        context.lineTo(locX + 29, locY + 13);
        context.lineTo(locX + 29, locY + 11);
        context.lineTo(locX + 41, locY + 11);
        context.closePath();
        context.fillStyle = colorYellow;
        context.fill();    

        if(curColor == colorYellow){
            context.drawImage(crayonImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(crayonImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }

        // Yellow
        locX = (curColor == colorBrown) ? 18 : 52;
        locY += 46;

        context.beginPath();
        context.moveTo(locX + 41, locY + 11);
        context.lineTo(locX + 41, locY + 35);
        context.lineTo(locX + 29, locY + 35);
        context.lineTo(locX + 29, locY + 33);
        context.lineTo(locX + 11, locY + 27);
        context.lineTo(locX + 11, locY + 19);
        context.lineTo(locX + 29, locY + 13);
        context.lineTo(locX + 29, locY + 11);
        context.lineTo(locX + 41, locY + 11);
        context.closePath();
        context.fillStyle = colorBrown;
        context.fill();    

        if(curColor == colorBrown){
            context.drawImage(crayonImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(crayonImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }
    }
    else if(curTool == "marker")
    {
        // Draw the marker tool background
        context.drawImage(markerBackgroundImage, 0, 0, canvasWidth, canvasHeight);

        // Purple
        locX = (curColor == colorPurple) ? 18 : 52;
        locY = 19;

        context.beginPath();
        context.moveTo(locX + 10, locY + 24);
        context.lineTo(locX + 10, locY + 24);
        context.lineTo(locX + 22, locY + 16);
        context.lineTo(locX + 22, locY + 31);
        context.closePath();
        context.fillStyle = colorPurple;
        context.fill();    

        if(curColor == colorPurple){
            context.drawImage(markerImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(markerImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }

        // Green
        locX = (curColor == colorGreen) ? 18 : 52;
        locY += 46;

        context.beginPath();
        context.moveTo(locX + 10, locY + 24);
        context.lineTo(locX + 10, locY + 24);
        context.lineTo(locX + 22, locY + 16);
        context.lineTo(locX + 22, locY + 31);
        context.closePath();
        context.fillStyle = colorGreen;
        context.fill();    

        if(curColor == colorGreen){
            context.drawImage(markerImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(markerImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }

        // Yellow
        locX = (curColor == colorYellow) ? 18 : 52;
        locY += 46;

        context.beginPath();
        context.moveTo(locX + 10, locY + 24);
        context.lineTo(locX + 10, locY + 24);
        context.lineTo(locX + 22, locY + 16);
        context.lineTo(locX + 22, locY + 31);
        context.closePath();
        context.fillStyle = colorYellow;
        context.fill();    

        if(curColor == colorYellow){
            context.drawImage(markerImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(markerImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }

        // Yellow
        locX = (curColor == colorBrown) ? 18 : 52;
        locY += 46;

        context.beginPath();
        context.moveTo(locX + 10, locY + 24);
        context.lineTo(locX + 10, locY + 24);
        context.lineTo(locX + 22, locY + 16);
        context.lineTo(locX + 22, locY + 31);
        context.closePath();
        context.fillStyle = colorBrown;
        context.fill();    

        if(curColor == colorBrown){
            context.drawImage(markerImage, locX, locY, mediumImageWidth, mediumImageHeight);
        }else{
            context.drawImage(markerImage, 0, 0, 59, mediumImageHeight, locX, locY, 59, mediumImageHeight);
        }
    }
    else if(curTool == "eraser")
    {
        context.drawImage(eraserBackgroundImage, 0, 0, canvasWidth, canvasHeight);
        context.drawImage(eraserImage, 18, 19, mediumImageWidth, mediumImageHeight);    
    }else{
        alert("Error: Current Tool is undefined");
    }

    if(curSize == "small"){
        locX = 467;
    }else if(curSize == "normal"){
        locX = 450;
    }else if(curSize == "large"){
        locX = 428;
    }else if(curSize == "huge"){
        locX = 399;
    }
    locY = 189;
    context.beginPath();
    context.rect(locX, locY, 2, 12);
    context.closePath();
    context.fillStyle = '#333333';
    context.fill();    

    // Keep the drawing in the drawing area
    context.save();
    context.beginPath();
    context.rect(drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
    context.clip();

    var radius;
    var i = 0;
    for(; i < clickX.length; i++)
    {        
        if(clickSize[i] == "small"){
            radius = 2;
        }else if(clickSize[i] == "normal"){
            radius = 5;
        }else if(clickSize[i] == "large"){
            radius = 10;
        }else if(clickSize[i] == "huge"){
            radius = 20;
        }else{
            alert("Error: Radius is zero for click " + i);
            radius = 0;    
        }

        context.beginPath();
        if(clickDrag[i] && i){
            context.moveTo(clickX[i-1], clickY[i-1]);
        }else{
            context.moveTo(clickX[i], clickY[i]);
        }
        context.lineTo(clickX[i], clickY[i]);
        context.closePath();

        if(clickTool[i] == "eraser"){
            //context.globalCompositeOperation = "destination-out"; // To erase instead of draw over with white
            context.strokeStyle = 'white';
        }else{
            //context.globalCompositeOperation = "source-over";    // To erase instead of draw over with white
            context.strokeStyle = clickColor[i];
        }
        context.lineJoin = "round";
        context.lineWidth = radius;
        context.stroke();

    }
    //context.globalCompositeOperation = "source-over";// To erase instead of draw over with white
    context.restore();

    // Overlay a crayon texture (if the current tool is crayon)
    if(curTool == "crayon"){
        context.globalAlpha = 0.4; // No IE support
        context.drawImage(crayonTextureImage, 0, 0, canvasWidth, canvasHeight);
    }
    context.globalAlpha = 1; // No IE support

    // Draw the outline image
    context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);
}

其实HTML5说白了还是需要很多Javascript支持,不过Canvas非常不错,可以让你在上面自由地绘制图形和动画。这款基于HTML5 Canvas的网页画板就是一个很好的例子。源代码下载>>

矩阵变换

  上面所说的坐标变换的三种方式——平移translate(),缩放scale(),以及旋转rotate()都可以通过transform()做到

  在介绍矩阵变换transform()前,先来介绍下什么是变换矩阵

  [注意]关于transform的详细介绍,移步CSS3的transform介绍

必威手机官网 7

  以上是Canvas中transform()方法所对应的变换矩阵

【transform()】

  而此方法正是传入图中所示的六个参数,具体为context.transform(a,b,c,d,e,f)

  各参数意义对应如下表:

参数    意义
a    水平缩放(1)
b    水平倾斜(0)
c    垂直倾斜(0)
d    垂直缩放(1)
e    水平位移(0)
f    垂直位移(0)

  建议使用transform()的时候,可以在如下几个情况下使用:

  1、使用context.transform (1,0,0,1,dx,dy)代替context.translate(dx,dy)

  2、使用context.transform(sx,0,0,sy,0,0)代替context.scale(sx, sy)

  3、使用context.transform(1,tany,tanx,1,0,0)来实现倾斜效果

<canvas id="drawing" style="border:1px solid black">
    <p>The canvas element is not supported!</p>
</canvas>
<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
    var context = drawing.getContext('2d');

    context.beginPath();
    context.lineWidth = 3;
    context.strokeRect(10,10,50,50);

    context.save();
    context.transform (1,0,0,1,30,30);
    context.strokeStyle = 'pink';
    context.strokeRect(0,0,50,50);
    context.restore();

    context.save();
    context.transform(2,0,0,2,0,0)
    context.strokeStyle = 'lightblue';
    context.strokeRect(50,10,50,50);
    context.restore();

    context.save();
    context.transform(1,0,Math.tan(30*Math.PI/180),1,0,0);
    context.strokeStyle = 'lightgreen';
    context.strokeRect(200,10,50,50);
    context.restore();
} 
</script>

【setTransform()】

  setTransform():将变换矩阵重置为默认状态,然后再调用transform()

transform()方法的行为相对于由 rotate(),scale()translate(), or transform() 完成的其他变换。例如:如果已经将绘图设置为放到两倍,则 transform() 方法会把绘图放大两倍,那么绘图最终将放大四倍。这一点和之前的变换是一样的。

  但是setTransform()不会相对于其他变换来发生行为。它的参数也是六个,context.setTransform(a,b,c,d,e,f),与transform()一样

  当前面的代码已经使用了多个transform()、translate()、rotate()、scale()等变换方法,无法轻易地从当前的矩阵变化到想要的矩阵时,就可以使用setTransform()方法将矩阵重置为默认状态,然后再调用transform()

cxt.transform(1,0,0,1,50,100);
cxt.transform(2,0,0,1.5,0,0);
cxt.transform(1.-0.2,-0.2,1,0,0);
cxt.setTransform(1,0,0,1,100,100);

 

本文由必威发布于必威-前端,转载请注明出处:所以首选大波浪进度图必威手机官网:,谈到图

相关阅读