在React中使用Canvas元素,让你的页面背景跳动起来!!!

博主在上周第一次上web交互课的时候,老师带着我们做了一个课堂demo,主要使用了canvas绘图元素。给大家看一下demo效果。
在这里插入图片描述
实现这个功能的原生js代码如下

class circle {
    /**
     * 构造器
     * @param color      颜色
     * @param radius     半径
     * @param v          速度
     * @param angle      角度
     * @param x          当前x坐标
     * @param y          当前y坐标
     */
    constructor(color, radius, v, angle, x, y) {
        this.color = color;
        this.radius = radius;
        this.v = v;
        this.angle = angle;
        this.x = x;
        this.y = y
    }
    /**
     * 绘制圆
     * @param  ctx 绘制上下文
     */
    draw(ctx) {
        //开始绘制路径
        ctx.beginPath();
        //绘制圆
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        //关闭绘制路径
        ctx.closePath();
        //设置fill颜色
        ctx.fillStyle = this.color;
        //fill
        ctx.fill();
    }
}

//圆对象数组
let arr = [];
//圆数量
const CNT = 100;
//绘制区域中心点
let centerX, centerY;
//canvas 元素
const canvas = document.querySelector('canvas');
//绘制上下文
const ctx = canvas.getContext('2d');
//设置canvas满屏
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//设置中心点
centerX = canvas.width / 2;
centerY = canvas.height / 2;
//实例化圆
for (let i = 0; i < CNT; i++) {
    let c1 = new circle(
   		//随机颜色
        "rgba(" + 255 * Math.random() + ","  + 255 * Math.random() + "," + 255 * Math.random() + "," +Math.random() +")",
        //随机半径
        66 * Math.random() + 1,
        //随机速度
        4 * Math.random() + 1,
        //随机角度
        360 * Math.random(),
        //x坐标
        centerX,
        //y坐标
        centerY
    );
    arr.push(c1);
}
//绘制方法
function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < CNT; i++) {
        //移动x坐标
        arr[i].x += arr[i].v * Math.cos(arr[i].angle);
        //移动y坐标
        arr[i].y += arr[i].v * Math.sin(arr[i].angle);
        //反弹(angle在笛卡尔坐标系)
        if (arr[i].y < arr[i].radius) {//上
            arr[i].angle = 0 - arr[i].angle;
        }
        if (arr[i].y > (canvas.height - arr[i].radius)) {  //下
            arr[i].angle = 0 - arr[i].angle;
        }
        if (arr[i].x < arr[i].radius) { //左
            arr[i].angle = Math.PI - arr[i].angle;
        }
        if (arr[i].x > (canvas.width - arr[i].radius)) {  //右
            arr[i].angle = Math.PI - arr[i].angle;
        }

        //调用圆的绘制方法
        arr[i].draw(ctx);
    }
    //延迟50ms
    setTimeout(draw, 50);
}
//调用绘制
draw();

因为最近实验室有一个项目,传统的登陆注册背景都太过单调,我就想着能不能来点花里胡哨的哈哈,所以就想把这个动态效果作为背景,那么就涉及到了在React中使用canvas元素的知识。

获取真实DOM

我们都知道react虚拟DOM,既然用canvas绘图肯定要获取真实DOM。demo如下:

<canvas ref={this.canvas} width="666" height="999">
    您的浏览器不支持canvas,请更换浏览器.
</canvas>
constructor(){
    super();
    // 创建一个 ref 来存储 canvas 的 DOM 元素
    this.canvas = React.createRef();
}
componentDidMount(){
	//获取当前真实canvasDOM
    const canvas = this.canvas.current;
}

说明:经过这样这个对象的的canvas属性指向被被ref包装了一层的canvas元素。然后在元素被渲染之后通过canvas属性的current获取真实canvasDOM。

ref的用法(英文):https://reactjs.org/docs/refs-and-the-dom.html
ref的用法(中文):https://reactjs.bootcss.com/docs/refs-and-the-dom.html

启用绘画

获取当前CanvasRenderingContext2D实例的原型。
componentDidMount() {
    const canvas = this.canvas.current;
    //绘制上下文在这里插入代码片
    const ctx = canvas.getContext("2d");
    console.log(ctx);
    console.log(Object.getPrototypeOf(ctx));
}

Object.getPrototypeOf()用于获取对象的原型,自己可以在浏览器上打印对比上面两的输出项的区别.

给CanvasRenderingContext2D原型添加方法
componentDidMount() {
    //获取真实canvasDOM
    const canvas = this.canvas.current;
    //圆对象数组
    let arr = [];
    //圆数量
    const CNT = 50;
    //绘制区域中心点
    let centerX, centerY;
    //绘制上下文
    const ctx = canvas.getContext("2d");
    //设置canvas满屏
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    //设置中心点
    centerX = canvas.width / 2;
    centerY = canvas.height / 2;
    //实例化圆
    for (let i = 0; i < CNT; i++) {
      let c1 = new Circle(
        //随机颜色
        "rgba(" +
        255 * Math.random() +
        "," +
        255 * Math.random() +
        "," +
        255 * Math.random() +
        "," +
        Math.random() +
        ")",
        //随机半径
        66 * Math.random() + 1,
        //随机速度
        4 * Math.random() + 1,
        //随机角度
        360 * Math.random(),
        //x坐标
        centerX,
        //y坐标
        centerY
      );
      arr.push(c1);
    }

    function draw() {
      //清除画布
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      for (let i = 0; i < CNT; i++) {
        //移动x坐标
        arr[i].x += arr[i].v * Math.cos(arr[i].angle);
        //移动y坐标
        arr[i].y += arr[i].v * Math.sin(arr[i].angle);
        //反弹(angle在笛卡尔坐标系)
        if (arr[i].y < arr[i].radius) {
          //上
          arr[i].angle = 0 - arr[i].angle;
        }
        if (arr[i].y > canvas.height - arr[i].radius) {
          //下
          arr[i].angle = 0 - arr[i].angle;
        }
        if (arr[i].x < arr[i].radius) {
          //左
          arr[i].angle = Math.PI - arr[i].angle;
        }
        if (arr[i].x > canvas.width - arr[i].radius) {
          //右
          arr[i].angle = Math.PI - arr[i].angle;
        }
        //调用圆的绘制方法
        arr[i].draw(ctx);
      }
      //延迟50ms
      setTimeout(draw, 42);
    }
    //调用绘制
    draw();
  }

注意:执行画圆的draw(ctx)函数不能放在componentDidMount()里面,否则无法绘制。
最后在render()里面渲染就可以了,这样就实现了在react中渲染canvas元素效果:
在这里插入图片描述
附上在react中实现的js代码:

import React, { Component } from "react";

class Circle extends Component {
  constructor(color, radius, v, angle, x, y) {
    super();
    this.color = color;
    this.radius = radius;
    this.v = v;
    this.angle = angle;
    this.x = x;
    this.y = y;
    // 创建一个 ref 来存储 canvas 的 DOM 元素
    this.canvas = React.createRef();
  }

  componentDidMount() {
    //获取真实canvasDOM
    const canvas = this.canvas.current;
    //圆对象数组
    let arr = [];
    //圆数量
    const CNT = 50;
    //绘制区域中心点
    let centerX, centerY;
    //绘制上下文
    const ctx = canvas.getContext("2d");
    //设置canvas满屏
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    //设置中心点
    centerX = canvas.width / 2;
    centerY = canvas.height / 2;
    //实例化圆
    for (let i = 0; i < CNT; i++) {
      let c1 = new Circle(
        //随机颜色
        "rgba(" +
        255 * Math.random() +
        "," +
        255 * Math.random() +
        "," +
        255 * Math.random() +
        "," +
        Math.random() +
        ")",
        //随机半径
        66 * Math.random() + 1,
        //随机速度
        4 * Math.random() + 1,
        //随机角度
        360 * Math.random(),
        //x坐标
        centerX,
        //y坐标
        centerY
      );
      arr.push(c1);
    }

    function draw() {
      //清除画布
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      for (let i = 0; i < CNT; i++) {
        //移动x坐标
        arr[i].x += arr[i].v * Math.cos(arr[i].angle);
        //移动y坐标
        arr[i].y += arr[i].v * Math.sin(arr[i].angle);
        //反弹(angle在笛卡尔坐标系)
        if (arr[i].y < arr[i].radius) {
          //上
          arr[i].angle = 0 - arr[i].angle;
        }
        if (arr[i].y > canvas.height - arr[i].radius) {
          //下
          arr[i].angle = 0 - arr[i].angle;
        }
        if (arr[i].x < arr[i].radius) {
          //左
          arr[i].angle = Math.PI - arr[i].angle;
        }
        if (arr[i].x > canvas.width - arr[i].radius) {
          //右
          arr[i].angle = Math.PI - arr[i].angle;
        }
        //调用圆的绘制方法
        arr[i].draw(ctx);
      }
      //延迟50ms
      setTimeout(draw, 42);
    }
    //调用绘制
    draw();
  }

  /**
   * 绘制圆
   * @param  ctx 绘制上下文
   */
  draw(ctx) {
    //开始绘制路径
    ctx.beginPath();
    //绘制圆
    ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    //关闭绘制路径
    ctx.closePath();
    //设置fill颜色
    ctx.fillStyle = this.color;
    //fill
    ctx.fill();
  }

  render() {
    return <canvas ref={this.canvas} className="circle"></canvas>;
  }
}

export default Circle;

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值