UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

200 lines (167 loc) 4.61 kB
/** @jsx jsx */ import {Component} from 'react' import PropTypes from 'prop-types' import pickBy from 'lodash/pickBy' import propTypes, {defaultProps} from './propTypes' import {jsx} from '@instructure/emotion' import generateComponentTheme from './theme' import generateStyle from './styles' import {withStyleOverrides} from '@instructure/quiz-common/util/withStyleOverrides' @withStyleOverrides(generateStyle, generateComponentTheme) export default class Canvas extends Component { static displayName = 'Canvas' static componentId = `Quizzes${this.displayName}` static propTypes = { ...propTypes, realRef: PropTypes.func, drawShape: PropTypes.func.isRequired, onMouseOut: PropTypes.func, onMouseMove: PropTypes.func, onDoubleClick: PropTypes.func, onClick: PropTypes.func, onKeyPress: PropTypes.func, onMouseDown: PropTypes.func, onMouseUp: PropTypes.func, onTouchStart: PropTypes.func, onTouchMove: PropTypes.func, onTouchEnd: PropTypes.func, styles: PropTypes.object, } static defaultProps = { realRef: null, onMouseOut: null, onMouseMove: null, onDoubleClick: null, onClick: null, onKeyPress: null, onMouseDown: null, onMouseUp: null, onTouchStart: null, onTouchMove: null, onTouchEnd: null, ...defaultProps, } state = { isDrawing: false, } constructor(props) { super(props) // these handlers shouldn't change, so set them once per initialization this.eventHandlers = { onMouseOut: this.onMouseOut, ...pickBy(props, (prop, propName) => prop !== null && propName.startsWith('on')), } } componentDidMount() { if (this.props.realRef) { this.props.realRef(this) } this.drawShape(this.props.coordinates) } componentDidUpdate() { this.drawShape(this.props.coordinates) if (this.isDrawing()) { this.canvasElement.focus() } } // =========== // HELPERS // =========== isDrawing() { return this.state.isDrawing } getInitialCoordinate() { return this.props.coordinates[0] } getLastCoordinate() { return this.props.coordinates[this.props.coordinates.length - 1] } getCurrentCoordinates(e) { const x = e.nativeEvent.offsetX const y = e.nativeEvent.offsetY return {x, y} } convertTouchCoordinates(touchEvent) { const rect = this.canvasElement.getBoundingClientRect() const x = touchEvent.changedTouches[0].pageX - rect.left const y = touchEvent.changedTouches[0].pageY - rect.top const evt = { nativeEvent: { offsetX: x, offsetY: y, }, } return evt } focus() { this.canvasElement.focus() } refCanvas = node => { this.canvasElement = node } // =========== // ACTIONS // =========== drawShape = coordinates => { this.clearCanvas() this.props.drawShape(coordinates) } clearCanvas() { const ctx = this.canvasElement.getContext('2d') ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height) } setStrokeStyle(color = this.props.styles.shapeColor) { const ctx = this.canvasElement.getContext('2d') ctx.lineWidth = 2 ctx.strokeStyle = color } setFillStyle(color = this.props.styles.shapeColor) { const ctx = this.canvasElement.getContext('2d') ctx.globalAlpha = 0.5 ctx.fillStyle = color } startDrawing(e) { const initialCoordinate = this.getCurrentCoordinates(e) this.props.handleSetCoordinates([initialCoordinate], true) this.setState({ isDrawing: true, }) } stopDrawing(coordinates) { this.props.handleSetCoordinates(coordinates, false) this.setState({ isDrawing: false, }) } // =========== // HANDLERS // =========== onMouseOut = e => { // the mouse out event is handled by default as a click/release on the exiting canvas border this.props.onMouseOut // eslint-disable-line no-unused-expressions ? this.props.onMouseOut(e) : e => {} } // =========== // RENDER // =========== renderCanvas() { if (this.props.readOnly) { return <canvas height={this.props.height} ref={this.refCanvas} width={this.props.width} /> } return ( <canvas css={this.props.styles.canvas} height={this.props.height} ref={this.refCanvas} tabIndex="0" width={this.props.width} onBlur={this.onMouseOut} {...this.eventHandlers} /> ) } render() { return <div css={this.props.styles.canvasWrapper}>{this.renderCanvas()}</div> } }