canvas学习之API整理笔记(二)


Posted in Javascript onDecember 29, 2016

前面我整理过一篇文章介绍了一些基本的API,从这篇文章我们已经可以基本了解到常用绘图的API、简单的变换和动画。而本篇文章的主要内容包括高级动画、像素操作、性能优化等知识点,讲解每个知识点的同时还会有一些酷炫的demo,保证看官们全程在线,毫无尿点,看完不会后悔,哈哈,一个耿直的笑^_^。

除此之外,关于canvas的一系列实例即将来袭!欢迎关注!

开始之前

//获取canvas容器
var can = document.getElementById('canvas');
//创建一个画布
var ctx = can.getContext('2d');

下面所有的操作都在画布ctx上进行操作。

高级动画

继上一篇简单介绍了动画(主要是requestAnimationFrame方法),现在我们来一步步实现一个在画布内滚动的实例。

html代码:

<canvas id="canvas" width="400" height="200" style="background:#fff;"></canvas>

js代码:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var ball = {  //小球属性,原点位置,速度,半径等。
  x: 100, 
  y: 100,
  vx: 4,
  vy: 2,
  radius: 20,
  color: 'blue',
  draw: function() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  }
};
function draw() {
  ctx.clearRect(0,0, canvas.width, canvas.height);  //绘制之前清除整个画布
  ball.draw();  //在画布中绘制小球
  ball.x += ball.vx;  //改变小球位置坐标
  ball.y += ball.vy;  //改变小球位置坐标
  if (ball.y + ball.vy > canvas.height-15 || ball.y + ball.vy < 15) {  //边界判断
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width-15 || ball.x + ball.vx < 15) {  //边界判断
    ball.vx = -ball.vx;
  }
  window.requestAnimationFrame(draw);  //循环执行
}
draw();

上面代码实现的效果如下图:

canvas学习之API整理笔记(二)

代码我已经写了基本的注释,不难理解,简单概括一下这个实例的实现思想:

创建一个小球对象,包含一个绘制自己的方法。在整个画布中绘制这个小球,然后在下一次绘制之前,先清除整个画布,改变小球的各个属性(包含了逻辑,比如边界的判断),然后重新绘制一遍,从而达到了动起来的效果。

如果你把上面代码中的

ctx.clearRect(0,0, canvas.width, canvas.height);

换成下面这样:

ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);

就可以得到渐变尾巴的效果:

canvas学习之API整理笔记(二)

大概意思就是使用半透明的白色背景填充画布来代替直接清除这个画布,从而实现了想要的效果。

像素操作

如果我们想对一个canvas画布进行如下操作:获取每一个点的信息,对每一个坐标点进行操作。那我们就需要了解一下ImageData对象了。

ImageData对象(由getImageData方法获取的)中存储着canvas对象真实的像素数据,它包含以下几个只读属性:

width

图片宽度,单位是像素。

height

图片高度,单位是像素。

data

Uint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255)。简单讲,就是一个数组,每四个元素存储一个点的颜色信息,这四个元素分别对应为R、G、B、A的值(知道颜色取值的一眼就明白了,不知道的也没关系,后面有实例,一看就明白)。

创建ImageData对象

去创建一个新的,空白的ImageData对像,你应该会使用createImageData()方法:

var myImageData = ctx.createImageData(width, height);

上面代码创建了一个新的具体特定尺寸的ImageData对像。所有像素被预设为透明黑。

获取像素数据

为了获得一个包含画布场景像素数据的ImageData对像,你可以用getImageData()方法

var myImageData = ctx.getImageData(left, top, width, height);

创建的myImageData对象就有width、height、data三个属性的值了。看下面这个实例:

html代码:

<div id="color">hover处的颜色</div>
<canvas id="myCanvas" width="300" height="150"></canvas>

js代码:

var can = document.getElementById('myCanvas');
var ctx = can.getContext('2d');
var img = new Image();
  img.src = "img_the_scream.jpg";
ctx.drawImage(img, 0, 0);
var color = document.getElementById('color');
function pick(event) {
  var x = event.layerX;
  var y = event.layerY;
  var area = ctx.getImageData(x, y, 1, 1); //创建ImageData对象
  var data = area.data;  //获取data属性(一个存储颜色rgba值的数组)
  var rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + data[3] + ')';
  color.style.color = rgba;
  color.textContent = rgba;
}
can.addEventListener('mousemove', pick);

实现的效果如下图:

canvas学习之API整理笔记(二)

注意: 如果有些同学试到这里发现有这个报错内容Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.,需要检查这行代码:

img.src = "img_the_scream.jpg";

这里的图片地址不能是跨域地址。网上有一些解决办法,这里就不展开讲了。

写入像素数据

你可以用putImageData()方法去对场景进行像素数据的写入。

ctx.putImageData(myImageData, x, y);  //在画布的(x, y)点开始绘制myImageData所存储的像素信息。

所以我们可以把获取到的像素信息进行处理,然后再重新绘制,就得到了新的图形。看看下面这个实例:

html代码:

<canvas id="canvas" width="660" height="277"></canvas>

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = 'img_the_scream.jpg';
ctx.drawImage(img, 0, 0);
var imageData = ctx.getImageData(0,0,canvas.width, canvas.height); //获取ImageData
var colors = imageData.data; //获取像素信息
function invert() {
  for (var i = 0; i < colors.length; i += 4) { //四个为一组
    colors[i]   = 225 - colors[i];   // red
    colors[i+1] = 225 - colors[i+1]; // green
    colors[i+2] = 225 - colors[i+2]; // blue
    colors[i+3] = 255;  //alpha
  }
  ctx.putImageData(imageData, 220, 0); //从(220, 0)开始绘制改变过的颜色
}
function toGray() {
  for (var i = 0; i < colors.length; i += 4) {
    var avg = (colors[i] + colors[i+1] + colors[i+2]) / 3; 
    colors[i] = avg; // red
    colors[i+1] = avg; // green
    colors[i+2] = avg; // blue
    colors[i+3] = 255;  //alpha
  }
  ctx.putImageData(imageData, 440, 0); //从(440, 0)开始绘制改变过的颜色
}
invert();  //反转色
toGray();  //变灰色

实现的效果如下图:

canvas学习之API整理笔记(二)

左边部分是原图,中间部分是把原图颜色经过反转得到的图案,右边部分是把原图颜色变灰得到的图案。

性能优化

坐标点尽量用整数

浏览器为了达到抗锯齿的效果会做额外的运算。为了避免这种情况,请保证使用canvas的绘制函数时,尽量用Math.floor()函数对所有的坐标点取整。比如:

ctx.drawImage(myImage, 0.3, 0.5); //不提倡这样写,应该像下面这样处理
ctx.drawImage(myImage, Math.floor(0.3), Math.floor(0.5));

使用多个画布绘制复杂场景

比如做一个游戏,有几个层面:背景层(简单变化)、游戏层(时刻变化)。这个时候,我们就可以创建两个画布,一个专门用来绘制不变的背景(少量绘制),另一个用来绘制游戏动态部分(大量绘制),就像这样:

<canvas id="background-can" width="480" height="320"></canvas>
<canvas id="game-can" width="480" height="320"></canvas>

用CSS设置静态大图

如果有一层是永远不变的,比如一张静态的背景图,最好使用div+css的方法去替代ctx.drawimage(),这么做可以避免在每一帧在画布上绘制大图。简单讲,dom渲染肯定比canvas的操作性能更高。

尽量少操作canvas的缩放

如果要对一个画布进行缩放,如果可以的话,尽量使用CSS3的transform来实现。总之,记住一个原则,能用html+div实现的尽量不用js对canvas进行操作。

更多Tips

  • 将画布的函数调用集合到一起(例如,画一条折线,而不要画多条分开的直线)
  • 使用不同的办法去清除画布(clearRect()、fillRect()、调整canvas大小)
  • 尽可能避免 shadowBlur特性
  • 有动画,请使用window.requestAnimationFrame() 而非window.setInterval()

结语

OK,canvas常用的API就基本总结完了,靠这些API已经足够开发一些中型游戏了。比如之前自己写的实例demo之小游戏tinyHeart,就是用这些函数画出来的。关键是这些函数的组合使用,多多练习就好了。

如果你把我之前的两篇文章都看了的话,相信你会对canvas越来越感兴趣。所以为了让大家的兴趣不会中断,我后续还会出一系列的关于canvas的实例,注意,是一系列!敬请期待!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
web 页面分页打印的实现
Jun 22 Javascript
使用jquery实现select添加实现后台权限添加的效果
May 28 Javascript
js清除input中type等于file的值域(示例代码)
Dec 24 Javascript
jQuery的选择器中的通配符使用介绍
Mar 20 Javascript
JavaScript中判断两个字符串是否相等的方法
Jul 07 Javascript
jQuery基于ajax()使用serialize()提交form数据的方法
Dec 08 Javascript
AngularJS实现Input格式化的方法
Nov 07 Javascript
vue实现自定义日期组件功能的实例代码
Nov 06 Javascript
解决layer.msg 不居中 ifram中的问题
Sep 05 Javascript
你不知道的 TypeScript 高级类型(小结)
Aug 28 Javascript
vue+axios 拦截器实现统一token的案例
Sep 11 Javascript
如何利用JavaScript实现二叉搜索树
Apr 02 Javascript
Node.js复制文件的方法示例
Dec 29 #Javascript
详解微信小程序——自定义圆形进度条
Dec 29 #Javascript
JS作用域深度解析
Dec 29 #Javascript
javascript中的深复制详解及实例分析
Dec 29 #Javascript
canvas学习之API整理笔记(一)
Dec 29 #Javascript
Javascript Function.prototype.bind详细分析
Dec 29 #Javascript
jQuery自定义插件详解及实例代码
Dec 29 #Javascript
You might like
How do I change MySQL timezone?
2008/03/26 PHP
PHP教程 变量定义
2009/10/23 PHP
如何在Laravel5.8中正确地应用Repository设计模式
2019/11/26 PHP
一些主流JS框架中DOMReady事件的实现小结
2011/02/12 Javascript
基于jquery的跟随屏幕滚动代码
2012/07/24 Javascript
Jquery如何实现点击时高亮显示代码
2014/01/22 Javascript
JavaScript中使用指数方法Math.exp()的简介
2015/06/15 Javascript
jQuery实现带分组数据的Table表头排序实例分析
2015/11/24 Javascript
jQuery实现简单的DIV拖动效果
2016/02/19 Javascript
JS控制静态页面之间传递参数获取参数并应用的简单实例
2016/08/10 Javascript
JavaScript中闭包的详解
2017/04/01 Javascript
Vue使用vue-cli创建项目
2017/09/01 Javascript
javascript中的this作用域详解
2019/07/15 Javascript
vue使用高德地图点击下钻上浮效果的实现思路
2019/10/12 Javascript
公众号SVG动画交互实战代码
2020/05/31 Javascript
python 排列组合之itertools
2013/03/20 Python
玩转python爬虫之cookie使用方法
2016/02/17 Python
简单谈谈Python中的闭包
2016/11/30 Python
python中的字典操作及字典函数
2018/01/03 Python
详解Python 装饰器执行顺序迷思
2018/08/08 Python
Python性能分析工具Profile使用实例
2019/11/19 Python
Django集成celery发送异步邮件实例
2019/12/17 Python
HTML5 Web缓存和运用程序缓存(cookie,session)
2018/01/11 HTML / CSS
澳大利亚第一旅行车和房车配件店:Caravan RV Camping
2020/12/26 全球购物
自我鉴定范文
2013/11/10 职场文书
车间副主任岗位职责
2013/12/24 职场文书
小学英语课后反思
2014/04/26 职场文书
纪念9.18事变演讲稿
2014/09/14 职场文书
干部作风整顿自我剖析材料和整改措施
2014/09/18 职场文书
2015年药房工作总结
2015/04/25 职场文书
幼儿园老师工作总结2015
2015/05/22 职场文书
红歌会主持词
2015/07/02 职场文书
孙振耀退休感言
2015/08/01 职场文书
Python标准库pathlib操作目录和文件
2021/11/20 Python
如何在python中实现ECDSA你知道吗
2021/11/23 Python
MySQL 分区表中分区键为什么必须是主键的一部分
2022/03/17 MySQL