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 相关文章推荐
(JS实现)MapBar中坐标的加密和解密的脚本
May 16 Javascript
用javascript实现给出的盒子的序列是否可连为一矩型
Aug 30 Javascript
JavaScript中的其他对象
Jan 16 Javascript
js原生appendChild的bug解决心得分享
Jul 01 Javascript
JavaScript加强之自定义event事件
Sep 21 Javascript
js实现省市联动效果的简单实例
Feb 10 Javascript
网页右下角弹出窗体实现代码
Jun 05 Javascript
JQuery实现动态表格点击按钮表格增加一行
Aug 24 Javascript
使用JQuery实现Ctrl+Enter提交表单的方法
Oct 22 Javascript
基于JS组件实现拖动滑块验证功能(代码分享)
Nov 18 Javascript
vue中使用ueditor富文本编辑器
Feb 08 Javascript
easyUI 实现的后台分页与前台显示功能示例
Jun 01 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
linux实现php定时执行cron任务详解
2013/12/24 PHP
php查看请求头信息获取远程图片大小的方法分享
2013/12/25 PHP
php基于dom实现读取图书xml格式数据的方法
2017/02/03 PHP
php生成条形码的图片的实例详解
2017/09/13 PHP
PHP全局使用Laravel辅助函数dd
2019/12/26 PHP
php在linux环境中如何使用redis详解
2020/12/15 PHP
javascript 限制输入和粘贴(IE,firefox测试通过)
2008/11/14 Javascript
JQuery动态给table添加、删除行 改进版
2011/01/19 Javascript
js的Prototype属性解释及常用方法
2014/05/08 Javascript
JavaScript实现将数组数据添加到Select下拉框的方法
2015/08/21 Javascript
18个非常棒的jQuery代码片段
2015/11/02 Javascript
js实现将选中内容分享到新浪或腾讯微博
2015/12/16 Javascript
实例详解JavaScript获取链接参数的方法
2016/01/01 Javascript
js获取一组日期中最近连续的天数
2017/05/25 Javascript
Vue2.0实现调用摄像头进行拍照功能 exif.js实现图片上传功能
2018/04/28 Javascript
解决bootstrap中下拉菜单点击后不关闭的问题
2018/08/10 Javascript
package.json各个属性说明详解
2020/03/11 Javascript
在Python中处理XML的教程
2015/04/29 Python
浅谈python中的数字类型与处理工具
2017/08/02 Python
在Python中使用gRPC的方法示例
2018/08/08 Python
python matplotlib库绘制散点图例题解析
2019/08/10 Python
python模式 工厂模式原理及实例详解
2020/02/11 Python
使用python操作lmdb对数据读取的实例
2020/12/11 Python
12个不为大家熟知的HTML5设计小技巧
2016/06/02 HTML / CSS
经典c++面试题六
2012/01/18 面试题
linux系统都有哪些运行级别
2012/04/15 面试题
你们项目是如何进行变更控制的
2015/08/26 面试题
运动会广播稿20字
2014/02/18 职场文书
横幅标语大全
2014/06/17 职场文书
酒店七夕情人节活动策划方案
2014/08/24 职场文书
财务科长个人对照检查材料
2014/09/18 职场文书
自查自纠工作情况报告
2014/10/29 职场文书
幼儿园百日安全活动总结
2015/05/07 职场文书
Element实现动态表格的示例代码
2021/08/02 Javascript
利用Python多线程实现图片下载器
2022/03/25 Python
Elasticsearch 索引操作和增删改查
2022/04/19 Python