如何使用组的旋转属性来旋转组,并在 Konva.js 中重新定位它并做出反应

How to use the rotation properties of the group to rotate the group ,and reposition it exactly in Konva.js with react

首先,感谢您抽出时间回答我的问题。

我正在为一个小型图像编辑器项目工作,基于 konva.js 和 react.But 我 运行 遇到了一些问题,这些问题困扰了我几天。 我想使用组的旋转属性来旋转组以及其中的所有形状,当单击旋转按钮时,它可以正常工作,但是当我单击文本按钮时,并想将其绘制在图像上,然后重新定位确实,pic1是我的预期结果,但实际上,我得到了意想不到的结果显示为pic2,我该如何正确旋转组和其中的所有形状。

我的简单示例如下:

class Test extends React.Component{

    constructor(props) {
        super(props)
        this.state = {
            historyArray: [],
            isTexting: true,
            image: new window.Image(),
            layerPos:{
                x: 300,
                y: 100,
            },
            imageWidth: 0,
            imageHeight: 0,
            rotateDegrees: 0,
        }
    }
    componentDidMount() {

        const {historyArray} = this.state;
        this.state.image.setAttribute("crossOrigin", "anonymous");
        this.state.image.src = "https://vd.youniwote.com/homework/44evmey2cbd/submit/44evmey2cbe.jpg";
        this.state.image.onload = () => {
            let imageWidth = this.state.image.width > 620 ? 620 / (parseFloat(this.state.image.height) / parseFloat(this.state.image.width)) : this.state.image.width;
            let imageHeight = this.state.image.height > 620 ? 620 : this.state.image.height;
            let imageObj = {
                mode: 'image',
                image: this.state.image,
                width: imageWidth,
                height: imageHeight,
            }
            historyArray.push(imageObj);
            this.setState({
                historyArray,
                imageWidth,
                imageHeight,
            })
        }
    }
    clickStage = (event) => {
        const {mode, isTexting, historyArray} = this.state;
        if (mode == 'text') {
            if (isTexting) {
                let textarea = document.createElement('textarea');
                document.body.appendChild(textarea);
                textarea.style.position = 'absolute';
                textarea.style.top = event.evt.pageY + 'px';
                textarea.style.left = event.evt.pageX + 'px';
                textarea.style.width = 100;
                textarea.focus();

                this.setState({
                    isTexting: false,
                })

                textarea.addEventListener('keydown',  (e) => {
                    if (e.keyCode === 13) {
                        historyArray.push({
                            mode: "text",
                            x:this.getActualPosInStage(event).x,
                            y: this.getActualPosInStage(event).y,
                            text: textarea.value,
                        })

                        document.body.removeChild(textarea);
                        this.setState({
                            isTexting: true,
                            historyArray
                        })
                    }
                });
            }
        }
    }

    getActualPosInStage = (e) => {
        const {layerPos} = this.state;
        let stageBox = this.stage.getStage().getContainer().getBoundingClientRect();
        let mousePos = {
            x: e.evt.clientX,
            y: e.evt.clientY,
        }
        let x = Math.abs(mousePos.x - stageBox.left - layerPos.x);
        let y = Math.abs(mousePos.y - stageBox.top - layerPos.y);
        return {x,y}
    }
    render() {
        return (
            <div>
                <div style={{top: 0, left: 0}}>
                    <Button onClick={() => {
                        this.setState({
                            mode: 'text',
                            groupDraggable: false,
                        })
                    }}>Text</Button>

                    <Button style={{marginLeft: 20}} onClick={() => {
                        const {rotateDegrees} = this.state;
                        this.setState({
                            rotateDegrees: rotateDegrees + 90,
                            groupDraggable: false,
                            mode: 'rotate',
                        })

                        this.group.offsetX(this.group.width() / 2);
                        this.group.offsetY(this.group.height() / 2);

                        this.group.x(this.group.width() / 2);
                        this.group.y(this.group.height() / 2);

                    }}>Rotate</Button>
                </div>
                <Stage
                    onClick={(e) => {this.clickStage(e)}}
                    width={1000}
                    height={1000}
                    ref={node => {
                        this.stage = node;
                    }}
                >
                    <Layer
                        x={this.state.layerPos.x}
                        y={this.state.layerPos.y}
                        ref={node => {
                            this.layer = node;
                        }}
                    >
                        <Group
                            rotation={this.state.rotateDegrees}
                            width={this.state.imageWidth}
                            height={this.state.imageHeight}
                            ref={node => {
                                this.group = node;
                            }}
                        >
                            {
                                this.state.historyArray.map((item, index) => {
                                    if (item.mode == 'image') {
                                        return (
                                            <Image
                                                key={index}
                                                width={item.width}
                                                height={item.height}
                                                image={this.state.image}
                                                ref={node => {
                                                    this.imageNode = node;
                                                }}
                                            />
                                        )
                                    } else if (item.mode == 'text') {
                                        return(
                                            <Text
                                                key={index}
                                                x={item.x}
                                                y={item.y}
                                                text={item.text}
                                                fontSize={24}
                                                fill='red'
                                            />
                                        )
                                    }
                                })
                            }
                        </Group>
                    </Layer>
                </Stage>
            </div>
        )
    }
}

这些问题困扰了我几天,如果有人能回答我的问题,我会很感激

作为文本的位置,需要使用相对于组的位置。在您的代码中,您使用的是相对于舞台左上角的位置。它给你不正确的结果,因为你的组被移动和旋转。

所以你需要计算相对于组的位置。简单的方法就是INVERT组的绝对变换矩阵,应用到鼠标点上:

const stage = e.target.getStage();
const pos = stage.getPointerPosition();

const absTransform = this.group.getAbsoluteTransform();

const invertedTransform = new Konva.Transform(
  absTransform.getMatrix()
).invert();

const shapePos = invertedTransform.point(pos);

演示:https://codesandbox.io/s/l2k4k3y7n9