LoginSignup
59
58

More than 5 years have passed since last update.

React.js で Canvas Element を使う

Last updated at Posted at 2016-07-28

以下のような Component を作る。

import React, { Component, PropTypes } from 'react';

export default class CanvasComponent extends Component {
  componentDidMount() {
    this.updateCanvas();
  }

  componentWillReceiveProps(nextProps) {
    if (this.props !== nextProps) {
      this.updateCanvas();
    }
  }

  componentDidUpdate() {
    this.updateCanvas();
  }

  updateCanvas() {
    const { canvas } = this;
    const context = canvas.getContext('2d');
    this.props.updateCanvas(context);
  }

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

CanvasComponent.propTypes = {
  width: PropTypes.number.isRequired,
  height: PropTypes.number.isRequired,
  updateCanvas: PropTypes.func.isRequired,
};

で、呼び出し元の updateCanvas() メソッドの中で Canvas の描画処理を書く。

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

const CANVAS_WIDTH = 140;
const CANVAS_HEIGHT = 30;

export default class TestComponent extends Component {
  constructor() {
    this.state = {
      text: 'text',
    };
  }

  render() {
    const canvasProps = {
      width: CANVAS_WIDTH,
      height: CANVAS_HEIGHT,
      updateCanvas: (context) => {
        context.fillStyle = 'red';
        context.font = '24px Arial';
        context.textAlign = 'left';
        context.textBaseline = 'top';
        context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
        context.fillText(this.state.text, 0, 0);
      },
    };

    return (
      <div>
        <input value={this.state.text} onChange={elm => this.setState({text: elm.target.value})} />
        <CanvasComponent {...canvasProps} />
      </div>
    );
  }
}

つまり、CanvasComponent のなかで更新があるか、Props に更新があった時に Canvas の描画処理を走らせれば良い。

なお、この手法は Canvas-Element に限った話ではなく、例えば jQuery-Plugin でも下記のようにすれば React で扱うことができる。

import React, { Component, PropTypes } from 'react';
import jQuery from 'jquery';
import 'fullcalendar';

export default class FullCalendar extends Component {
  componentDidMount() {
    const { calendar } = this.refs;
    jQuery(calendar).fullCalendar({
      timezone: 'JST',
      timeFormat: 'YYYY/M/D H:mm',
      header: {
        left: 'today prev next',
        center: 'title',
        right: 'month agendaWeek agendaDay',
      },
      firstDay: 1,
      eventClick: event => {
        if (typeof this.props.onClickEvent == 'function') {
          this.props.onClickEvent(event);
        }
      },
      dayClick: date => {
        jQuery(calendar).fullCalendar('changeView', 'agendaDay');
        jQuery(calendar).fullCalendar('gotoDate', date);
      },
    });
  }

  componentWillReceiveProps(nextProps) {
    const { calendar } = this.refs;
    if (nextProps.events !== this.props.events) {
      jQuery(calendar).fullCalendar('removeEvents');
      jQuery(calendar).fullCalendar('addEventSource', nextProps.events);
    }
  }

  componentWillUnmount() {
    const { calendar } = this.refs;
    jQuery(calendar).fullCalendar('destroy');
  }


  render() {
    return <div ref="calendar"></div>;
  }
}

FullCalendar.propTypes = {
  events: PropTypes.array,
  onClickEvent: PropTypes.func,
};
59
58
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
58