在 React 利用 Canvas 进行绘图
- 中
- en
前言
本来是利用 div
容器与 span
实体颜色方块的方式画图,然而在手机浏览器以及 Firefox 下显示会有错位的问题… 只好用 Canvas 画一份了。
因为 React 是直接作用于 DOM 元素,因此需要想办法获取绘制上下文来作图。
代码
首先是基类 CanvasComponent
,任何需要绘图的组件都衍生自此。
其中,除了 data
这个数据属性,其它作为 props
传进来的内容将原封不动的转发至 canvas
元素。
// CanvasComponent.tsx
import React, {CanvasHTMLAttributes, Component} from 'react';
export interface ICanvasComponentProps<T> extends CanvasHTMLAttributes<HTMLCanvasElement> {
data: T;
// must be present
width: number;
height: number;
}
abstract class CanvasComponent<P extends ICanvasComponentProps<T>, S, T> extends Component<P, S> {
protected canvas?: HTMLCanvasElement;
protected ctx?: CanvasRenderingContext2D;
protected drawId: number = 0;
private setupCanvas = (canvas: HTMLCanvasElement) => {
this.canvas = canvas;
if (canvas) {
this.ctx = canvas.getContext('2d')!;
this.dispatchDraw();
}
};
render() {
cancelAnimationFrame(this.drawId);
this.drawId = requestAnimationFrame(this.dispatchDraw);
const { data, ...props } = this.props;
return (
<canvas {...props} ref={this.setupCanvas} />
);
}
abstract draw(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void;
private dispatchDraw = () => {
if (this.ctx && this.canvas) {
this.draw(this.ctx, this.canvas);
}
};
}
export default CanvasComponent;
然后就是制作一个具现组件,比如绘制占用储存空间的 StorageBar
组件:
import CanvasComponent, {ICanvasComponentProps} from "./CanvasComponent";
interface ICanvasData {
used: number;
}
interface IPropsCanvas extends ICanvasComponentProps<ICanvasData> {
}
interface IState {
}
class StorageBar extends CanvasComponent<IPropsCanvas, IState, ICanvasData> {
draw(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void {
const { data, width, height } = this.props;
const {
used
} = data;
const w = width - 2;
const h = height - 2;
ctx.strokeStyle = '1px solid #000';
ctx.strokeRect(0, 0, width, height);
ctx.fillStyle = '#fff';
ctx.fillRect(1, 1, w, h);
const usedBar = w * used;
ctx.fillStyle = '#4d9510';
ctx.fillRect(1, 1, usedBar, h);
}
}
export default StorageBar;
最后,在页面调用储存条的显示:
<StorageBar
className="storage-bar"
data={{
used: used / total
}}
width={100}
height={16}
/>
具体效果
这个效果也可以在 stats.jixun.uk 查看。