Canvas

作者: woow_wu7 | 来源:发表于2018-06-17 15:52 被阅读29次

    canvas是画布的意思

    <canvas 
      id="canvas" 
      width='500',    // 包括了画布的宽高和分辨率
      height='400', 
      style={{border: '1px solid #ddd', display: 'block'}}
    >
    </canvas>
    
    注意:指定大小时,最好不要用css方式指定大小,canvas自带的大小属性,值不要带单位。
    
    注意:width和height也可以在js中指定。
    
    通过 ( domTarget.getContext().width   和   domTarget.getContext().height  ) 指定
    
    

    先定义状态,后绘制。


    状态和绘制------ 先状态后绘制

    (1) 画一条直线 和 一个三角形

    componentDidMount() {
        const canvasDOM = document.getElementById('canvas');
        const canvas2D = canvasDOM.getContext('2d');   // 获得绘画的上下文环境
      
        canvas2D.beginPath()          // 路径起始___________________
          canvas2D.moveTo(100, 100)    // 状态,( 注意:0,0位置是画布的左上角 )
          canvas2D.lineTo(300, 300)    // 状态
          canvas2D.lineTo(300, 100)
          canvas2D.lineTo(100, 100)
        canvas2D.closePath()          // 路径结束___________________
          canvas2D.lineWidth = 5        // 设置线条的宽度
          canvas2D.strokeStyle = 'red'  // 设置stroke的样式,主要是设置颜色
          canvas2D.stroke()             // 绘制
          canvas2D.fillStyle = "yellow"  // 设置多边形填充的样式,lineTo首尾相连的多边形
          canvas2D.fill()                // 绘制
    
    
        canvas2D.beginPath()         // 重新绘制的线条,和上面的多边形是不同的状态
         canvas2D.moveTo(200, 50)
         canvas2D.lineTo(400, 50)
        canvas2D.closePath()
         canvas2D.strokeStyle = 'black'
         canvas2D.lineWidth = 1
         canvas2D.stroke()
     }
    
    注意:canvas坐标系,是以左上角为远点,向右为x轴正方向,向下为y轴正方向
    

    (2) 画弧线和圆

    • arc()方法
      arc() 方法创建弧/曲线(用于创建圆或部分圆)

    • context.arc(centerx, centery, radius, startAngle, endAngle, counterclockwise)

    • context.arc(中心点x, 中心点y, 半径, 起始角度,结束角度,顺逆时针)
      注意:最后一个参数 counterclockwise 为 true时,是逆时针画图。( 默认是顺时针! )
      注意:起始角度为三点钟位置,并且是以弧度计算的
      注意:弧度 = 角度 * 2pi/360
      注意:无论是顺时针还是逆时针,角度的位置都是不变的,0.5pi,1pi等都是原来的位置

    counter相反的的意思 ( clockwise顺时针的意思 )( counterclockwise逆时针 )

    componentDidMount() {
        canvas2D.beginPath()
        canvas2D.arc(400, 250, 50, 0, 90* 2*Math.PI/360, false);
        canvas2D.stroke()
    }
        
    说明:以(400,250)为圆心, 50为半径,三点钟方向为起始,加90°为结束,顺时针弧
    
    
      componentDidMount() {
        const canvasDOM = document.getElementById('canvas');
        const canvas2D = canvasDOM.getContext('2d');
    
        canvas2D.beginPath()
        canvas2D.arc(400, 250, 50, 0, 90* 2*Math.PI/360);
        canvas2D.closePath()      // 如果路径没有封闭,closePath会封闭路径
        canvas2D.stroke()
    
    
        canvas2D.beginPath()     // 第二个弧没有closePath,就不会封闭路径
        canvas2D.arc(400, 250, 50, 180 * 2 * Math.PI / 360, 270 * 2 * Math.PI / 360);
        canvas2D.stroke()
    
      }
    
    context.arc()方法

    (3) 画矩形

    rectangle是矩形的意思

    • rect() 创建矩形
    • fillRect() 绘制被填充的矩形
    • strokeRect() 绘制矩形(无填充)
    • clearRect() 在给定的矩形内清除指定的像素
    • context.rect(x,y,width,height);
    • context.fillRect(x,y,width,height);
    • context.strokeRect(x,y,width,height); ------- stroke() + rect()
        context.rect(100, 100, 100, 100)
        context.stroke()
        // react()方法定义状态,stroke()方法绘制
    
    
        context.fillStyle = 'red'
        context.fillRect(300, 200, 50, 50) 
        context.clearRect(320, 220, 10, 10)  // 清除指定区域
        context.stroke()
        // 注意先定义状态,在绘制
      
    
        context.strokeRect(120, 120, 50, 50)
        // strokeRect()该方法定义并绘制
    

    (4) 常用api

    (1) translate() ------- 重新映射画布上的 (0,0) 位置
    • context.translate(x,y);
    (2) fillText() ------- 在画布上绘制“被填充的”文本
    • context.fillText(text,x,y,maxWidth);
    (3) font() ------- 设置或返回文本内容的当前字体属性
    (4) textAlign ------- 设置或返回文本内容的当前对齐方式(左右对齐)
    (5) textBaseline ------- 设置或返回在绘制文本时使用的当前文本基线 (上下对齐)

    (6) save() ------- 保存当前环境的状态
    (7) restore() ------ 返回之前保存过的路径状态和属性
    (8) lineCap() ------ 设置或返回线条的结束端点样式
    (9) clearRect() ------ 在给定的矩形内清除指定的像素
    • context.clearRect(x,y,width,height);
    (10) createLinearGradient() ------- 创建线性渐变(用在画布内容上)
    • context.createLinearGradient(x0,y0,x1,y1); ---- // 开始xy,结束xy
    (11) rotate() -------旋转当前绘图 (以弧度计算)
    • context.rotate(angle); 以弧度计算

    时钟实例:

    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './App.css';
    
    class App extends Component {
      componentDidMount() {
        const canvasDOM = document.getElementById('canvas');
        const context = canvasDOM.getContext('2d');
    
        const height = context.canvas.height = 400;
        const width = context.canvas.width = 400;
        const radius = height/2;
        const hours = [3,4,5,6,7,8,9,10,11,12,1,2];
        const clockBackground = context.translate(radius, radius);
    
    
        const drawBackgournd = () => {
          context.save()
          context.beginPath()
          context.arc(0, 0, radius - 5, 0, 2 * Math.PI)
          context.fillStyle = '#333'
          context.fill()
          context.closePath()
    
          context.beginPath()
          context.arc(0, 0, radius - 70, 0, 2 * Math.PI)
          context.fillStyle = '#444'
          context.fill()
    
          hours.forEach((hour, index) => {
            const x = (radius - 90) * Math.cos(2 * Math.PI / 12 * index);
            const y = (radius - 90) * Math.sin(2 * Math.PI / 12 * index);
            context.font = "18px  Arial"
            context.fillStyle = 'black'
            context.textAlign = 'center'
            context.textBaseline = 'middle'
            context.fillText(hour, x, y)
          })
    
          for (let i = 0; i < 60; i++) {
            const radian = 2 * Math.PI / 60;
            const x = Math.cos(radian * i) * (radius - 70);
            const y = Math.sin(radian * i) * (radius - 70);
            context.beginPath()
            if (i % 5 === 0) {
              context.fillStyle = '#3dffff'
              context.arc(x, y, 2, 0, 2 * Math.PI)
            } else {
              context.fillStyle = '#3dffff'
              context.arc(x, y, 2, 0, 2 * Math.PI)
            }
            context.fill()
        }
        }
    
    
        const getSeconds = (s) => {
          context.save()
          context.beginPath()
          context.arc(0, 0, radius - 120, 1.5 * Math.PI, 2 * Math.PI / 60 * s - 0.5 * Math.PI)
          const grd = context.createLinearGradient(-radius, 0, radius, 0);
          grd.addColorStop('0', "#1E90FF");
          grd.addColorStop('1', "#8A2BE2 ");
          context.strokeStyle = grd
    
          context.lineWidth = 3
          context.lineCap = 'round'
          context.stroke()
          context.restore()
    
          context.save()
          context.beginPath()
          const radian = 2 * Math.PI / 60;
          const x = Math.cos(radian * s) * (radius);
          const y = Math.sin(radian * s) * (radius);
          context.rotate(2 * Math.PI / 60 * s)
          context.strokeStyle = "#FF1493"
          context.moveTo(0, -radius + 66)
          context.lineTo(0, -radius + 5)
          context.lineWidth = 1
          context.stroke()
          context.restore()
    
        }
    
        const getHours = (h) => {
          context.save()
          context.beginPath()
          const radian = 2* Math.PI / 12;
          context.rotate(radian * h)
          context.arc(0, -radius + 24, 14, 0, 2* Math.PI)
          context.fillStyle = ' #00FF7F'
          context.fill()
          context.restore()
        }
    
        const getMinutes = (m) => {
          context.save()
          context.beginPath()
          const radian = 2 * Math.PI / 60;
          context.rotate(radian*m)
          context.arc(0, -radius + 50, 10, 0, 2 * Math.PI)
          context.fillStyle = ' #FF1493'
          context.fill()
          context.restore()
        }
    
        const draw = () => {
          setInterval(() => {
            context.clearRect(0, 0, width, height)
            const time = new Date();
            const hour = time.getHours();
            const minute = time.getMinutes();
            const second = time.getSeconds();
            drawBackgournd()
            getSeconds(second)
            getHours(hour)
            getMinutes(minute)
            context.restore()
          }, 1000)
        }
        draw()
        
      }
    
      render() {
        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h1 className="App-title">Welcome to React</h1>
            </header>
            <p className="App-intro">
              To get started, edit <code>src/App.js</code> and save to reload.
            </p>
            <canvas 
              id="canvas"
              style={{border: '1px solid #ddd', display: 'block', margin:'0 auto'}}></canvas>
          </div>
        );
      }
    }
    
    export default App;
    
    
    时钟效果图









    // 2018-6-15事件复习
    DOM的事件操作(监听和触发),都定义在EventTarget接口,所有节点对象都部署了这个接口。

    1. EventTarget接口提供三个实例方法:
      addEventListener: 绑定事件监听函数
      removeEventListener: 移除事件监听函数
      dispatchEvent: 触发事件

    (1) EventTarget.addEventListener()

    - 注意:第二个参数可以是监听函数,也可以是具有handleEvent方法的对象。
    - 注意:第三个参数可以是userCapture布尔值,也可以是配置对象。
    - 注意:该方法没有返回值

    EventTarget.addEventListener() 用于在当前节点或对象上,定义一个事件监听函数,一旦事件发生,就会执行这个监听函数。

    addEventListener(type事件名称, listener监听函数, useCapture是否在捕获阶段触发)
    

    addEventListener函数的三个参数:

    • type :事件名称,大小写敏感
    • listener :监听函数,事件发生时,会调用该监听函数
      ( listener --- 还可以是一个具有handleEvent方法的对象)
    • useCapture : 是否在捕获阶段触发。---- 注意:默认是false,即该监听函数默认在冒泡阶段触发 (capture是捕获的意思)
      ( useCapture:--- 还可以是一个属性配置对象)
      第三个参数是配置对象时,属性如下:
    1. capture: boolean表示监听函数是否在捕获阶段触发。
    2. once: boolean表示监听函数是否只执行一次,然后自动移除
    3. passive: boolean表示监听函数时候调用事件的preventDefault方法
    componentDidMount() {
      const buttonX = document.getElementById('trigger');
      const triggerClick = () => {
        console.log('aaaa')
      }
    
      // target.addEventLister()函数在当前节点或者对象上绑定事件的监听函数
      // 当事件发生时,会触发监听函数。
    
      buttonX.addEventListener('click', triggerClick, false);
      
      buttonX.addEventListener('click', {    // 具有handleEvent方法的对象
        handleEvent: () => {
          console.log('第二个参数除了是监听函数外,还可以是一个具有handleEvent方法的对象');
          console.log('第三个参数除了是useCapture布尔值,表示时候在捕获阶段触发。 还可以是一个配置对象');
          console.log('addEventListener可以针对当前对象的同一个事件,添加不同的监听函数,按顺序执行');
        }
      }, {
        capture: false,  // 表示是否在捕获阶段触发
        once: false,   // 表示监听函数是否只执行一次
      });
    }
    
    • addEventListener() 可以对当前对象的同一个事件,添加不同的监听函数,这些监听函数,按照添加顺序,顺序的执行
    • 如果对同一个事件,添加多个同一监听函数,则只会执行一次
    • 如果希望向监听函数传递参数,可以用匿名函数包装
    componentDidMount() {
    
      const buttonX = document.getElementById('trigger');
    
      const go = (x) => {
        console.log(x, 'addEventListener要添加参数时,可以用函数包装')
      }
    
      buttonX.addEventListener('click', () => { go('parameter')});
    }
    
    • 监听函数中的this,指向当前事件所在的对象。
      注意:监听函数中的this,指向当前事件所在的对象。(不能是箭头函数)
     
    (重要)
    
    componentDidMount() {
      const buttonX = document.getElementById('trigger');
    
      const arrowheadFunction = () => {
        console.log(this, '监听函数是箭头函数是,this指向当前类');
      }
    
      const ordinaryFunction = function() {
        console.log(this, '监听函数是普通函数时,this指向当前事件所在的对象');
      }
    
      buttonX.addEventListener('click', arrowheadFunction, false);
      buttonX.addEventListener('click', ordinaryFunction, false);
    }
    
    

    (2) EventTarget.removeEventListener

    EventTarget.removeEventListener方法用来移除addEventListener方法添加的事件监听函数。该方法没有返回值。

    - 注意: 该方法没有返回值
    - 注意: removeEventListener()方法移除的监听函数,必须是addEventListener()方法添加的监听函数,并且必须在同一个元素节点,否则无效(同一元素节点,同一监听函数)
    div.addEventListener('click', function (e) {}, false);
    div.removeEventListener('click', function (e) {}, false);
    上面代码中,removeEventListener方法无效,因为监听函数不是同一个匿名函数。
    
    
    --------------------------------------------
    
    
    element.addEventListener('mousedown', handleMouseDown, true);
    element.removeEventListener("mousedown", handleMouseDown, false);
    上面代码中,removeEventListener方法也是无效的,因为第三个参数不一样。
    

    (3) EventTarget.dispatchEvent()

    - 注意:该方法返回一个boolean值
    - 注意:参数是一个Event对象的实例
    - 注意:如果参数是空,或者参数不是一个有效的事件对象,就会报错。
    1. target.dispatchEvent()在当前节点上触发指定事件。
    2. target.dispatchEvent()在当前节点触发指定事件。如果触发事件了,又添加了addEventListener()方法,就是触发当前对象绑定的事件监听函数。
    para.addEventListener('click', hello, false); //事件发生,会触发hellow监听函数
    
    var event = new Event('click');  // 创建一个click事件实例
    
    para.dispatchEvent(event);  // 在para节点上,触发event事件
    

    (4) 为事件绑定监听函数的三种方法

    (1) 在html标签中 ( on + 事件名 = " 将要执行的代码" )

    注意: 属性的值是将要执行的代码,而不是事件。
    因此如果要执行函数,不要忘记加上一对圆括号。
    注意: ( on + 事件名 = "执行代码") 指定的监听代码只在冒泡阶段触发

    (2) 元素节点的事件属性
    (3) EventTarget.addEventListener()

    为事件添加监听函数三种方法对比:
    EventTarget.addEventListener()优点:

    • 可以为同一个事件,添加多个监听函数
    • 可以控制事件发生的阶段(捕获阶段,目标阶段,冒泡阶段)
    • 够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数。
      除了 DOM 节点,其他对象(比如window、XMLHttpRequest等)也有这个接口,它等于是整个 JavaScript 统一的监听函数接口。

    (5) 事件的传播

    一个事件发生后,会在子元素和父元素之间传播(propagation是传播的意思),这种传播分为三个阶段

    • 这种三阶段的传播模型,使得同一个事件会在多个节点上触发。
    • 这种三阶段的传播模型,使得同一个事件会在多个不同节点上触发。
    第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)
    
    第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
    
    第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)
    
    名词解释:
    
    propagation传播
    
    capture捕获
    
    phase阶段
    
    bubbling冒泡      ------( bubble名词  , bubbly形容词 )
    
    delegation代理
    
    immediate立即的,直接的
    
    componentDidMount() {
      const buttonX = document.getElementById('trigger');
    
      const wrapperX = document.getElementById('wrapper');
    
      buttonX.addEventListener('click', (e) => {
        e.stopPropagation();  // 阻止事件传播
        console.log('1111')
      }, false);
    
      wrapperX.addEventListener('click', () => {console.log('2222')}, false);
      // false 在bubble阶段捕获事件
      // 由于事件发生的节点的监听函数,设置了阻止传播,所以外层的该click事件不会触发
    }
    

    (6) stopPropagation

    阻止当前节点的事件传播,不会阻止该节点上的其他事件的监听函数或者(即使是同一事件,同一节点的该事件的其他监听函数任然会执行)

    (7) stopImmediatePropagation

    彻底阻止这个事件的传播,不再触发后面所有click的监听函数

    • immediate立即的,直接的

    (8) Event对象

    事件发生后,会产生一个事件对象,作为参数传给监听函数

    • 浏览器提供Event对象,所有事件都是Event对象的实例对象
    • 或者说 所有事件都 继承了 Event.prototype对象
    • Event本身就是构造函数,可以用来生成实例对象
    event = new Event(type事件名称, options配置对象);
    
    
    第一个参数type是字符串,表示事件的名称;
    
    第二个参数options是一个对象,表示事件对象的配置。
    
    // 参数对象中有两个属性,bubbles和cancleable,默认都是false
    
    var ev = new Event(
      'look',
      {
        'bubbles': true,     // 是否冒泡
        'cancelable': false  // 是否可以通过Event.preventDefault()取消这个事件
      }   // 这两个属性默认都是false
    );
    document.dispatchEvent(ev);
    
    // 注意,如果不是显式指定bubbles属性为true,生成的事件就只能在“捕获阶段”触发监听函数。
    

    (9) Event.eventPhase 事件目前所处的阶段

    0,事件目前没有发生。
    1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。
    2,事件到达目标节点,即Event.target属性指向的那个节点。
    3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。
    

    相关文章

      网友评论

        本文标题:Canvas

        本文链接:https://www.haomeiwen.com/subject/lpxmeftx.html