最近项目中有一个需求, 在提供的图片上画出图形区域,并且图形可以编辑改变。
问题点:
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();
效果如下
- 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" }}
/>
);
}
}
效果如下
两个点分别为(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;
}
效果如下