前端常用canvas、svg和拉斐尔js详解
Canvas(画布)
canvas 最早由Apple引入WebKit,用于Mac OS X 的 Dashboard,后来又在Safari和Google Chrome被实现。
基于 Gecko 1.8的浏览器,比如 Firefox 1.5, 同样支持这个元素。
<canvas> 元素是WhatWG Web applications 1.0规范的一部分,也包含于HTML 5中。
体验Canvas
什么是Canvas?
HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像。
画布是一个矩形区域,您可以控制其每一像素。
canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
创建Canvas元素(行内块元素)
向 HTML5 页面添加 canvas 元素。
规定元素的 id、宽度和高度:
<canvas id="myCanvas" width="200" height="100"></canvas>
Canvas坐标系
通过JavaScript来绘制
/*获取元素*/
var myCanvas = document.querySelector('#myCanvas');
/*获取绘图工具*/
var context = myCanvas.getContext('2d');
/*设置绘图的起始位置*/
context.moveTo(100,100);
/*绘制路径*/
context.lineTo(200,200);
/*描边*/
context.stroke();
canvas兼容性问题
处理兼容性:在标签中写文字
<canvas width="800" height="600">
<strong style="color: #fff;">您的浏览器版本过低,不支持canvas。请尽快升级。</strong>
</canvas>
Canvas的基本使用
图形绘制
需要理解些概念:
- 路径的概念
- 路径的绘制
- 描边 stroke()
- 填充 fill()
- 闭合路径
- 手动闭合
- 程序闭合 closePath()
- 填充规则(非零环绕)
- 开启新的路径 beginPath()
设置样式
- 画笔的状态
- lineWidth 线宽,默认1px
- lineCap 线末端类型:(butt默认)、round、square
- lineJoin 相交线的拐点 miter(默认)、round、bevel
- strokeStyle 线的颜色
- fillStyle 填充颜色
- setLineDash([]) 设置虚线
- getLineDash() 获取虚线宽度集合
- lineDashOffset 设置虚线偏移量(负值向右偏移)
具体用法
常用属性设置
首先获取元素,再具体操作.
/*获取元素*/
var myCanvas = document.querySelector('#myCanvas');
/*获取绘图工具*/
var context = myCanvas.getContext('2d');
/*重新开始路径*/
context.beginPath();
/*设置绘图的起始位置*/
context.moveTo(100,100);
/*绘制路径*/
context.lineTo(100,200);
/*绘制路径*/
context.lineTo(200,200);
/*闭合路径*/
context.closePath();
/*设置线宽*/
context.lineWidth = 10;
/*设置描边颜色*/
context.strokeStyle = 'blue';
/*设置填充颜色*/
context.fillStyle = 'red';
/*描边*/
context.stroke();
/*填充*/
context.fill();
/*清除画布context.clearRect(x,y,width,height);*/
context.clearRect(0,0,myCanvas.width,myCanvas.height);
var myCanvas = document.querySelector('canvas');
var ctx = myCanvas.getContext('2d');
/*画平行线*/
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(200,20);
ctx.lineTo(300,100);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 10;
ctx.lineCap = 'butt';
ctx.lineJoin = 'miter';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100,200);
ctx.lineTo(200,120);
ctx.lineTo(300,200);
ctx.strokeStyle = 'red';
ctx.lineWidth = 20;
ctx.lineCap = 'square';
ctx.lineJoin = 'bevel';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100,300);
ctx.lineTo(200,220);
ctx.lineTo(300,300);
ctx.strokeStyle = 'green';
ctx.lineWidth = 30;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.stroke();
var myCanvas = document.querySelector('canvas');
var ctx = myCanvas.getContext('2d');
/*画线*/
ctx.moveTo(100,100.5);
ctx.lineTo(300,100.5);
/*[5,10] 数组是用来描述你的排列方式的*/
ctx.setLineDash([20]);
/*获取虚线的排列方式 获取的是不重复的那一段的排列方式*/
console.log(ctx.getLineDash());
/*如果是正的值 往后偏移*/
/*如果是负的值 往前偏移*/
ctx.lineDashOffset = -20;
ctx.stroke();
渐变设置
线性渐变
var lg = gd.createLinearGradient(x1,y1,x2,y2);渐变开始的位置到渐变结束的位置
lg.addColorStop(0-1系数,颜色);
var lg = gd.createLinearGradient(100,0,200,0);
//lg.addColorStop(0-1系数,颜色);
lg.addColorStop(0,'red');
lg.addColorStop(1,'green');
gd.beginPath();
gd.fillStyle = lg;
gd.fillRect(100,100,200,200);
var myCanvas = document.querySelector('canvas');
var ctx = myCanvas.getContext('2d');
/*fillStyle 'pink' '#000' 'rgb()' 'rgba()' */
/*也可以使用一个渐变的方案了填充矩形*/
/*创建一个渐变的方案*/
/*渐变是由长度的*/
/*x0y0 起始点 x1y1 结束点 确定长度和方向*/
var linearGradient = ctx.createLinearGradient(100,100,500,400);
linearGradient.addColorStop(0,'pink');
//linearGradient.addColorStop(0.5,'red');
linearGradient.addColorStop(1,'blue');
ctx.fillStyle = linearGradient;
ctx.fillRect(100,100,400,100);
/*pink---->blue*/
/*回想线性渐变---->要素 方向 起始颜色 结束颜色 */
/*通过两个点的坐标可以控制 渐变方向*/
径向渐变
var rg = gd.createRadialGradient(cx,cy,r,cx2,cy2,r2);
rg.addColorStop(0-1系数,颜色);
var rg = gd.createRadialGradient(200,200,10,200,200,100);
rg.addColorStop(0,'red');
rg.addColorStop(1,'green');
gd.beginPath();
gd.fillStyle = rg;
gd.fillRect(100,100,200,200);
实例练习
-
鼠标写字(常用客户端签字)
oC.onmousedown = function(ev){ // 清除画布 // gd.clearRect(0,0,oC.width,oC.height); gd.beginPath(); gd.moveTo(ev.pageX,ev.pageY); document.onmousemove=function(ev){ gd.lineTo(ev.pageX,ev.pageY); gd.stroke(); }; document.onmouseup=function(){ document.onmousemove=null; document.onmouseup=null; }; };
参考文档
Canvas图形绘制
矩形绘制
-
rect(x,y,w,h) 没有独立路径
-
strokeRect(x,y,w,h) 有独立路径,不影响别的绘制
-
fillRect(x,y,w,h) 有独立路径,不影响别的绘制
-
clearRect(x,y,w,h) 擦除矩形区域
x,y坐标,w,h长宽
var oC = document.getElementById('box'); var gd = oC.getContext('2d'); //描边矩形: gd.beginPath(); gd.lineWidth = 20; gd.strokeStyle = 'red'; gd.strokeRect(50,50,300,200); //填充矩形: gd.beginPath(); gd.fillStyle = 'red'; gd.fillRect(400,200,100,50);
圆弧绘制
-
弧度与角度转换
1.什么是弧度 是一种长度的描述单位
2.一个弧度怎么去描述 一个圆有多少个弧度 2 * π
3.一弧度有多长 一个弧度一个半径的长度//角度转弧度 function d2a(n){ return n*Math.PI/180; } //弧度转角度 function a2d(n){ return n*180/Math.PI; }
-
arc(x,y,r,s,e,a)
-
x 圆心横坐标
-
y 圆心纵坐标
-
r 半径
-
startAngle 开始角度
-
endAngle 结束角度
-
anticlockwise 是否逆时针方向绘制(默认false表示顺时针;true表示逆时针)
wifi例子
var oC = document.getElementById('box'); var gd = oC.getContext('2d'); function d2a(n){ return n*Math.PI/180; } gd.beginPath(); gd.arc(300,300,5,d2a(0),d2a(360),false); gd.fillStyle = 'red'; gd.fill(); gd.beginPath(); gd.arc(300,300,20,d2a(-90),d2a(0),false); gd.strokeStyle = 'blue'; gd.stroke(); gd.beginPath(); gd.arc(300,300,40,d2a(-90),d2a(0),false); gd.strokeStyle = 'pink'; gd.stroke(); gd.beginPath(); gd.arc(300,300,60,d2a(-90),d2a(0),false); gd.strokeStyle = 'yellow'; gd.stroke();
-
绘制文本
-
ctx.font = ‘微软雅黑’ 设置字体
-
strokeText(text,x,y,maxWidth)描边文字
-
fillText(text,x,y,maxWidth)填充文字
- text 要绘制的文本
- x,y 文本绘制的坐标(文本左下角,起始坐标-基准)
- maxWidth 设置文本最大宽度,可选参数
-
ctx.textAlign文本水平对齐方式,相对绘制坐标来说的
- left
- center
- right
- start 默认
- end
-
ctx.direction属性css(rtl ltr) start和end于此相关
- 如果是ltr,start和left表现一致
- 如果是rtl,start和right表现一致
-
ctx.textBaseline 设置基线(垂直对齐方式 )
- top 文本的基线处于文本的正上方,并且有一段距离
- middle 文本的基线处于文本的正中间
- bottom 文本的基线处于文本的证下方,并且有一段距离
- hanging 文本的基线处于文本的正上方,并且和文本粘合(印度文基线)
- alphabetic 默认值,基线处于文本的下方,并且穿过文字(英文基线)
- ideographic 和bottom相似,但是不一样(中文基线)
-
measureText() 获取文本宽度obj.width
var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.在画布的中心绘制一段文字*/ /*2.申明一段文字*/ var str = '您吃-,了吗'; /*3.确定画布的中心*/ var w = ctx.canvas.width; var h = ctx.canvas.height; /*4.画一个十字架在画布的中心*/ ctx.beginPath(); ctx.moveTo(0, h / 2 - 0.5); ctx.lineTo(w, h / 2 - 0.5); ctx.moveTo(w / 2 - 0.5, 0); ctx.lineTo(w / 2 - 0.5, h); ctx.strokeStyle = '#eee'; ctx.stroke(); /*5.绘制文本*/ ctx.beginPath(); ctx.strokeStyle = '#000'; var x0 = w/2; var y0 = h/2; /*注意:起点位置在文字的左下角*/ /*有文本的属性 尺寸 字体 左右对齐方式 垂直对齐的方式*/ ctx.font = '40px Microsoft YaHei'; /*左右对齐方式 (center left right start end) 基准起始坐标*/ ctx.textAlign = 'center'; /*垂直对齐的方式 基线 baseline(top,bottom,middle) 基准起始坐标*/ ctx.textBaseline = 'middle'; //ctx.direction = 'rtl'; //ctx.strokeText(str,x0,y0); ctx.fillText(str,x0,y0); /*6.画一个下划线和文字一样长*/ ctx.beginPath(); /*获取文本的宽度*/ console.log(ctx.measureText(str)); var width = ctx.measureText(str).width; ctx.moveTo(x0-width/2,y0 + 20); ctx.lineTo(x0+width/2,y0 + 20); ctx.stroke();
实例练习
-
绘制扇形
var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*在中心位置画一个半径150px的圆弧右上角 扇形 边 填充 */ var w = ctx.canvas.width; var h = ctx.canvas.height; /*把起点放到圆心位置*/ ctx.moveTo(w/2,h/2); ctx.arc(w/2,h/2,150,0,-Math.PI/2,true); /*闭合路径*/ //ctx.closePath(); ctx.fill();
-
绘制彩色六等份颜色随机圆
var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); var w = ctx.canvas.width; var h = ctx.canvas.height; /*分成几等分*/ var num = 360; /*一份多少弧度*/ var angle = Math.PI * 2 / num; /*原点坐标*/ var x0 = w / 2; var y0 = h / 2; /*获取随机颜色*/ var getRandomColor = function () { var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return 'rgb(' + r + ',' + g + ',' + b + ')'; } /*上一次绘制的结束弧度等于当前次的起始弧度*/ //var startAngle = 0; for (var i = 0; i < num; i++) { var startAngle = i * angle; var endAngle = (i + 1) * angle; ctx.beginPath(); ctx.moveTo(x0, y0); ctx.arc(x0, y0, 150, startAngle, endAngle); /*随机颜色*/ ctx.fillStyle = getRandomColor(); ctx.fill(); }
-
根据数据绘制饼图
var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.根据37期的年龄分布绘制饼图*/ /*2.准备统计的数据*/ /*15-20岁 6个*/ /*20-25岁 30个*/ /*25-30岁 10个*/ /*30-35岁 8个*/ var data = [6, 30, 10, 8]; /*3.在饼图表示出来*/ /*4.需要把数据转出弧度*/ var angleList = []; var total = 0; data.forEach(function (item, i) { total += item; }); console.log(total); /*第二是转换成弧度的时候就可以去绘制扇形 减少一次遍历*/ data.forEach(function (item, i) { var angle = Math.PI * 2 * (item/total); angleList.push(angle); }); console.log(angleList); /*5.根据弧度绘制扇形*/ var w = ctx.canvas.width; var h = ctx.canvas.height; var x0 = w/2; var y0 = h/2; /*获取随机颜色*/ var getRandomColor = function () { var r = Math.floor(Math.random() * 256); var g = Math.floor(Math.random() * 256); var b = Math.floor(Math.random() * 256); return 'rgb(' + r + ',' + g + ',' + b + ')'; } var startAngle = 0; angleList.forEach(function (item,i) { /*上一次绘制的结束弧度等于当前次的起始弧度*/ var endAngle = startAngle + item; ctx.beginPath(); ctx.moveTo(x0,y0); ctx.arc(x0,y0,150,startAngle,endAngle); ctx.fillStyle = getRandomColor(); ctx.fill(); /*记录当前的结束位置作为下一次的起始位置*/ startAngle = endAngle; });
做动画
绘制图片
- drawImage()
- 三个参数drawImage(img,x,y)
- img 图片对象、canvas对象、video对象
- x,y 图片绘制的左上角
- 五个参数drawImage(img,x,y,w,h)
- img 图片对象、canvas对象、video对象
- x,y 图片绘制的左上角
- w,h 图片绘制尺寸设置(图片缩放,不是截取)
- 九个参数drawImage(img,x,y,w,h,x1,y1,w1,h1)
- img 图片对象、canvas对象、video对象
- x,y,w,h 图片中的一个矩形区域
- x1,y1,w1,h1 画布中的一个矩形区域
- 三个参数drawImage(img,x,y)
序列帧动画(定时器)
- 绘制精灵图
- 动起来
- 控制边界
- 键盘控制
坐标变换
变形
原点永远都是画布的左上角。不可改变。
操作的都是画布
操作之后画图,为了不影响后面的图,要把画布还原回原来的样子
保存画布当前的状态gd.save();
还原画布之前保存的状态gd.restore();
先gd.save(),变形,画图,gd.restore();
-
平移 移动画布的原点
-
translate(x,y) 参数表示移动目标点的坐标
var oC = document.getElementById('box'); var gd = oC.getContext('2d'); function d2a(n){ return n*Math.PI/180; } gd.save(); gd.translate(-100,-100); gd.beginPath(); gd.fillStyle = 'rgba(255,0,0,0.3)'; gd.fillRect(0,0,oC.width,oC.height); gd.restore();
-
-
缩放
-
scale(x,y) 参数表示宽高的缩放比例
gd.scale(2,1); gd.beginPath(); gd.rect(50,50,50,50); gd.fill();
-
-
旋转
-
rotate(angle) 参数表示旋转角度
var oC = document.getElementById('box'); var gd = oC.getContext('2d'); function d2a(n){ return n*Math.PI/180; } gd.rotate(d2a(10)); gd.beginPath(); gd.fillStyle = 'rgba(255,0,0,0.3)'; gd.fillRect(0,0,300,300);
-
阴影
gd.shadowOffsetX x偏移
gd.shadowOffsetY y偏移
gd.shadowBlur 模糊度
gd.shadowColor 颜色
var oC = document.getElementById('box');
var gd = oC.getContext('2d');
function d2a(n){
return n*Math.PI/180;
}
gd.beginPath();
gd.shadowOffsetX = 10;
gd.shadowOffsetY = 10;
gd.shadowBlur = 20;
gd.shadowColor = 'red';
gd.fillRect(100,100,300,150);
canvas事件
canvas玩事件比较麻烦,只能判断坐标
中心画圆
var oC = document.getElementById('c1');
var gd = oC.getContext('2d');
var cx = oC.width/2,
cy = oC.height/2,
r = 100;
gd.beginPath();
gd.arc(cx,cy,r,d2a(0),d2a(360),false);
gd.fill();
点击利用勾股定理判断点击的点距离中心点的距离是否大于半径
oC.onclick=function(ev){
gd.clearRect(0,0,oC.width,oC.height);
var a = ev.pageX-cx;
var b = ev.pageY-cy;
var dis = Math.sqrt(a*a+b*b);
if(dis<r){
gd.beginPath();
gd.arc(cx,cy,r,d2a(0),d2a(360),false);
gd.fillStyle = 'red';
gd.fill();
}else{
gd.beginPath();
gd.arc(cx,cy,r,d2a(0),d2a(360),false);
gd.fillStyle = 'black';
gd.fill();
}
};
图片对象
导出图片
oC.toDataURL(mime-type);
image/png
image/jpg
image/gif
得到的是一个Base64
var oC = document.getElementById('box');
var gd = oC.getContext('2d');
function d2a(n){
return n*Math.PI/180;
}
var d = 0;
setInterval(function(){
gd.save();
gd.beginPath();
gd.translate(400,300);
gd.rotate(d2a(d+=4));
gd.rect(-100,-100,200,200);
gd.stroke();
gd.restore();
},16);
document.onclick = function(){
// base64
var result = oC.toDataURL('image/png');
var oImg = new Image();
oImg.src = result;
document.body.appendChild(oImg);
}
引入图片背景
var cp = gd.createPattern(oImg,平铺方式);
'repeat'
'repeat-x'
'repeat-y'
'no-repeat'
需要在image加载成功之后做。
eg:
var cp = gd.createPattern(oImg,'repeat');
gd.beginPath();
gd.fillStyle = cp;
gd.fillRect(0,0,500,500);
eg:
var oImg = new Image();
oImg.src = '1.jpg';
oImg.onload=function(){
var cp = gd.createPattern(oImg,'repeat');
gd.beginPath();
gd.fillStyle = cp;
gd.fillRect(0,0,500,500);
};
使用外部图片
gd.drawImage(
oImg,
dx,dy,dw,dh
);
gd.drawImage(
oImg,
sx,sy,sw,sh,
dx,dy,dw,dh
);
图片的起始坐标和宽高
canvas中其实坐标和宽高
eg:
gd.drawImage(
oImg,
15,198,108,148,
100,100,219,296
);
eg:
var oImg = new Image();
oImg.src = '2.jpg';
oImg.onload=function(){
gd.drawImage(
oImg,
128,60,91,40,
100,100,364,160
);
};
SVG
canvas 位图 (行内块)
放大会失真
加事件太麻烦
复杂
性能高
游戏、高级动画
SVG 矢量图(行内块)
放大不会失真
加事件so easy
简单
性能差
线条、图标
画线
<line x1 y1 x2 y2 stroke stroke-width></line>
x1,y1 起点位置
x2,y2 终点位置
stroke 描边颜色
stroke-width 线宽
用js操作
获取
html元素怎么获取svg元素就怎么获取
<svg width="800" height="600">
<line x1="100" y1="100" x2="200" y2="200" />
</svg>
svg{ background:#fff;}
line{ stroke:red; stroke-width:20; -webkit-transition:1s all ease; -webkit-transform:rotate(0deg);}
line:hover{ stroke:blue; stroke-width:50; -webkit-transform:rotate(50deg);}
矩形
矩形
rect
x y width height
eg:
<rect x="300" y="300" width="200" height="200" fill="yellow" stroke-width="20" stroke="blue" onclick="alert(1)" />
圆形
圆形
circle
cx cy r
eg:
<circle cx="300" cy="100" r="100" stroke="green" fill="#CCC" onclick="alert(2)" />
椭圆
椭圆
ellipse
cx cy rx ry
eg:
<ellipse cx="100" cy="100" rx="100" ry="30" fill="blue" stroke="yellow" stroke-width="30" stroke-opacity="0.5" fill-opacity="0.5" />
js操作svg
1、点击
2、绑定事件的点击
eg:
oL.addEventListener('click',function(){
alert(1)
},false)
3、改变style
eg:
oL.addEventListener('click',function(){
this.style.stroke = 'red';
},false)
4、getAttribute
5、setAttribute
eg:
alert(oL.getAttribute('x1'))
alert(oL.getAttribute('y1'))
alert(oL.getAttribute('y2'))
oL.setAttribute('x1','200')
oL.setAttribute('x2','400')
oL.setAttribute('y1','200')
oL.setAttribute('y2','400')
6、 var x1 = 100;
var y2 = 200;
setInterval(function(){
x1++;
y2++;
oL1.setAttribute('x1',x1);
oL1.setAttribute('y2',y2);
},30);
svg在事件冒泡中的使用小demo
拉斐尔js库
RaphaelJS
官网:http://dmitrybaranovskiy.github.io/raphael/
eg:
创建svg图形
var paper = Raphael(0, 0, 800, 600);
创建圆cx/cy/r
var circle = paper.circle(400,300,100);
圆的属性
circle.attr({
'stroke':'red',
'stroke-opacity':0.5,
'stroke-width':'20',
'fill':'blue',
'fill-opacity':0.5
});
eg:事件绑定的点击
circle.click(function(){
alert(1);
});
circle.click(function(){
alert(2);
});
eg:hover(双事件)
circle.hover(function(){
alert(1);
},function(){
alert(2);
});
eg:
circle.click(function(){
this.attr({
'stroke':'green',
'stroke-opacity':1,
'fill':'yellow',
'fill-opacity':1
});
});
eg:画块
var paper = Raphael(0, 0, 800, 600);
var rect = paper.rect(100,100,200,100,r);r===>圆角
rect.attr({
'fill':'aqua',
'stroke':'orange',
'stroke-width':10
});
eg:运动
rect.click(function(){
this.animate({
'fill':'red',
'x':500
},1000,'bounce');
});
eg:
var paper = Raphael(0, 0, 1000, 600);
var rect = paper.rect(100,100,200,100);
rect.attr({
'fill':'aqua',
'stroke':'orange',
'stroke-width':10,
'r':10
});
rect.click(function(){
this.animate({
'fill':'red',
'x':500,
'r':100
},1000,'back-out');
});