canvas20181114

1. canvas 描边、填充、画线、闭合路径、非零环绕原则

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas</title>
</head>
<style>
    canvas {
        border: 1px solid red;
    }
</style>
<body>
<canvas id="can" width="300" height="300"></canvas>
<p class="juxing">矩形</p>
<p class="triangle">三角形锯齿问题</p>
<p class="triangle2">三角形闭合没锯齿,填充色</p>
<p class="middleEmpty">中空矩形</p>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
  let cvs = document.getElementById('can')
  let ctx = cvs.getContext('2d')
  $('.juxing').click(function () {
    a(ctx)
  })
  $('.triangle').click(function () {
    b(ctx)
  })
  $('.triangle2').click(function () {
    c(ctx)
  })
  $('.middleEmpty').click(function () {
    middleEmpty(ctx)
  })

  /**
   * 矩形
   * @param ctx
   */
  function a (ctx) {
    ctx.beginPath()
    // 初始位置
    ctx.moveTo(10, 10)
    // 线条
    ctx.lineTo(110, 10)
    ctx.lineTo(110, 110)
    ctx.lineTo(10, 110)
    ctx.lineTo(10, 10)
    // 描边
    ctx.stroke()
  }

  /**
   * 等腰三角形 ,继续画解决锯齿
   * @param ctx
   */
  function b (ctx) {
    ctx.beginPath()
    ctx.moveTo(110, 10)

    ctx.lineTo(160, 60)
    /*  这些线条接着画不会出现锯齿*/
    ctx.lineTo(60, 60)
    ctx.lineTo(110, 10)

    // 解决锯齿
    ctx.lineTo(160, 60)
    /* 这里也要接下继续画取消底点锯齿*/

    // 线条颜色设置,必须放在绘制之前  ctx.strokeStyle = css任意的颜色表示
    ctx.strokeStyle = 'blue'
    // 线宽设置,必须放在绘制之前
    ctx.lineWidth = 6

    ctx.stroke()
  }

  /**
   * 闭合路径:
   * 从当前路径的起点点到结束点连一条路径。
   * ctx.closePath()
   * 图形可以省去最后一条边,也解决了锯齿
   * @param ctx
   */
  function c (ctx) {
    ctx.beginPath()  // 清除当前路径,开启新路径,解决了所有图片颜色覆盖问题
    ctx.moveTo(110, 10)
    ctx.lineTo(160, 60)
    ctx.lineTo(60, 60)
    // 有了closePath,绘图直线图形时,最后一条边就可以省去了
    ctx.closePath()

    ctx.strokeStyle = 'yellow'
    // 线宽设置,必须放在绘制之前
    ctx.lineWidth = 6
    ctx.fillStyle = 'red'  // 填充色
    ctx.fill() // 填充   默认黑色
    ctx.stroke()
  }

  /*
      * 非零环绕原则:
      * 是用来判断哪些区域属于路径内( 计算结果非0,即为路径内,有填充色 )。
      * 在路径包围的区域中,随便找一点,向外发射一条射线,
      * 和所有围绕它的边相交,
      * 然后开启一个计数器,从0计数,
      * 如果这个射线遇到顺时针围绕,那么+1,
      * 如果遇到逆时针围绕,那么-1,
      * 如果最终值非0,则这块区域在路径内。
      *
      * 备注:基数边的区域一定在路径内,有填充色。(画线过程体现出图形的方向)
      * */



  // 封装一个绘制矩形的函数
  function juXing (startX, startY, width, height, lineWidth, strokeStyle, fillStyle) {

    ctx.moveTo(startX, startY)
    ctx.lineTo(startX + width, startY)
    ctx.lineTo(startX + width, startY + height)
    ctx.lineTo(startX, startY + height)
    ctx.closePath()

    ctx.lineWidth = lineWidth
    ctx.strokeStyle = strokeStyle
    ctx.fillStyle = fillStyle

    /*
      * 描边的时候,会占用原图形的一部分( 线宽的一半 )。
      * 所以,日常开发中,为了让线宽符合要求,
      * 最好先填充,再描边,防止填充时覆盖掉线宽的一半。
      * */

    ctx.fill()
    ctx.stroke()
  }

  /**
   * 中间空的填充矩形
   * 两个矩形 一个逆时针画,一个顺时针画,
   * 最中间部分,根据非零环绕原则等于0,则是在路径外,无填充色
   * @param ctx
   */
  function middleEmpty (ctx) {
    // 顺时针画一个大矩形
    juXing(10, 10, 100, 100, 6, 'skyblue')

    // 逆时针画一个小矩形
    ctx.moveTo(35, 35)
    ctx.lineTo(35, 55)
    ctx.lineTo(55, 55)
    ctx.lineTo(55, 35)
    // 顺 -> 两个都是顺时针的话,全都被填充了
    /*   ctx.moveTo( 35, 35 );
       ctx.lineTo( 55, 35 );
       ctx.lineTo( 55, 55 );
       ctx.lineTo( 35, 55 );*/
    ctx.lineWidth = 3
    ctx.strokeStyle = 'blue'
    ctx.closePath()
    ctx.stroke()

    // 一起填充
    ctx.fillStyle = 'red'
    ctx.fill()
  }


</script>
</body>
</html>
View Code

 2.canvas 线帽(线顶点)、线连接点、矩形api,清空画布,渐变矩形(一堆线加上 rgb线条颜色 )

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas</title>
</head>
<style>
    canvas {
        border: 1px solid red;
    }

    p {
        border: 2px solid blue;
        padding:10px;
        margin-right: 10px;
    }
</style>
<body>
<canvas id="can" width="300" height="300"></canvas>
<div style="display: flex;">
    <p class="juxing">线帽(线顶点)lineCap</p>
    <p class="triangle">线连接点 lineJoin</p>
    <p class="triangle2">矩形api</p>
    <p class="middleEmpty">清空画布</p>
    <p class="bian">渐变矩形</p>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
  let cvs = document.getElementById('can')
  let ctx = cvs.getContext('2d')
  $('.juxing').click(function () {
    a(ctx)
  })
  $('.triangle').click(function () {
    b(ctx)
  })
  $('.triangle2').click(function () {
    c(ctx)
  })
  $('.middleEmpty').click(function () {
    d(ctx, cvs)
  })
  $('.bian').click(function () {
    lineGarden(ctx)
  })

  /*
        * 设置线帽样式:
        * ctx.lineCap = ‘butt' 、'round'、'square'
        * butt是默认值,
        * round线头是圆的,多了两个半径出来,比默认的长
        * square线头两段各增加线宽的一半,比默认的长
        * */

  function a (ctx) {

    ctx.beginPath() // 防止 stroke 重绘之前的路径  默认 stroke 会生成所有路径
    // canvas在绘制线条的时候,会向左向右偏移线宽的一半,然后进行绘制。
    // 如果线宽为奇数,那么边缘的颜色值,会缩减一半(调整线条粗细看效果)。
    ctx.lineWidth = 10

    // 默认线头
    ctx.moveTo(10, 10)
    ctx.lineTo(210, 10)
    ctx.stroke()

    ctx.beginPath()
    ctx.lineCap = 'square'    // 增长线头,两端各增长线宽的一半
    // ctx.lineCap = 'round';  // 圆线头,两端的圆半径为线宽的一半
    ctx.moveTo(10, 30)
    ctx.lineTo(210, 30)
    ctx.stroke()
  }

  /*
      * 设置线连接点样式:
      * ctx.lineJoin = ‘miter' 、'round'、'bevel'
      * miter是默认值,两边向外延伸相交为尖尖角,
      * round是圆头,
      * bevel两边相连为一个斜面。
      * */
  function b (ctx) {
    ctx.beginPath()
    ctx.lineWidth = 10

    // 默认交点为尖尖角
    ctx.lineJoin = 'miter'
    ctx.moveTo(10, 10)
    ctx.lineTo(60, 110)
    ctx.lineTo(110, 10)
    ctx.stroke()

    ctx.beginPath()
    ctx.lineJoin = 'round'   // 交点为圆头
    // ctx.lineJoin = 'bevel'; // 交点为斜面
    ctx.moveTo(10, 50)
    ctx.lineTo(60, 150)
    ctx.lineTo(110, 50)
    ctx.stroke()

  }

  function c (ctx) {
    ctx.beginPath()
    /*
       * 画一个矩形路径:
       * ctx.rect( 起点x轴坐标,起点y轴坐标,宽,高 );
       * */
    ctx.rect(30, 10, 50, 50)
    ctx.stroke()

    /*
        * 绘制一个描边矩形,这个方法不会产生任何路径:
        * ctx.strokeRect( 起点x轴坐标,起点y轴坐标,宽,高 )
        * */
    ctx.strokeRect(30, 100, 50, 50)

    /*
     * 绘制一个填充矩形,这个方法不会产生任何路径:
     * ctx.fillRect( 起点x轴坐标,起点y轴坐标,宽,高 )
     * */
    ctx.fillRect(30, 170, 50, 50)

  }

  function d (ctx, cvs) {
    /*
      * 按照矩形的大小来清除画布中指定位置的内容:
      * ctx.clearRect( 起点x轴坐标,起点y轴坐标,宽,高 );
      * */
    ctx.clearRect(0, 0, cvs.width, cvs.height)

  }

  /* 渐变矩形  画一堆横线,加上rgb 线条*/
  function lineGarden (ctx) {
    // 需求,在50,50点绘制一个宽高各100的渐变矩形
    var i = 0, len = 100
    for (; i < len; i++) {
      // 为了防止重绘
      ctx.beginPath()
      ctx.moveTo(50, 50 + i)
      ctx.lineTo(150, 50 + i)
      // 红色通道值依次累加
      ctx.strokeStyle = 'rgb(' + 0 + ', ' + Math.floor(255 / 99 * i) + ', ' + 255 + ' )'
      // ctx.strokeStyle = 'rgb(' + 0 + ', ' + i + ', ' + 255 + ' )';
      ctx.stroke()
    }
  }
</script>
</body>
</html>
View Code

 3. canvas 折线图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        canvas {
            border: 1px solid red;
        }
    </style>
</head>
<body>
0.各部分都可单独研究绘画细节
1.定义坐标轴距离画布上下左右的边距
  定义线条粗细
2.求横坐标原点,x轴顶点,y轴顶点坐标,并画x/y轴
3.定义箭头的宽高,把箭头当成一个矩形,放大可看细节,并填充
4.画任意点,把点当成矩形来画  fillRect()  宽高为2,为1的话颜色太浅, 宽高可以适当变大变小
5.画折线,连接的点实际上连接的是小矩形的左上角顶点,要偏移一下坐标,坐标偏移小矩形宽高的一半,使线穿过小矩形的中心
6.计算 x/y轴的长度,计算数据的缩放比例
7.[1,5640,654,2,2,6,5,3,56] 根据y轴的缩放比例来修改原数据,根据x轴的缩放比例来显示各个点的间距

<canvas id="cvs" width="500" height="500"></canvas>
<script>
  var cvs = document.getElementById('cvs');
  var ctx = cvs.getContext('2d');

  ctx.lineWidth = 2;

  // 坐标轴距离画布上右下左的边距
  var padding = {
    top: 20,
    right: 20,
    bottom: 20,
    left: 20
  }

  // 坐标轴中箭头的宽和高
  var arrow = {
    width: 12,
    height: 20
  }

  // 求坐标轴上顶点的坐标
  var vertexTop = {
    x: padding.left,
    y: padding.top
  }

  // 求坐标轴原点的坐标
  var origin = {
    x: padding.left,
    y: cvs.height - padding.bottom
  }

  // 求坐标轴右顶点的坐标
  var vertexRight = {
    x: cvs.width - padding.right,
    y: cvs.height - padding.bottom
  }


  // 画坐标轴中的两条线
  ctx.moveTo( vertexTop.x, vertexTop.y );
  ctx.lineTo( origin.x, origin.y );
  ctx.lineTo( vertexRight.x, vertexRight.y );
  ctx.stroke();

  // 画上顶点箭头
  ctx.beginPath();
  ctx.moveTo( vertexTop.x, vertexTop.y );
  ctx.lineTo( vertexTop.x - arrow.width / 2, vertexTop.y + arrow.height );
  ctx.lineTo( vertexTop.x, vertexTop.y + arrow.height / 2 );
  ctx.lineTo( vertexTop.x + arrow.width / 2, vertexTop.y + arrow.height );
  ctx.closePath();
  ctx.fill();

  // 画右顶点箭头
  ctx.beginPath();
  ctx.moveTo( vertexRight.x, vertexRight.y );
  ctx.lineTo( vertexRight.x - arrow.height, vertexRight.y - arrow.width / 2 );
  ctx.lineTo( vertexRight.x - arrow.height / 2, vertexRight.y );
  ctx.lineTo( vertexRight.x - arrow.height, vertexRight.y + arrow.width / 2 );
  ctx.closePath();
  ctx.fill();

  // 求坐标轴默认可显示数据的最大值
  coordMaxX = cvs.width - padding.left - padding.right - arrow.height;
  coordMaxY = cvs.height - padding.top - padding.bottom - arrow.height;

  /*
  * 在坐标轴中指定位置画点,坐标算法:
  * 点的x轴:原点x坐标 + 点到原点的水平距离
  * 点的y轴:原点y坐标 - 点到原点的垂直距离
  * */

  // 需求,利用折线图的方式展示一下门口大爷酱香饼每日销售量
  // [ 10, 20, 50, 80, 120, 300, 100, 50, 2 ];

  var data = [ 100, 200, 400, 600, 1200, 1800, 1000, 500, 20 ];

  // 求数据缩放的比例
  var ratioX = coordMaxX / data.length;
  var ratioY = coordMaxY / Math.max.apply( null, data );

  // 根据比例,对元数据进行缩放
  var ratioData = data.map( function( val, i ) {
    return val * ratioY;
  });

  // 画点
  ratioData.forEach( function( val, index ) {
    ctx.fillRect( origin.x + ( index * ratioX) - 2, origin.y - val - 2, 4, 4 );
  });

  // 画折线
  ctx.beginPath();
  ratioData.forEach( function( val, index ) {
    ctx.lineTo( origin.x + ( index * ratioX), origin.y - val );
  });
  ctx.stroke();
</script>
</body>
</html>
View Code

 4. canvas 饼图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        canvas {
            border: 1px solid red;
        }
        p{
            line-height: 30px;
            margin:0;
        }
    </style>
</head>
<body>
<p>混入式继承,换种方式的原型继续 Person.prototype={}</p>
画扇形 <br/>
<p>1.计算数据占的比例 i/sum*360度</p>
<p>2. 度数转为弧度</p>
<p>3.每一个扇形的开始弧度等于上一个扇形的结束弧度
每一个扇形的结束弧度等于上一个扇形的结束弧度+所占的弧度</p>
<p>4. 画扇形 填充</p>
<p>5.0计算扇形平分线的坐标(即圆上点的坐标)</p>
<p>5.1画平分线,并延长(延长半径,则平分钱也延长了)并填充颜色,覆盖点扇形内的线</p>
<p>6绘制文字</p>
<canvas id="cvs" width="500" height="500"></canvas>
<script>
  (function (w) {

    // 把角度转换为弧度
    function angleToRadian (angle) {
      return Math.PI / 180 * angle
    }

    // 混入式继承
    function extend (o1, o2) {
      for (var key in o2) {
        // 只有o2自己的属性才会copy到o1身上
        if (o2.hasOwnProperty(key)) {
          o1[key] = o2[key]
        }
      }
    }

    /*
     * constrcutor { Pipe } 饼图构造函数
     * param { x: number } 圆心x轴坐标
     * param { y: number } 圆心y轴坐标
     * param { r: number } 圆半径
     * param { data: Array } 绘制饼图所需的数据
     * */
    function Pipe (x, y, r, data) {

      this.x = x
      this.y = y
      this.r = r
      this.data = data

      // 一组颜色
      this.colors = ['orange', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'peru', 'pink']
    }

    // 给原型扩充方法
    extend(Pipe.prototype, {

      // 绘制饼图
      draw: function () {

        // 在外面保存一下this
        var self = this

        // 数据的总和
        var num = 0
        this.data.forEach(function (obj) {
          num += obj.val
        })

        // 一个数据值所占用的角度
        var baseAngle = 360 / num

        // 假设一开始就绘制了一个起始为0,结束为0的扇形
        var startAngle = 0,
          endAngle = 0,
          lineAngle = 0,
          lineX, lineY

        // 画扇形
        this.data.forEach(function (obj, i) {

          // 每次进来,计算当前扇形的起始角度和结束角度

          // 下一个扇形的起始角度,是当前扇形的结束角度
          startAngle = endAngle
          // 这个结束角度 = 上一个扇形的结束角度 + 当前数值所对应的角度
          endAngle = endAngle + baseAngle * obj.val

          // 求扇形中间线的角度
          lineAngle = startAngle + baseAngle * obj.val / 2;
          /*
          * 根据中间线的角度,求中间的线的x和y坐标:
          * x = 圆心x + r * Math.cos( angleToRadian( pointAngle ) )
          * y = 圆心y + r * Math.sin( angleToRadian( pointAngle ) )
          * */
          lineX = self.x + ( self.r + 20 ) * Math.cos( angleToRadian( lineAngle ) );
          lineY = self.y + ( self.r + 20 ) * Math.sin( angleToRadian( lineAngle ) );

          // 第一个扇形
          ctx.beginPath()
          ctx.moveTo(self.x, self.y)
          ctx.arc(self.x, self.y, self.r, angleToRadian(startAngle), angleToRadian(endAngle))
          ctx.closePath()
          ctx.fillStyle = self.colors[i]
          ctx.fill()

          // 画每一个扇形的平分线
          ctx.beginPath();
          ctx.moveTo( self.x, self.y );
          ctx.lineTo( lineX, lineY );
          ctx.strokeStyle = self.colors[ i ];
          ctx.stroke();

          // 绘制文字
          if ( lineAngle >= 90 && lineAngle <= 270 ) {
            ctx.textAlign = 'right';
          }else {
            ctx.textAlign = 'left';
          }
          ctx.fillText( obj.msg, lineX, lineY );
        })
      }
    })

    // 把构造函数暴露到全局
    w.Pipe = Pipe

  }(window));

  var cvs = document.getElementById('cvs')
  var ctx = cvs.getContext('2d')

  // var pipe = new Pipe( 200, 200, 80, [ 10, 30, 50, 60, 20 ] );
  // [ 10, 30, 50, 60, 20 ]
  var pipe = new Pipe(200, 200, 80, [
    {
      val: 10,
      msg: '米饭'
    },
    {
      val: 30,
      msg: '面条'
    },
    {
      val: 50,
      msg: '馒头'
    },
    {
      val: 50,
      msg: '豆腐脑'
    },
    {
      val: 50,
      msg: '饺子'
    },
    {
      val: 90,
      msg: '汤圆'
    },
  ])
  pipe.draw()

</script>
</body>
</html>
View Code

 5.canvas 生成图片、画弧、扇形、文字

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas</title>
</head>
<style>
    canvas {
        border: 1px solid red;
    }

    p {
        border: 2px solid blue;
        padding: 10px;
        margin-right: 10px;
    }
</style>
<body>
<canvas id="can" width="300" height="300"></canvas>
<div style="display: flex;">
    <p class="juxing">虚线 setLineDash 生成图片 canvas.toDataUrl('image/png')</p>
    <p class="triangle">画弧</p>
    <p class="triangle2">扇形</p>
    <p class="middleEmpty">清空画布</p>
    <p class="bian">文字</p>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
  let cvs = document.getElementById('can')
  let ctx = cvs.getContext('2d')
  $('.juxing').click(function () {
    a(ctx, cvs)
  })
  $('.triangle').click(function () {
    b(ctx)
  })
  $('.triangle2').click(function () {
    c(ctx)
  })
  $('.middleEmpty').click(function () {
    d(ctx, cvs)
  })
  $('.bian').click(function () {
    e(ctx)
  })

  /*
      * 设置画线的时候空白部分和实线部分的大小。
      * ctx.setLineDash( [ 5, 3 ] )
      * 参数可以任意多个
      * */

  function a (ctx, cvs) {
    ctx.beginPath()

    ctx.lineDashOffset = 3 // 设置偏移量,进行微调用,效果不明显
    ctx.setLineDash([5, 3]) // 5像素实线,3像素空白,则变成了虚线
    ctx.moveTo(10, 10)
    ctx.lineTo(210, 10)
    ctx.stroke()

    let url = cvs.toDataURL('image/png')
    let img = document.createElement('img')
    img.src = url
    document.body.appendChild(img)
    /*
         * 获取线条绘制规则。
         * ctx.getLineDash()
         * */
    console.log(ctx.getLineDash())
  }

  /*
  * 画弧( 画的是路径 )
  * ctx.arc( 圆心x轴坐标,圆心y轴坐标,半径, 起点弧度,结束点弧度,是否逆时针画(可选) )
  * arc方法内部会先从路径结束点到弧的起点画一条路径线。
  * 起点弧度、结束点弧度以及弧度的方向共同决定了弧的大小。
  * */
  // 把角度转换为弧度
  function angleToRadian (angle) {
    return Math.PI / 180 * angle
  }

  function b (ctx) {
    ctx.beginPath()
    // 顺时针画一段弧  从x轴水平线右侧开始   默认顺时针
    ctx.arc(100, 100, 30, angleToRadian(90), angleToRadian(270))
    ctx.stroke()
    // 逆时针画一段弧
    ctx.beginPath()
    ctx.arc(200, 100, 30, angleToRadian(90), angleToRadian(270), true)
    ctx.stroke()
  }

  function c (ctx) {
    ctx.beginPath()
    /*
       * 画扇形:
       * 1、先设置路径起点为圆心
       * 2、画弧
       * 3、闭合路径
       * */
    ctx.beginPath()
    ctx.moveTo(100, 100)  //  arc方法内部会先从路径结束点到弧的起点画一条路径线。
    ctx.arc(100, 100, 90, angleToRadian(220), angleToRadian(310))
    ctx.closePath()
    ctx.stroke()
  }

  function d (ctx, cvs) {
    ctx.clearRect(0, 0, cvs.width, cvs.height)
    ctx.setLineDash([])   // 清空虚线
  }

  /*
        * 设置文字的属性
        * ctx.font = 和css语法一样。
        * 注意:这里设置字体大小时必须带单位,单位支持css的所有表示方式。
        * 注意:单独设置字体大小不会生效,必须要加一个额外属性样式。

ctx.strokeText( 描边文字, 参考x轴坐标,参考y轴坐标,限制文字的最大长度(可选) )
ctx.fillText( 填充文字, 参考x轴坐标,参考y轴坐标,限制文字的最大长度(可选) )

      * 设置文字的水平对其方式:
      * ctx.textAlign = 'left || start' 、 'right || end' 、 'center'
      * 默认值为start。

  * 设置文字的垂直对其方式:
  * ctx.textBaseline = 'top' 、'bottom'、'middle'、'alphabetic'、'hanging'、'ideographic'
  * 默认值为alphabetic。
  * */
  function e (ctx) {
    ctx.beginPath()
    // ctx.font = '3rem 微软雅黑'
    ctx.font = '28px 微软雅黑'
    ctx.fillStyle = 'deeppink';

    ctx.textAlign = 'left'  // 水平对齐
    ctx.textBaseline = 'middle' // 垂直对齐
    ctx.strokeText('描边字', 100, 100)  // 描边文字,空心字
    // ctx.strokeText('中文字',100,100,100)  // 第一个参数限制文字宽度,小了则压缩字体
    ctx.fillText('中文字', 200, 200)    // 填充文字,一般字

    // 绘制文字的参考点
    ctx.beginPath()
    ctx.arc(100, 100, 4, 0, Math.PI * 2)
    ctx.fill()

    // 在画布的左上角绘制一段文字
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';
    ctx.fillText( '左上角', 0, 0 );

    // 在画布的右上角绘制一段文字
    ctx.textAlign = 'right';
    ctx.textBaseline = 'top';
    ctx.fillText( '右上角', cvs.width, 0 );

  }
</script>
</body>
</html>
View Code

 6. canvas drawImage() 三参数、五参数、九参数、

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas</title>
</head>
<style>
    canvas {
        border: 1px solid red;
    }
</style>
<body>
<img src="NPC5.png" alt="" id="img"> <!--  160*260  -->
<canvas id="can" width="300" height="360"></canvas>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
  let cvs = document.getElementById('can')
  let ctx = cvs.getContext('2d')
  // 直接画,避开函数调用产生的异步问题
  /*
       * ctx.drawImage()
       * 绘制图像,有三种使用方式。
       * 三参数 图像、坐标x,坐标y
       * 五参数 图像、坐标x,坐标y, 图片宽,图片高
       * 九参数  把裁剪到的部分图像绘制到指定的坐标,并指定其大小。
       * drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
       *  sx, sy, sw, sh 切原图一小块的坐标宽高
       *  dx,dy,dw,dh 是最终图片的坐标,宽高
       * */

  let img = document.getElementById('img')
  img.onload = function () {
    // ctx.drawImage(img, 10, 10)
    // ctx.drawImage(img, 10, 100, img.width, img.height)

    // drawImage(image, dx, dy)
    // drawImage(image, dx, dy, dw, dh)
    // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

    ctx.drawImage(img, 0, img.height / 4 * 2, img.width / 4, img.height / 4,
      40, 140, img.width / 4, img.height / 4)

    let i = 0, step = 0
    let interval = setInterval(() => {
      ctx.clearRect(80, 140, cvs.width, cvs.height)
      ctx.drawImage(img, img.width / 4 * i, img.height / 4, img.width / 4, img.height / 4,
        180 - step * 5, 140, img.width / 4, img.height / 4)
      i++
      step++
      if (i > 3) {
        i = 0
      }
      if (180 - step * 5 < 80) {
        window.clearInterval(interval)
      }
    }, 100)
  }
</script>
</body>
</html>
View Code

7.canvas 平移 translate、旋转 rotate、缩放 scale

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        canvas {
            border: 1px solid red;
        }
    </style>
</head>

<body>
<canvas width="300" height="300" id="can"></canvas>
<script>
  let cvs = document.getElementById('can')
  let ctx = cvs.getContext('2d')

  /*
  * 平移,移动原来的矩形
  * 平移、缩放、旋转会叠加
  * */
  ctx.fillRect(30, 30, 20, 20)
  ctx.translate(20, 20)
  ctx.fillStyle = 'blue'
  ctx.fillRect(30, 30, 20, 20)

  /*
  * 旋转  相对于原点的旋转
  * 先平移到中间某位置
  * */
  ctx.translate(100, 100)
  ctx.fillRect(0, 0, 30, 30)  // 从 (0,0)开始

  ctx.rotate(Math.PI / 6)
  ctx.fillStyle = 'red'
  ctx.fillRect(0, 0, 30, 30)

  ctx.rotate(Math.PI / 6)
  ctx.fillStyle = 'pink'
  ctx.fillRect(0, 0, 30, 30)

  /*
  * 缩放  相对于原点的缩放
  * 先平移到中间某位置
  * 最好单独试,免得叠加计算出错
  *
  * */
  ctx.rotate(-Math.PI / 6*2)  // 旋转回来
  ctx.translate(60, 60)
  ctx.fillRect(0, 0, 40, 40)
  ctx.scale(0.5, 0.5)
  ctx.fillStyle = 'blue'
  ctx.fillRect(0, 0, 40, 40)

</script>
</body>
</html>
View Code

 8.工厂模式 函数返回时   return new Person(name,age)

 /* 工厂模式 */
  function Person (name, age) {
    this.name = name
    this.age = age
  }

  // 工厂
  // 工厂模式相当于省去new关键字
  function getPerson (name, age) {
    return new Person(name, age)
  }

  console.log(getPerson('女神', 18))

9.状态保存、回滚 ctx.save()   ctx.restore()   判断点在画布中, 旋转图形绘制(第9不太懂)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas</title>
</head>
<style>
    canvas {
        border: 1px solid red;
    }

    p {
        border: 2px solid blue;
        padding: 10px;
        margin-right: 10px;
    }
</style>
<body>
<canvas id="can" width="300" height="360"></canvas>
<div style="display: flex;">
    <p class="juxing">状态保存、回滚 ctx.save() restore()</p>
    <p class="triangle">判断点在画布中</p>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
  let cvs = document.getElementById('can')
  let ctx = cvs.getContext('2d')

  /*
  * 绘制一个旋转图形的步骤:
  * 1、先平移坐标轴到图形的中心
  * 2、旋转坐标轴
  * 3、绘制图形( 需要注意,平移旋转之后,坐标体系变化,不能按照之前定好的坐标来绘制旋转图形 )
  * */

  /* // 正常情况下的参考矩形
   ctx.fillStyle = 'pink';
   ctx.fillRect( 100, 100, 50, 50 );  // 原来的图形

   // 平移到矩形的中心
   ctx.translate( 125, 125 );
   // 旋转坐标系
   ctx.rotate( Math.PI / 180 * 30 );
   // 绘制图形
   ctx.fillStyle = 'blue';
   ctx.fillRect( -25, -25, 50, 50 );  //  新坐标为宽高的一半,不懂。*/


  // 定时器
  // 先统一平移到矩形的中心
  ctx.translate(125, 125)
  // 基于这个中心不断绘制旋转矩形
  setInterval(function () {
    // 清除上一次的矩形
    ctx.clearRect(-50, -50, cvs.width, cvs.height)
    // 旋转坐标系
    ctx.rotate(Math.PI / 180 * 4)
    // 绘制图形
    ctx.fillRect(-25, -25, 50, 50)
  }, 50)

  /* 工厂模式 */
  function Person (name, age) {
    this.name = name
    this.age = age
  }

  // 工厂
  // 工厂模式相当于省去new关键字
  function getPerson (name, age) {
    return new Person(name, age)
  }

  console.log(getPerson('女神', 18))

  /**
   * 状态保存: ctx.save();
   *  状态回滚: ctx.restore();
   * */
  $('.juxing').click(function () {
    // 保存下面的状态
    ctx.save()
    ctx.lineWidth = 10
    ctx.strokeStyle = 'blue'

    ctx.moveTo(10, 10)
    ctx.lineTo(210, 10)
    ctx.stroke()

    // 再保存下面的状态
    ctx.save()
    ctx.strokeStyle = 'green'

    ctx.beginPath()
    ctx.moveTo(10, 60)
    ctx.lineTo(210, 60)
    ctx.stroke()

    // 回滚
    ctx.restore()
    ctx.beginPath()
    ctx.moveTo(10, 90)
    ctx.lineTo(210, 90)
    ctx.stroke()

  })

  $('.triangle').click(function () {
    /*
       * 判断点在不在路径中:
       * ctx.isPointInPath( 要判断的点x轴坐标,要判断的点y轴坐标 )
       * */

    ctx.rect(10, 10, 50, 50)
    ctx.stroke()

    // 点击画布,判断点击的位置在不在路径中
    cvs.addEventListener('click', function (e) {
      var x = e.pageX - cvs.offsetLeft
      var y = e.pageY - cvs.offsetTop
      alert(ctx.isPointInPath(x, y))
    })

  })

</script>
</body>
</html>
View Code

 10 监听者模式(不懂)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>

        // 一个监听者 服务与 多个听众
        // 监听者
        var jianTingZhe = {

            // 听众列表
            listeners: {
                birdOver: [],
                birdFlappy: [],
                birdRotate: []
            },

            // 小鸟死亡触发时,告知所有监听死亡的听众
            triggerBirdOver: function() {
                this.listeners.birdOver.forEach( function( listen ) {
                    listen();
                });
            },

            // 小鸟飞翔的时候,告知所有监听飞翔的听众
            triggerBirdFlappy: function() {
                this.listeners.birdFlappy.forEach( function( listen ) {
                    listen();
                });
            },

            // 小鸟飞翔的时候,告知所有监听飞翔的听众
            triggerBirdRotate: function() {
                this.listeners.birdRotate.forEach( function( listen ) {
                    listen();
                });
            }

        };

        // 这是一个模块,整体可以认为是一个听众监听N多事件
        (function( w ) {
            // 小鸟死亡听众
            jianTingZhe.listeners.birdOver.push( function() {
                console.log( '小鸟死了,我就哭' );
            } );
            jianTingZhe.listeners.birdOver.push( function() {
                console.log( '小鸟死了,我很伤心' );
            } );
            jianTingZhe.listeners.birdOver.push( function() {
                console.log( '小鸟死了,我要埋葬它' );
            } );

            // 小鸟飞翔听众
            jianTingZhe.listeners.birdFlappy.push( function() {
                console.log( '小鸟飞了,我很搞笑' );
            });
            jianTingZhe.listeners.birdFlappy.push( function() {
                console.log( '小鸟飞了,我要把射下来' );
            });

            // 小鸟旋转听众
            jianTingZhe.listeners.birdRotate.push( function() {
                console.log( '小鸟转了,呱唧呱唧!' );
            });
        }( window ));

        // 这是另一个模块,整体可以认为是一个听众
        (function( w ) {
            // 小鸟旋转听众
            jianTingZhe.listeners.birdRotate.push( function() {
                console.log( '我是模块2' );
            });
        }( window ));


        // 监听到小鸟死了
        jianTingZhe.triggerBirdOver();
        // 监听到小鸟转了
        jianTingZhe.triggerBirdRotate()
        // 监听到小鸟飞翔了
        jianTingZhe.triggerBirdFlappy()
    </script>
</body>
</html>
View Code

11.动画帧函数  requestAnimationFrame 函数回调的触发是由浏览器来控制的,行就会比较稳定,适合用来做流畅的动画。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            height: 20px;
            background-color: black;
        }
    </style>
</head>
<body>
    <div id="div"></div>
    <script>
        /*
        * 请求动画帧函数,这个函数和setTimeout方法使用类似,
        * 他都是定时器,却别在于setTimeout可以自由指定回调的触发时间,
        * 而requestAnimationFrame函数回调的触发是由浏览器来控制的。
        *
        * requestAnimationFrame( callback )
        * 备注:当浏览器重绘页面的时候,就会调用这个callback,
        * 这样callbackg的执行就会比较稳定,适合用来做流畅的动画。
        * */

        /*setInterval( function() {
            console.log(111);
        }, 50);*/

        /*function con() {
            console.log(111);
            setTimeout( con, 50);
        }
        setTimeout( con, 50);*/

        // 简化setTimeout不断执行回调的方式
        /*(function con() {
            console.log(111);
            setTimeout( con, 50);
        }());*/

        // 把setTimeout改为requestAnimationFrame
        (function con() {
            console.log(111);
            requestAnimationFrame( con );
        }());
    </script>
</body>
</html>
View Code

 12. ctx2.drawImage( cvs1, 0, 0 );  // 把第一个canvas的内容绘制到第二个canvas中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            height: 20px;
            background-color: black;
        }
    </style>
</head>
<body>
    <canvas id="cvs1"></canvas>
    <canvas id="cvs2"></canvas>
    <script>
        /*
        * drawImage的第一个参数可以是img、canvas或video
        * */

        var cvs1 = document.querySelector( '#cvs1' );
        var cvs2 = document.querySelector( '#cvs2' );

        var ctx1 = cvs1.getContext( '2d' );
        var ctx2 = cvs2.getContext( '2d' );

        // 给第一个画布绘制内容
        ctx1.fillRect( 10, 10, 100, 100 );

        // 把第一个canvas的内容绘制到第二个canvas中
        ctx2.drawImage( cvs1, 0, 0 );
        ctx2.fillStyle = 'blue';
        ctx2.fillRect( 0, 0, 50, 50 );

    </script>
</body>
</html>
View Code

 

 

posted @ 2018-11-14 09:59  gyz418  阅读(157)  评论(1编辑  收藏  举报