首页 > 解决方案 > 如何使画布绘图始终居中

问题描述

我正在研究另一个盒子内的盒子的可视化。

我做了改变外盒高度、宽度、长度的道具和改变内盒大小的缓冲道具。

我应该如何在画布上绘制两个框(更改缓冲区时的外框和内框)?

我做了高度缓冲区,我猜它正在工作,因为当我增加这个值时,内盒总是在外盒的中间,但是当我改变宽度或长度缓冲区时,无法弄清楚如何使它相同:/

这是我的组件代码:

import React, { Component } from 'react';
import './CanvasBox.css';

class CanvasBox extends Component {
    constructor(props) {
        super(props);
        this.canvas = React.createRef();
        this.state = {
            x1: this.props.x1,
            x2: this.props.x2,
            y: this.props.y,
            bufferLength: this.props.bufferLength || 5,
            bufferWidth: this.props.bufferWidth || 5,
            bufferHeight: this.props.bufferHeight || 5,
            color: this.props.color || '#ff8d4b'
        };
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (
            this.props.x1 !== prevProps.x1 ||
            this.props.x2 !== prevProps.x2 ||
            this.props.y !== prevProps.y ||
            this.props.bufferLength !== prevProps.bufferLength ||
            this.props.bufferWidth !== prevProps.bufferWidth ||
            this.props.bufferHeight !== prevProps.bufferHeight
        ) {
            this.setState(
                {
                    x1: this.props.x1,
                    x2: this.props.x2,
                    y: this.props.y,
                    bufferLength: this.props.bufferLength || 5,
                    bufferWidth: this.props.bufferWidth || 5,
                    bufferHeight: this.props.bufferHeight || 5,
                    color: this.props.color || '#ff8d4b'
                },
                this.draw()
            )
        }
    }

    componentDidMount = () => {
        console.log('componentDidMount')
        const ctx = this.canvas.current.getContext('2d');
        console.log('ctx', ctx)
        this.setState({ ctx }, () => this.draw());
    };

    drawCube = (x, y, wx, wy, h, color, offset) => {
        const ctx = this.state.ctx;
        // left side
        ctx.beginPath();
        ctx.moveTo(x, y);
        ctx.lineTo(x - wx, y - wx * 0.5);
        ctx.lineTo(x - wx, y - h - wx * 0.5);
        ctx.lineTo(x, y - h * 1);
        ctx.closePath();
        ctx.fillStyle = this.shadeColor(color, -10);
        ctx.strokeStyle = color;
        ctx.stroke();
        ctx.fill();

        // right side
        ctx.beginPath();
        ctx.moveTo(x, y);
        ctx.lineTo(x + wy, y - wy * 0.5);
        ctx.lineTo(x + wy, y - h - wy * 0.5);
        ctx.lineTo(x, y - h * 1);
        ctx.closePath();
        ctx.fillStyle = this.shadeColor(color, 10);
        ctx.strokeStyle = this.shadeColor(color, 50);
        ctx.stroke();
        ctx.fill();

        // top
        ctx.beginPath();
        ctx.moveTo(x, y - h);
        ctx.lineTo(x - wx, y - h - wx * 0.5);
        ctx.lineTo(x - wx + wy, y - h - (wx * 0.5 + wy * 0.5));
        ctx.lineTo(x + wy, y - h - wy * 0.5);
        ctx.closePath();
        ctx.globalAlpha = 0.5;
        ctx.fillStyle = this.shadeColor(color, 20);
        ctx.strokeStyle = this.shadeColor(color, 60);
        ctx.stroke();
        ctx.fill();
    };

    shadeColor = (color, percent) => {
        color = color.substr(1);

        const num = parseInt(color, 16),
            amt = Math.round(2.55 * percent),
            R = (num >> 16) + amt,
            G = ((num >> 8) & 0x00ff) + amt,
            B = (num & 0x0000ff) + amt;
        return (
            '#' +
            (
                0x1000000 +
                (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
                (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
                (B < 255 ? (B < 1 ? 0 : B) : 255)
            )
                .toString(16)
                .slice(1)
        );
    };

    draw = () => {
        // clear the canvas
        this.state.ctx.clearRect(0, 0, this.props.width, this.props.height);

        // draw the cube
        this.drawCube(
            this.props.width/2,
            this.props.height/2 + this.state.y,
            Number(this.state.x1),
            Number(this.state.x2),
            Number(this.state.y),
            '#0000',
            0
        );

        this.drawCube(
            this.props.width/2,
            this.props.height/2 + this.state.y - this.state.bufferHeight,
            Number(this.state.x1) - this.state.bufferLength*2,
            Number(this.state.x2) - this.state.bufferWidth*2,
            Number(this.state.y) - this.state.bufferHeight*2,
            this.state.color,
            0
        );

        requestAnimationFrame(this.draw);

    };

    render() {
        return (
            <div>
                <canvas width={this.props.width} height={this.props.height} ref={this.canvas} />
            </div>
        );
    }
}

export default CanvasBox;

这是我的工作示例:

https://codepen.io/robson-webdev/pen/xxxRKKM

非常感谢所有的建议和帮助。

标签: javascripthtmlreactjscanvashtml5-canvas

解决方案


我不是 100% 确定你想要什么,但这可能会有所帮助。

首先将画布原点移动到画布的中心。所以如果你画一些东西,例如ctx.fillRect(-10,-10,20,20)它在画布的中心。你可以这样做ctx.setTransform(1, 0, 0, 1, ctx.canvas.width / 2, ctx.canvas.height / 2)

然后只需将您的框相对于新原点移动即可。

将原点重置为左上角ctx.setTransform(1,0,0,1,0,0);

做我认为你想做的事情所需的模组都可以在绘图功能中完成。

draw = () => {
    const state = this.state;
    const ctx = state.ctx;

    ctx.setTransform(1,0,0,1,0,0);  // restore top left origin
    ctx.clearRect(0, 0, this.props.width, this.props.height);
    ctx.setTransform(1,0,0,1,ctx.canvas.width / 2, ctx.canvas.height / 2);

    this.drawCube(
        state.x1 / 2 - state.x2 / 2,  // center x
        state.x1 / 4 + state.x2 / 4 + state.y / 2,  // center y
        +(state.x1) ,
        +(state.x2) ,
        +(state.y) ,
        '#0000',
        0
    );
    const x1 = state.x1 - state.bufferLength;
    const y1 = state.x2 - state.bufferWidth;
    const z1 = state.y - state.bufferHeight * 2;
    this.drawCube(
        x1 / 2 - y1 / 2, x1 / 4 + y1 / 4 + z1/ 2,
        x1, y1, z1,
        state.color,
        10
    );

    requestAnimationFrame(this.draw);

};

推荐阅读