每次在React中更改状态时,有没有办法不重新加载画布?

时间:2017-07-27 17:05:18

标签: javascript reactjs canvas html5-canvas

我刚刚将react-color模块添加到此画布页面。当用户选择颜色时,strokeStyle会正确地更改为该颜色,但它也会重置整个画布,使其成为空白。有没有办法改变这一点,只是颜色本身改变但画布不重置?我尝试添加shouldComponentUpdate,但只是这样才使颜色完全没有变化。

import React from 'react';
import { Layer, Stage, Image } from 'react-konva';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { GithubPicker } from 'react-color';

class Canvas extends React.Component {
  constructor(props) {
    super(props);
    this.canvasEvent = this.canvasEvent.bind(this);
    this.props.socket.on('canvas_update', data => {
      const idToUpdate = data.canvasId == 1 ? 'drawing2' : 'drawing1';
      const node = document.createElement('p');
      const textNode = document.createTextNode('test');
      node.appendChild(textNode);
      document.getElementById(idToUpdate).appendChild(node);
      console.log(data.canvasJSON);
      this.updateKonva(idToUpdate, data.canvasJSON);

      //this.renderDisabledKonva(undefined, data.canvasJSON, data.canvasId);
    });
    this.state = {
      background: '#fff',
    };
  }

  canvasEvent(canvasJSON) {
    this.props.socket.emit('canvas_event', { roomId: this.props.roomId, canvasJSON: canvasJSON, canvasId: this.props.canvasId }, () => {});
  }

  handleChangeComplete = (color) => {
    this.setState({ background: color.hex });
  };

  renderTools() {
    return (
      <div className="tool">
        Tool:
        <select id="tool">
          <option value="brush">Brush</option>
          <option value="eraser">Eraser</option>
        </select>
      </div>
    );
  }

  renderKonva(container) {
    const that = this;
    const width = 400; // window.innerWidth;
    const height = 400; // window.innerHeight - 25;
    // first we need Konva core things: stage and layer
    const stage = new Konva.Stage({
      container: `drawing${this.props.canvasId}`,
      width: width,
      height: height
    });
    const layer = new Konva.Layer({});
    stage.add(layer);
    // then we are going to draw into special canvas element
    const canvas = document.createElement('canvas');
    canvas.width = stage.width() / 1.5;
    canvas.height = stage.height() / 1.5;
    // created canvas we can add to layer as 'Konva.Image' element
    let image = new Konva.Image({
      image: canvas,
      x: stage.width() / 6,
      y: stage.height() / 6,
      stroke: '#05AFF2',
      shadowBlur: 5,
    });
    layer.add(image);
    stage.draw();
    // Now we need to get access to context element
    const context = canvas.getContext('2d');
    context.strokeStyle = this.state.background;
    context.lineJoin = 'round';
    context.lineWidth = 5;
    let isPaint = false;
    let lastPointerPosition;
    let mode = 'brush';
    // now we need to bind some events
    // we need to start drawing on mousedown
    // and stop drawing on mouseup
    stage.on('contentMousedown.proto', () => {
      isPaint = true;
      lastPointerPosition = stage.getPointerPosition();
    });
    stage.on('contentMouseup.proto', () => {
      isPaint = false;
    });
    // and core function - drawing
    stage.on('contentMousemove.proto', () => {
      if (!isPaint) {
        return;
      }
      if (mode === 'brush') {
        context.lineWidth = 5;
        context.globalCompositeOperation = 'source-over';
      }
      if (mode === 'eraser') {
        context.lineWidth = 15;
        context.globalCompositeOperation = 'destination-out';
      }
      context.beginPath();
      let localPos = {
        x: lastPointerPosition.x - image.x(),
        y: lastPointerPosition.y - image.y(),
      };
      context.moveTo(localPos.x, localPos.y);
      const pos = stage.getPointerPosition();
      localPos = {
        x: pos.x - image.x(),
        y: pos.y - image.y(),
      };
      context.lineTo(localPos.x, localPos.y);
      context.closePath();
      context.stroke();
      lastPointerPosition = pos;
      layer.draw();

      const dataURL = stage.toDataURL();
      // window.open(dataURL);
      that.canvasEvent(dataURL);
      // that.canvasEvent(stage.toJSON());
      // console.log(layer);
    });
    const select = document.getElementById('tool');
    select.addEventListener('change', () => {
      mode = select.value;
    });
  }

  render() {
    return (
      <div className="container">
        {this.renderTools()}
        <GithubPicker onChangeComplete={this.handleChangeComplete} color={this.state.background} />
        <div id={'drawing' + this.props.canvasId} ref={ref => this.renderKonva(ref)} />
      </div>
    );
  }
}

const mapStateToProps = state => ({
  isAuthenticated: state.authReducer.isAuthenticated,
  user: state.authReducer,
  roomId: state.roomReducer.currentUserRoom,
});

const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(Canvas);

2 个答案:

答案 0 :(得分:1)

我可以想到几个方法来解决这个问题:

  1. 如果您想要保留任何类型的信息,只需将其添加到州。在重新渲染期间,只需参考状态以确保事物仍然呈现为原样。
  2. 将细分分解为更小的组件并将shouldComponentUpdate添加到它们中。在您拥有所需的精细控制之前,请继续解决问题。请记住,此函数还将下一个props和next状态作为参数传递,因此您可以观察这些属性,只有在与组件相关的内容发生更改时才会呈现,否则返回false。
  3. 注意:如果你正在使用外部库,react-color,并且你试图阻止它的某些组件被渲染,但是没有访问它,你可以在它周围放一个包装器组件纯粹是为了控制渲染条件。

    我希望这有帮助!

答案 1 :(得分:0)

虽然我没有办法只进行部分重新加载,但我确实为此做了一个解决方法:

import React from 'react';
import { Layer, Stage, Image } from 'react-konva';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { GithubPicker } from 'react-color';

class Canvas extends React.Component {
  constructor(props) {
    super(props);
    this.canvasEvent = this.canvasEvent.bind(this);
    this.props.socket.on('canvas_update', data => {
      const idToUpdate = data.canvasId == 1 ? 'drawing2' : 'drawing1';
      const node = document.createElement('p');
      const textNode = document.createTextNode('test');
      node.appendChild(textNode);
      document.getElementById(idToUpdate).appendChild(node);
      this.updateKonva(idToUpdate, data.canvasJSON);
      this.handleChangeComplete = this.handleChangeComplete.bind(this);
      //this.renderDisabledKonva(undefined, data.canvasJSON, data.canvasId);
    });
  }

  handleChangeComplete(color) {
    newColor = color.hex;
  }

  canvasEvent(canvasJSON) {
    this.props.socket.emit('canvas_event', { roomId: this.props.roomId, canvasJSON: canvasJSON, canvasId: this.props.canvasId }, () => {});
  }

  renderTools() {
    return (
      <div className="tool">
        Tool:
        <select id="tool">
          <option value="brush">Brush</option>
          <option value="eraser">Eraser</option>
        </select>
      </div>
    );
  }

  renderKonva(container) {
    const that = this;
    const width = 400; // window.innerWidth;
    const height = 400; // window.innerHeight - 25;
    // first we need Konva core things: stage and layer
    const stage = new Konva.Stage({
      container: `drawing${this.props.canvasId}`,
      width: width,
      height: height
    });
    const layer = new Konva.Layer({});
    stage.add(layer);
    // then we are going to draw into special canvas element
    const canvas = document.createElement('canvas');
    canvas.width = stage.width() / 1.5;
    canvas.height = stage.height() / 1.5;
    // created canvas we can add to layer as 'Konva.Image' element
    let image = new Konva.Image({
      image: canvas,
      x: stage.width() / 6,
      y: stage.height() / 6,
      stroke: '#05AFF2',
      shadowBlur: 5,
    });
    layer.add(image);
    stage.draw();
    // Now we need to get access to context element
    const context = canvas.getContext('2d');
    // context.strokeStyle = this.state.background;
    context.lineJoin = 'round';
    context.lineWidth = 5;
    let isPaint = false;
    let lastPointerPosition;
    let mode = 'brush';
    // now we need to bind some events
    // we need to start drawing on mousedown
    // and stop drawing on mouseup
    stage.on('contentMousedown.proto', () => {
      isPaint = true;
      lastPointerPosition = stage.getPointerPosition();
    });
    stage.on('contentMouseup.proto', () => {
      isPaint = false;
    });
    // and core function - drawing
    stage.on('contentMousemove.proto', () => {
      if (!isPaint) {
        return;
      }
      if (mode === 'brush') {
        context.lineWidth = 5;
        context.globalCompositeOperation = 'source-over';
      }
      if (mode === 'eraser') {
        context.lineWidth = 15;
        context.globalCompositeOperation = 'destination-out';
      }
      context.beginPath();
      let localPos = {
        x: lastPointerPosition.x - image.x(),
        y: lastPointerPosition.y - image.y(),
      };
      context.moveTo(localPos.x, localPos.y);
      const pos = stage.getPointerPosition();
      localPos = {
        x: pos.x - image.x(),
        y: pos.y - image.y(),
      };
      context.lineTo(localPos.x, localPos.y);
      context.closePath();
      context.stroke();
      lastPointerPosition = pos;
      layer.draw();
      context.strokeStyle = newColor;
      const dataURL = stage.toDataURL();
      // window.open(dataURL);
      that.canvasEvent(dataURL);
      // that.canvasEvent(stage.toJSON());
      // console.log(layer);
    });
    const select = document.getElementById('tool');
    select.addEventListener('change', () => {
      mode = select.value;
    });

  }

  render() {
    return (
      <div className="container">
        {this.renderTools()}
        <GithubPicker onChangeComplete={this.handleChangeComplete} />
        <div id={'drawing' + this.props.canvasId} ref={ref => this.renderKonva(ref)} />
      </div>
    );
  }
}

let newColor = '#fff';

const mapStateToProps = state => ({
  isAuthenticated: state.authReducer.isAuthenticated,
  user: state.authReducer,
  roomId: state.roomReducer.currentUserRoom,
});

const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(Canvas);