React 绘制图形

作者: Lingli_yu | 来源:发表于2020-07-14 16:26 被阅读0次

最近项目中有一个需求, 在提供的图片上画出图形区域,并且图形可以编辑改变。

问题点:
1、图形为多边形,画图需要canvas来实现;
2、图片存在缩放的问题,需要让canvas match渲染出的图片;

React Canvas

先放结论:react canvas 绘制2d 画布有bug,绘制指定两点坐标的图形错误。
后来发现,原来从18年开始,react 已不再维护canvas 相关api。

  • 原生Canvas绘图
<canvas id="canvas"></canvas>
....
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(100, 100);
ctx.stroke();

效果如下


原生canvas
  • React Canvas 绘图
import React from "react";

export default class CanvasTest extends React.Component {
  constructor(props) {
    super(props);
    this.ref = React.createRef();
  }

  componentDidMount() {
    const { current } = this.ref;
    if (!current) return;
    const ctx = current.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(100, 100);
    ctx.stroke();
  }

  render() {
    return (
      <canvas
        ref={this.ref}
        style={{ width: "300px", height: "300px", border: "1px solid #000" }}
      />
    );
  }
}

效果如下


react canvas

两个点分别为(0,0), (100,100)。绘制出的线的斜率不一致。

  • 解决办法:React Konva
    React Konva 是Konva的基于React 的实现。所有相关的API 可以查阅Konva中的文档。
Canvas match 图片

将canvas 以绝对定位的方式覆盖在图片上。以图片缩放比例的方式来动态计算canvas 大小。

// 需要在图片渲染后再调用calcViewPosition函数,所以需要根据实际的效果添加setTimeout; 
    calcViewPosition = () => {
        if (!this.props.imgSize) {
            return;
        }
        const parent = this.containerRef.current;
        if (!parent) {
            return;
        }
        const parentRect = parent.getBoundingClientRect();
        const parentWidth = parentRect.width;
        const parentHeight = parentRect.height;
        const imageWidth = this.props.imgSize[0];
        const imageHeight = this.props.imgSize[1];

        const ratioX = imageWidth / parentWidth;
        const ratioY = imageHeight / parentHeight;
        let ratio = 0;

        let offset = { left: 0, top: 0 };

        if (ratioX < ratioY) {
            ratio = this.props.objectFitContain ? ratioY : ratioX;
        } else {
            ratio = this.props.objectFitContain ? ratioX : ratioY;
        }
        offset.left = (parentWidth - imageWidth / ratio) / 2;
        offset.top = (parentHeight - imageHeight / ratio) / 2;

        this.setState({
            offset,
            ratio
        });
    }
...
            <div className={styles.container} ref={this.containerRef}>
                {imgSource && (
                    <img src={imgSource} style={{ objectFit: imgObjectFit }} className={styles.img} draggable={false} />
                )}
                <Stage
                    width={size.width / scale}
                    height={size.height / scale}
                    className={stageRectClass}
                    style={style}
                    onMouseUp={this.mouseUpEventHandler}
                    onMouseMove={this.mouseMoveEventHandler}
                >
                    <Layer>
                        <Line points={pointers} stroke={lineColor} closed={true} strokeWidth={rectLineWidth} />
                        <Line
                            points={linePointers}
                            stroke={distanceLineColor}
                            closed={true}
                            strokeWidth={distanceLineWidth}
                        />
                    </Layer>
                </Stage>
            </div>
// Less 
.container {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.stage {
    position: absolute;
    z-index: 10;
    top: 0;
    left: 0;
}

.img {
    width: 100%;
    height: 100%;
}

.stageLine {
    .stage();
    z-index: 20;
}

效果如下


demo

相关文章

网友评论

    本文标题:React 绘制图形

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