-
Chapter7 Canvas高级绘图
普通类 -
- 支持
- 批判
- 提问
- 解释
- 补充
- 删除
-
-
高级Canvas绘图
canvas不仅仅绘制直线和曲线,还能够直接绘制已有的图片、文本、图案甚至是音视频
绘制图像
方法一:
使用createImageData(),是最没有效率地单个像素地创建图像
方法二:
var img = document.getElementById("portrait"); //获得图像
context.drawImage(img, 0, 0); //绘制图像,在调用这个函数之前需要先准备好图片对象
方法三:
利用js先动态创建一个img对象
var img=new Image();
img.src="portrait";
context.drawImage(img,0,0);
PS:注意,如果图片的比例出现异常可能是因为使用CSS设置画布的宽和高度,CSS设置和直接在<canvas>标签中设置不一样,它会导致画布和画布中的内容被拉伸或压缩而导致变形
裁剪、切割和伸缩图片
context.drawImage(img, x,y,width,height); //x,y是相对于画布左上角的位置,width为图像显示的宽度,height为图像显示的高度
context.drawImage(img, source_x, source_y,source_width,source_height,x,y,width,height); //后4个参数与上方一致,此函数可以剪裁图像。source_x和source_y表示从原始图片的(source_x, source_y)开始剪裁,source_width和source_height表示剪裁的宽度和高度
这是一个200*200px的图片,当前显示的就是剪裁图片的上半部分,然后在相对于画布的(75,25)上呈现
context.drawImage(img, 0, 0, 200, 100, 75, 25, 200, 100);
绘制视频帧
var video = document.getElementById("videoPlayer");
context.drawImage(video,0,0,video.clientWidth,video.clientHeight);
这样会捕获页面上视频的当前帧并绘制到画布上。这样一来通过定时器不断捕获播放中的视频并将新画面绘制到画布上看起来就像是另一个视频播放器。更有意思的是还可以在绘制之前方法或缩小图片,甚至于添加一些滤镜效果,比如黑白(http://html5doctor.com/video-canvas-magic/)
绘制文本
绘制文本钱要设置context的font属性,最简单的要设置字体大小和字体名称,可以多列几种字体如果不确定浏览器支持什么的话 context.textBaseline = "top"; context.font = "bold 20px Arial"; //bold加粗必须要放在最前面 context.fillStyle = "black"; //文本颜色 context.fillText("I'm stuck in a canvas. Someone let me out!", 10, 10); //绘制实心文本 context.font = "bold 40px Verdana,sans-serif"; context.lineWidth = 1; //轮廓的宽度 context.strokeStyle = "red"; //轮廓颜色 context.strokeText("I’m an OUTLINE", 20, 50); //绘制文本轮廓
-
阴影与填充
阴影
//绘制阴影的星星. (http://www.prosetech.com/html5/Chapter%2007/Shadows.html)
context.shadowOffsetX = 10; //阴影相对于内容的水平位置,可设定负值
context.shadowOffsetY = 10; //阴影相对于内容的垂直位置
context.shadowBlur = 4; //阴影的模糊程度,0表示锐利的阴影,其实也就是副本,这个值越大模糊程度越大,20已经比较模糊了,一般来说这个值>3的效果比较好
img = document.getElementById("star");
context.drawImage(img, 250, 30); //绘制图像
填充图案
图案和渐变比较容易实现但是却能够带来很好的效果。
var img = document.getElementById("brickTile");
var pattern = context.createPattern(img, "repeat"); //初始化填充的方式的变量(repeat:在两个方向填充;repeat-x:水平方向填充; repeat-y:垂直方向填充)
context.fillStyle = pattern; //设定context的填充变量
context.rect(0, 0, canvas.width, canvas.height);
context.fill();
填充渐变
填充的第二种方式即渐变,混合在一起的两种或多种颜色,支持线性渐变和放射性渐变
1. 左下角的❤
//创建渐变对象,这个是线性填充。传入两个坐标点(10,0)和(100,0),可见起点和终点是在一个水平线上,实际的混合颜色的宽度为90个像素,超过此范围的部分填充色会变成实色。渐变线决定最终的效果
var gradient = context.createLinearGradient(10, 0, 100, 0);
//设置5种颜色
gradient.addColorStop("0","magenta");
gradient.addColorStop(".25","blue");
gradient.addColorStop(".50","green");
gradient.addColorStop(".75","yellow");
gradient.addColorStop("1.0","red");
//调用绘制❤形的函数
drawHeart(60, 200);
context.fillStyle = gradient; //填充颜色
context.fill();
context.stroke();
2.右下角的❤
//放射性填充,接受的参数为渐变的起点(180,250),内部颜色有一个半径为10像素的圆表示,外部颜色由一个半径为50像素的圆表示 。超出范围的会显示实色
gradient = context.createRadialGradient(180, 250, 10, 180, 250, 50);
gradient.addColorStop("0","magenta");
gradient.addColorStop(".25","blue");
gradient.addColorStop(".50","green");
gradient.addColorStop(".75","yellow");
gradient.addColorStop("1.0","red");
drawHeart(180, 230);
context.fillStyle = gradient;
context.fill();
context.stroke();
绘制❤形有意思么?用的就是上一章里面的贝塞尔曲线
function drawHeart(x, y) {
context.beginPath();
context.moveTo(x, y);
context.bezierCurveTo(x, y - 40, x - 45, y - 40, x - 48, y);
context.bezierCurveTo(x - 45, y + 30, x, y + 40, x, y + 80);
context.bezierCurveTo(x, y + 90, x + 45, y + 40, x + 45, y);
context.bezierCurveTo(x + 45, y - 30, x, y - 30, x, y);
context.closePath();
}
-
赋予图形交互能力
canvas本身是非保留性的绘图界面,一旦图形绘制完成之后,Canvas不会保留图形的区域,因此添加交互性需要记录绘制的每一个对象,在用户单击的时候还要判断被单击的是不是其中的一个图形
http://www.prosetech.com/html5/Chapter%2007/InteractiveCircles_WithDrag.html
记录绘制的内容
需要知道修改和重绘什么内容,创建自定义对象是比较好的做法
function Circle(x, y, radius, color) { this.x = x; this.y = y; this.radius = radius; this.color = color; this.isSelected = false;}
使用这个函数,就可以利用var myCircle=new Circle(0,0,20,"red");来创建一个红色的圆的对象
点击【add Circle】按钮会触发addRandomCircle函数
function addRandomCircle() {
// 设定随机的大小和位置. var radius = randomFromTo(10, 60); var x = randomFromTo(0, canvas.width); var y = randomFromTo(0, canvas.height); //设定随机的颜色. var colors = ["green", "blue", "red", "yellow", "magenta", "orange", "brown", "purple", "pink"]; var color = colors[randomFromTo(0, 8)]; // 创建一个圆的对象. var circle = new Circle(x, y, radius, color); // 把这个对象放到数组中去 circles.push(circle); // 重画画布 drawCircles();}
function drawCircles() {
//在画圆的数组之前先清除画布,Canvas实际会在绘图逻辑执行完之后再清除或绘制所有内容,因此不会出现闪烁的情况.
context.clearRect(0, 0, canvas.width, canvas.height);
// 遍历数组.
for(var i=0; i<circles.length; i++) {
var circle = circles[i];
// 画圆
context.globalAlpha = 0.85;
context.beginPath();
context.arc(circle.x, circle.y, circle.radius, 0, Math.PI*2);
context.fillStyle = circle.color;
context.strokeStyle = "black";
//如果圆被选中的,则突出边框来显示状态
if (circle.isSelected) {
context.lineWidth = 5;
}
else {
context.lineWidth = 1;
}
context.fill();
context.stroke();
}
基于坐标的碰撞检测
检测碰撞是否发生,即计算鼠标点击的点是否落在某个形状内。用户单击画布的时候会选触发下面的函数
function canvasClick(e) {
//取得画布上被单击的点
var clickX = e.pageX - canvas.offsetLeft;
var clickY = e.pageY - canvas.offsetTop;
//查找被单击的圆圈。这里是反向遍历,因为绘制的时候正向遍历,后绘制的圆会叠加在先前对象的上面,因此选中的是后绘制的圆
for(var i=circles.length-1; i>=0; i--) {
var circle = circles[i];
//计算单击的点与每个圆圆心的距离,如果距离小于半径则说明该圆圈被选中了
var distanceFromCenter = Math.sqrt(Math.pow(circle.x - clickX, 2) + Math.pow(circle.y - clickY, 2))
if (distanceFromCenter <= circle.radius) {
//清除之前选择的圆圈
if (previousSelectedCircle != null) previousSelectedCircle.isSelected = false;
previousSelectedCircle = circle;
//选中的圆被设置为被选中的状态
circle.isSelected = true;
//更新显示
drawCircles();
//停止搜索
return;
}
}
}
一定记住要记录绘制的所有内容才能在将来灵活地修改并重绘它们
-
给Canvas添加动画
基本的动画
绘制动画要利用定时器。js提供了两个函数。setTimeout()以及setInterval()。前者是等待特定时间执行代码,后者是每隔一定时间执行代码。如果绘图时间比间隔时间长的话,使用setInterval可能导致停顿,因此setTimeout()可能比较好
setTimeout("functionName", time); //提供调用的函数以及等待的时间,以毫秒计数
活动:考虑一下怎么在Canvas中绘制一个长方形从高空掉落的画面呀
实例:弹跳球
http://www.prosetech.com/html5/Chapter%2007/Animation.html
// 每个球的基本信息.
function Ball(x, y, dx, dy, radius) {
this.x = x;
this.y = y;
this.dx = dx; //水平方向的速度
this.dy = dy; //垂直方向的速度
this.radius = radius;
this.strokeColor = "black";
this.fillColor = "red";
}
function addBall() {
// 取得用户要求的半径.
var radius = parseFloat(document.getElementById("ballSize").value);
// 创建球的对象.
var ball = new Ball(50,50,1,1,radius);
// 存储到数组.
balls.push(ball);
}
function clearBalls() {
// 清除球的数组
balls = [];
}
//负责计算球的位置和速度以及画出圆圈
function drawFrame() {
context.clearRect(0, 0, canvas.width, canvas.height);
// 以新路径开始绘图.
context.beginPath();
for(var i=0; i<balls.length; i++) {
// 移动到新的位置
var ball = balls[i];
ball.x += ball.dx;
ball.y += ball.dy;
// 添加入重力因素,球加速下落.
if ((ball.y) < canvas.height) ball.dy += 0.22;
// 具有摩擦力,球的水平移动的速度减慢.
ball.dx = ball.dx * 0.998;
//如果球碰到某一边就反弹回来.
if ((ball.x + ball.radius > canvas.width) || (ball.x - ball.radius < 0)) {
ball.dx = -ball.dx;
}
// 如果球碰到底部就反弹回来.
if ((ball.y + ball.radius > canvas.height) || (ball.y - ball.radius < 0)) {
ball.dy = -ball.dy*0.96;
}
// 用户是不是想要把球连接起来.
if (!document.getElementById("connectedBalls").checked) {
context.beginPath();
context.fillStyle = ball.fillColor;
}
else {
context.fillStyle = "white";
}
// 画球
context.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2);
context.lineWidth = 1;
context.fill();
context.stroke();
}
// 20ms重新调用一次函数.
setTimeout("drawFrame()", 20);
}
//判断是否被选中
function canvasClick(e) {
var clickX = e.pageX - canvas.offsetLeft;
var clickY = e.pageY - canvas.offsetTop;
for(var i in balls)
{
var ball = balls[i];
if ((clickX > (ball.x-ball.radius)) && (clickX < (ball.x+ball.radius)))
{
if ((clickY > (ball.y-ball.radius)) && (clickY < (ball.y+ball.radius)))
{
// 改变被选中的球的速度.
ball.dx -= 2;
ball.dy -= 3;
return;
}
}
}
}
实例:走迷宫
http://www.prosetech.com/html5/Chapter%2007/Maze.html
这里面使用到了像素颜色进行碰撞检测。
function checkForCollision() {
// 获取笑脸的位置,稍微扩宽一点点.
var imgData = context.getImageData(x-1, y-1, 15+2, 15+2);
var pixels = imgData.data;
// 检测像素的值,每个像素从rgba4个数值来表示的.
for (var i = 0; n = pixels.length, i < n; i += 4) {
var red = pixels[i];
var green = pixels[i+1];
var blue = pixels[i+2];
var alpha = pixels[i+3];
//检测是不是碰到了黑色的墙
if (red == 0 && green == 0 && blue == 0) {
return true;
}
// 是不是碰到了黑色的边
if (red == 169 && green == 169 && blue == 169) {
return true;
}
}
// 没有碰撞.
return false;
}
更多炫酷网站
-
-
- 标签:
- 画布
- 颜色
- 对象
- 效果
- 速度
- 函数画布
-
学习元评论 (0条)
聪明如你,不妨在这 发表你的看法与心得 ~