Skip to content

Instantly share code, notes, and snippets.

@nummi
Forked from ryanflorence/App.js
Created December 16, 2016 00:06
Show Gist options
  • Save nummi/0b64b863b660d912fbf59e603b30f24a to your computer and use it in GitHub Desktop.
Save nummi/0b64b863b660d912fbf59e603b30f24a to your computer and use it in GitHub Desktop.
import React, { Component } from 'react'
import { Block, Flex } from 'jsxstyle'
const Row = Flex
const Col = (props) => <Flex {...props} flexDirection="column"/>
var history = {
redo_list: [],
undo_list: [],
saveState: function(canvas, list, keep_redo) {
keep_redo = keep_redo || false;
if(!keep_redo) {
this.redo_list = [];
}
(list || this.undo_list).push(canvas.toDataURL());
},
undo: function(canvas, ctx) {
this.restoreState(canvas, ctx, this.undo_list, this.redo_list);
},
redo: function(canvas, ctx) {
this.restoreState(canvas, ctx, this.redo_list, this.undo_list);
},
restoreState: function(canvas, ctx, pop, push) {
if (pop.length) {
this.saveState(canvas, push, true);
var restore_state = pop.pop();
var img = new Image()
img.src = restore_state
img.onload = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
}
}
}
}
const colors = [
'black',
'hsl(10, 70%, 50%)',
'hsl(70, 70%, 50%)',
'hsl(130, 70%, 50%)',
'hsl(200, 70%, 50%)',
'hsl(260, 70%, 50%)',
'hsl(320, 70%, 50%)'
]
class App extends Component {
state = {
canvasHeight: 0,
canvasWidth: 0,
canvasLeft: 0,
canvasTop: 0,
previousMouseX: 0,
previousMouseY: 0,
mouseX: 0,
mouseY: 0,
mousedown: false,
tooltype: 'draw',
colorIndex: 0
}
componentDidMount() {
this.ctx = this.canvasNode.getContext('2d')
// prevent zoom
document.addEventListener('gesturestart', function (e) {
e.preventDefault();
});
window.addEventListener('orientationchange', this.setCanvasSize)
this.setCanvasSize()
}
setCanvasSize = () => {
const { left, top } = this.canvasNode.getBoundingClientRect()
const { height, width } = this.el.getBoundingClientRect()
this.setState({
canvasHeight: height,
canvasWidth: width,
canvasLeft: left,
canvasTop: top
})
}
handleMouseDown = (e) => {
history.saveState(this.canvasNode)
const { canvasLeft, canvasTop } = this.state
const x = parseInt(e.touches[0].clientX-canvasLeft, 10)
const y = parseInt(e.touches[0].clientY-canvasTop, 10)
this.setState({
mouseY: y,
mouseX: x,
previousMouseY: y,
previousMouseX: x,
mousedown: true
})
}
handleMouseUp = () => {
if (
this.state.mouseY === this.state.previousMouseY &&
this.state.mouseX === this.state.previousMouseX
) {
const { ctx } = this
ctx.beginPath()
ctx.arc(this.state.mouseX, this.state.mouseY, 3, 0, Math.PI*2, true)
ctx.closePath()
ctx.fill()
}
this.setState({
mousedown: false
})
}
handleMouseMove = (e) => {
e.preventDefault()
const { colorIndex, canvasLeft, canvasTop, mousedown, tooltype, previousMouseX, previousMouseY } = this.state
const { ctx } = this
const mouseX = parseInt(e.touches[0].clientX-canvasLeft, 10)
const mouseY = parseInt(e.touches[0].clientY-canvasTop, 10)
if (mousedown) {
ctx.beginPath();
if (tooltype === 'draw') {
ctx.globalCompositeOperation = 'source-over'
ctx.strokeStyle = colors[colorIndex]
ctx.lineWidth = e.touches[0].force ? e.touches[0].force*25 : 3
} else {
ctx.globalCompositeOperation = 'destination-out'
ctx.lineWidth = 10
}
ctx.moveTo(previousMouseX, previousMouseY)
ctx.lineTo(mouseX, mouseY)
ctx.lineJoin = ctx.lineCap = 'round'
ctx.stroke()
}
this.setState({
previousMouseX: mouseX,
previousMouseY: mouseY
})
}
toggleToolType = () => {
this.setState({
tooltype: this.state.tooltype === 'draw' ? 'erase' : 'draw'
})
}
undo = (e) => {
e.preventDefault()
history.undo(this.canvasNode, this.ctx)
}
redo = (e) => {
e.preventDefault()
history.redo(this.canvasNode, this.ctx)
}
clear = (e) => {
e.preventDefault()
this.ctx.clearRect(0, 0, this.canvasNode.width, this.canvasNode.height)
}
changeColor = (colorIndex) => {
this.setState({ colorIndex })
}
render() {
const { colorIndex } = this.state
return (
<Col height="100%">
<Block flex="1" props={{ ref: n => this.el = n }}>
<canvas
ref={n => this.canvasNode = n}
width={this.state.canvasWidth}
height={this.state.canvasHeight}
onTouchStart={this.handleMouseDown}
onTouchMove={this.handleMouseMove}
onTouchEnd={this.handleMouseUp}
/>
</Block>
<Row background={colors[colorIndex]}>
{colors.map((color, index) => (
<Block
background={color}
flex="1"
height="50px"
position="relative"
top={index === colorIndex ? '-10px' : ''}
props={{ onTouchEnd: () => this.changeColor(index) }}
alignSelf="flex-end"
/>
))}
</Row>
<Row background="#f0f0f0">
<Button props={{ onTouchEnd: this.undo}}>Undo</Button>
<Button props={{ onTouchEnd: this.redo}}>Redo</Button>
<Button props={{ onTouchEnd: this.clear}}>Clear</Button>
</Row>
</Col>
)
}
}
const Button = (props) => (
<Block
flex="1" padding="10px" color="hsl(200, 50%, 50%)" border="none" height="100%" textAlign="center" padding="20px" activeBackgroundColor="#ccc"
{...props}
/>
)
export default App
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment