Canvas 2

2019-07-05  本文已影响0人  向上而活

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的某个图形/图像添加事件监听非常麻烦!

上一篇下一篇

猜你喜欢

热点阅读