Canvas 2
1.使用Canvas进行绘图——绘制路径
路径:Path,类似于Photoshop中的钢笔工具,可以绘制直线、各种曲线;但路径本身是不可见的,有三种用途:
(1)创建选区(clip),对画布内容进行裁剪
(2)进行描边(stroke),绘制任意形状的折线
(3)进行填充(fill),填充出任意形状的图形
相关函数:
ctx.beginPath() 开始一条新的路径
ctx.arc() 绘制一条圆形/椭圆/拱形路径
ctx.moveTo(x,y) 移动到指定点
ctx.lineTo(x,y) 从上一点到指定点绘制直线
ctx.busierCurve() 绘制贝塞尔曲线
ctx.closePath() 闭合路径
---------------------------------
ctx.clip() 基于当前路径进行裁切
ctx.stroke() 基于当前路径进行描边
ctx.fill() 基于当前路径进行填充
例子
<h2>绘制路径——直线</h2>
<canvas id="c3" width="500" height="400">
</canvas>
<script>
var ctx = c3.getContext('2d');
//开始路径
ctx.beginPath();
//绘制路径——X轴
ctx.moveTo(50, 350);
ctx.lineTo(450,350);
ctx.lineTo(450-10,350+10); //小箭头
ctx.moveTo(450,350);
ctx.lineTo(450-10,350-10); //小箭头
//ctx.strokeStyle = '#f00';
//ctx.stroke();
//绘制路径——Y轴
//ctx.beginPath();
ctx.moveTo(50, 350);
ctx.lineTo(50,50);
ctx.lineTo(50-10,50+10); //小箭头
ctx.moveTo(50,50);
ctx.lineTo(50+10,50+10); //小箭头
//描边路径
ctx.strokeStyle = '#00f';
ctx.stroke();
</script>
<body>
<h2>绘制路径——绘制圆环进度条</h2>
<canvas id="c4" width="500" height="400">
</canvas>
<script>
var ctx = c4.getContext('2d');
//进度条的底色
ctx.lineWidth = 20;
ctx.strokeStyle = '#fff';
ctx.beginPath();
ctx.arc(250,200,100, 0, 2*Math.PI);
ctx.stroke();
//进度条颜色
ctx.strokeStyle = '#0a0';
var degree = 0; //角度值
var timer = setInterval(function(){
degree+=3;
ctx.beginPath();
//ctx.arc(250,200,100,0,degree*Math.PI/180);
ctx.arc(250,200,100,0-Math.PI/2,degree*Math.PI/180-Math.PI/2);
ctx.stroke();
},100);
</script>
</body>
<body>
<h2>绘制路径——圆拱形</h2>
<canvas id="c6" width="500" height="400">
</canvas>
<script>
var ctx = c6.getContext('2d');
/**闭嘴**/
function closeMouth(){
ctx.beginPath();
ctx.arc(250,200,100,0,2*Math.PI); //外环
ctx.lineTo(250,200); //横线
ctx.stroke();
ctx.beginPath();
ctx.arc(250,150,25,0,2*Math.PI)//眼球
ctx.fillStyle = '#abe';
ctx.fill();
ctx.beginPath();
ctx.arc(250+18,142,5,0,2*Math.PI)//眼神光大
ctx.fillStyle = '#fff';
ctx.fill();
ctx.beginPath();
ctx.arc(250+12,133,3,0,2*Math.PI)//眼神光小
ctx.fill();
}
//closeMouth();
/**张嘴**/
function openMouth(){
ctx.beginPath();
ctx.arc(250,200,100,Math.PI/4,7*Math.PI/4); //外环
ctx.lineTo(250,200); //上嘴唇
ctx.closePath(); //闭合路径得到下嘴唇
ctx.stroke();
ctx.beginPath();
ctx.arc(250,150,25,0,2*Math.PI)//眼球
ctx.fillStyle = '#abe';
ctx.fill();
ctx.beginPath();
ctx.arc(250+18,142,5,0,2*Math.PI)//眼神光大
ctx.fillStyle = '#fff';
ctx.fill();
ctx.beginPath();
ctx.arc(250+12,133,3,0,2*Math.PI)//眼神光小
ctx.fill();
}
//openMouth();
//使用定时器定期调用张嘴&闭嘴
var isOpen = true; //当前是否为张嘴
setInterval(function(){
ctx.clearRect(0,0,500,400);
if(isOpen){
closeMouth();
isOpen = false;
}else {
openMouth();
isOpen = true;
}
},300)
</script>
</body>
2.使用Canvas绘图——绘制图像
提示:图像的定位点在自己的左上角
ctx.drawImage( img, x, y ) //原始大小绘制
ctx.drawImage( img, x, y, w, h ) //使用指定的宽高绘制图像——可能进行图像大小缩放
注意:绘制图像时,必须等待图片异步加载完成
var img = new Image();
img.src = "xx.png"; //向服务器异步请求图片
img.onload = function(){ //图片加载完成
ctx.drawImage( img, x, y )
}
<body>
<h2>绘制图像-随机位置与大小</h2>
<canvas id="c7" width="500" height="500">
</canvas>
<script>
var ctx = c7.getContext('2d');
var img = new Image();
img.src = 'img/star.png';
img.onload = function(){
for(var i=0; i<20; i++){
var size = rn(20,150);//每个星星的大小都在20 150 之间
ctx.drawImage(img, rn(0,500-size), rn(0,500-size), size, size)
}
}
/**返回指定范围内的随机数**/
function rn(min, max){
return Math.random()*(max-min)-min;
}
</script>
</body>
图像的旋转问题:
(1)Canvas中的旋转不是画布旋转!而是“绘图上下文(画笔)”旋转。
ctx.rotate(deg)
(2)旋转必须有一个轴点——默认是坐标轴的原点;若想以某个固定的点旋转,就必须平移画布的坐标原点:
ctx.translate(x, y) //指定新的坐标轴原点
<body>
<h2>绘制图像</h2>
<canvas id="c7" width="500" height="400">
</canvas>
<script>
var ctx = c7.getContext('2d');
var p2 = new Image();
p2.src = "img/p2.png";
var degree = 0; //当前旋转的角度值
p2.onload = function(){
setInterval(function(){
degree+=5;
/****绘制左上角的飞机****/
ctx.clearRect(0,0,500,400);
ctx.rotate(degree*Math.PI/180);
ctx.drawImage(p2, -p2.width/2, -p2.height/2);
ctx.rotate(-degree*Math.PI/180);//因为转动的是画笔,所以,画完这个飞机后,为了不影响画下一个飞机,还要把画笔转回来;
/****绘制右上角的飞机****/
ctx.translate(500, 0);//坐标原点平移到右上角
ctx.rotate(degree*Math.PI/180);
ctx.drawImage(p2,-p2.width/2,-p2.height/2);
ctx.rotate(-degree*Math.PI/180);
ctx.translate(-500,0);//坐标原点平移回左上角
/****绘制左下角的飞机****/
ctx.translate(0, 400);//坐标原点平移到左下角
ctx.rotate(degree*Math.PI/180);
ctx.drawImage(p2,-p2.width/2,-p2.height/2);
ctx.rotate(-degree*Math.PI/180);
ctx.translate(0,-400);//坐标原点平移回左上角
/****绘制右下角的飞机****/
ctx.translate(500,400);//坐标原点平移到右下角
ctx.rotate(degree*Math.PI/180);
ctx.drawImage(p2,-p2.width/2,-p2.height/2);
ctx.rotate(-degree*Math.PI/180);
ctx.translate(-500,-400);//坐标原点平移回左上角
}, 100)
}
</script>
</body>
<body>
<h2>可以随鼠标移动的飞机</h2>
<canvas id="c7" width="400" height="600">
</canvas>
<script>
var ctx = c7.getContext('2d');
var p2 = new Image();
p2.src = "img/p2.png";
p2.onload = function(){
var x = 200; //飞机的坐标
var y = 500; //飞机的坐标
setInterval(function(){
//清除画布内容
ctx.clearRect(0,0,400,600);
//绘制飞机
ctx.drawImage(p2, x-p2.width/2, y-p2.height/2);
},100);
//为Canvas元素绑定鼠标移动事件监听函数
c7.onmousemove = function(e){
x = e.offsetX; //鼠标相对于画布的编译量
y = e.offsetY;
}
}
</script>
</body>
<body>
<h2>使用Canvas模拟验证码图片</h2>
<canvas id="vcode" width="90" height="26"></canvas>
<span id="change">看不清,换一张</span>
<script>
var change = document.getElementById('change');
change.onclick = function() {
doimg()
};
function doimg() {
var ctx = vcode.getContext('2d');
var w = vcode.width;
var h = vcode.height;
//填充一个矩形做背景
ctx.fillStyle = rc(180, 240);
ctx.fillRect(0, 0, w, h);
//绘制5个随机字符
var pool = 'ABCDEFGHJKLMNPQRSTWXY3456789';
ctx.textBaseline = 'bottom';
for (var i = 0; i < 5; i++) {
var x = 18 * i + 5; //文本的X
var y = h; //文本的Y
var c = pool[rn(0, pool.length)];
ctx.fillStyle = rc(30, 180); //文本颜色
ctx.font = rn(10, 30) + 'px Arial'; //文本大小
var deg = rn(-45, 45); //文本旋转
ctx.translate(x, y);
ctx.rotate(deg * Math.PI / 180);
ctx.fillText(c, 0, 0);
ctx.rotate(-deg * Math.PI / 180); //恢复到原来的角度
ctx.translate(-x, -y); //把画笔恢复到原位
}
//绘制5条干扰线
for (var i = 0; i < 5; i++) {
ctx.beginPath();
ctx.moveTo(rn(0, w), rn(0, h));
ctx.lineTo(rn(0, w), rn(0, h));
ctx.strokeStyle = rc(30, 180);
ctx.stroke();
}
//绘制50个杂色点——半径为1的圆
for (var i = 0; i < 50; i++) {
ctx.beginPath();
ctx.arc(rn(0, w), rn(0, h), 1, 0, 2 * Math.PI);
ctx.fillStyle = rc(30, 230);
ctx.fill();
}
}
doimg();
//获取一个指定范围内随机数 random number
function rn(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
//获取一个指定范围内随机颜色 random color
function rc(min, max) {
var r = rn(min, max);
var g = rn(min, max);
var b = rn(min, max);
return `rgb(${r},${g},${b})`;
}
</script>
</body>
<body>
<h2>使用Canvas+Audio实现一个播放器</h2>
<audio src="res/bg.mp3"></audio>
<canvas id="c2" width="400" height="500"></canvas>
<script>
var progress = 0; //全局函数 每个函数都可以改变它 等到下面4个加载函数全部运行完,progress 的值增加到100 说明所有图片加载完成 就开始执行 startDraw()函数
var bg = new Image();
bg.src = 'img/bg.jpg';
bg.onload = function(){
console.log('1背景图片加载完成');
progress += 20;
if(progress===100){
startDraw();
}
}
var cover = new Image();
cover.src = 'img/disc.png';
cover.onload = function(){
console.log('2封面图片加载完成');
progress += 40;
if(progress===100){
startDraw();
}
}
var play = new Image();
play.src = 'img/play.png';
play.onload = function(){
console.log('3播放按钮图片加载完成');
progress += 20;
if(progress===100){
startDraw();
}
}
var pause = new Image();
pause.src = 'img/pause.png';
pause.onload = function(){
console.log('4暂停按钮图片加载完成');
progress += 20;
if(progress===100){
startDraw(); //总进度已到100,开始绘图
}
}
function startDraw(){
console.log('开始绘图')
var ctx = c2.getContext('2d');
//绘制背景图
ctx.drawImage(bg, 0,0, c2.width, c2.height);
//绘制黑色胶片
ctx.beginPath();
ctx.arc(c2.width/2,c2.height/2,100,0,2*Math.PI);
ctx.fill();
//绘制胶片封面图片 250-70.7
var x = c2.width/2 - 100*Math.sin(Math.PI/4);
var y = c2.height/2 - 100*Math.sin(Math.PI/4);//这两行代码是为了计算 胶片封面图片左上角的坐标(也可以理解为 从哪个点开始绘制)
var w = 2*100*Math.sin(Math.PI/4);//胶片封面图片宽和高;
ctx.drawImage(cover,x,y,w,w);
//绘制播放按钮
ctx.drawImage(play,x,y+180,w,w);
}
//为“播放按钮添加事件监听函数”——实际是为Canvas添加
c2.onmousemove = function(e){
var x = e.offsetX;
var y = e.offsetY;
var cx = 200; //按钮的圆心
var cy = 430;
var r = 70.7; //按钮的半径
//计算此点到按钮圆心的距离是否小于半径
if(Math.sqrt((x-cx)*(x-cx)+(y-cy)*(y-cy))<=70.7){
c2.style.cursor = 'pointer';
}else {
c2.style.cursor = 'default';
}
}
//为“播放按钮添加事件监听函数”——实际是为Canvas添加
c2.onclick = function(e){
var x = e.offsetX;
var y = e.offsetY;
var cx = 200; //按钮的圆心
var cy = 430;
var r = 70.7; //按钮的半径
//计算此点到按钮圆心的距离是否小于半径
if(Math.sqrt((x-cx)*(x-cx)+(y-cy)*(y-cy))<=70.7){
console.log('按钮被点击了');
}
}
</script>
</body>
3.总结:Canvas绘图技术相关属性和方法——重点
ctx.fillStyle = 颜色/渐变色/样式;
ctx.strokeStyle =颜色/渐变色/样式;
ctx.font = "10px sanse-sarif";
ctx.textBaseline = '';
ctx.lineWidth = 1;
(1)绘制矩形
ctx.fillRect() ctx.strokeRect() ctx.clearRect()
(2)绘制文本
ctx.fillText() ctx.strokeText() ctx.measureText()
(3)绘制路径
ctx.beginPath()
ctx.moveTo() ctx.lineTo() ctx.arc() ctx.rect()
ctx.stroke() ctx.fill() ctx.clip()
(4)绘制图像
ctx.drawImage(img,x,y,[w],[h])
ctx.rotate( deg )
ctx.translate( x, y )
4.基于Canvas的图表绘制框架/工具库
(1)Chart.js —— 免费开源,有九类图表可供使用
(2)Echart.js —— 免费的,百度提供,中文手册易上手
(3)FusionCharts.js —— 收费的,功能强大
5.第三方图表绘制工具——Chart.js的使用
(1)寻找官网:http://www.chartjs.org
(2)看官网介绍:
Simple yet flexible JavaScript charting for developers
(3)看官网提供的使用文档(API Document)
http://www.chartjs.org/docs/
(4)根据官网上的实例编写代码:
<canvas id="c2" width="600" height="400"></canvas>
<script src="js/Chart.js"></script>
<script>
new Chart(c2, {
type: 'bar', //图表类型
data: { }, //图表数据
options: { } //可选参数
});
</script>
1.补充知识点:
若Canvas绘图中需要绘制多张图片,必须等待所有图片加载完成才能开始绘制;而每张图片的加载都是异步请求,彼此没有先后顺序,哪一张先加载完成完全无法预测。
var progress = 0; //全局加载进度
var img = new Image();
img.src = 'xx.jpg';
img.onload = function(){
progress += 10; //该图片权重
if(progress===100){
startDraw();
}
}
2.补充知识点:如何为Canvas中的某个图形/图像添加事件监听
HTML中,只有标签/元素才能添加事件监听。
Canvas绘图技术,只有一个标签——Canvas。
为某个部分中的图形/图像添加事件监听,只能委托给Canvas,获取事件发生的坐标,是否处于目标图像/图形范围内。
结论:为Canvas的某个图形/图像添加事件监听非常麻烦!