博主在上周第一次上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;