-
Notifications
You must be signed in to change notification settings - Fork 50.2k
Description
Hi
I am not sure if this is a bug or not, probably I am doing something wrong, just want to make sure. I am trying to create a PaperJS bridge for React, based on ReactARTFiber.
I am running Ubuntu 16.04, Chrome Version 58.0.3029.96 (64-bit) and using a hacked version of React 16.0.0-alpha.12, where I export ReactFiberReconciler through react-dom. (I know about #9103, how can we import ReactFiberReconciler in the mean time?)
Anyway, below is the code for my App. App component has a changePathColor method, where I do this.setState({ strokeColor: newColor }), which triggers a re-render, but the commitUpdate function in PaperRenderer receives the old value for strokeColor.
BTW I have some other questions, what would be the best place to ask or learn more about fiber, apart from the source code?
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import invariant from 'fbjs/lib/invariant'
import emptyObject from 'fbjs/lib/emptyObject'
import { ReactFiberReconciler } from 'react-dom'
import paper from 'paper'
const COLORS = [
'red',
'black',
'green',
'orange',
'brown',
'violet',
]
const TYPES = {
LAYER: 'Layer',
PATH: 'Path',
CIRCLE: 'Circle',
GROUP: 'Group',
TOOL: 'Tool',
}
const SEGMENTS = [
// ...
]
const SEGMENTS2 = [
// ...
]
const Layer = TYPES.LAYER
const Path = TYPES.PATH
const Circle = TYPES.CIRCLE
const Group = TYPES.GROUP
const Tool = TYPES.TOOL
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
class App extends Component {
constructor(props) {
super(props)
this.state = {
strokeColor: COLORS[0],
activeTool: 'move',
width: 800,
height: 600,
zoom: 1,
circles: [
{ id: 1, center: [100,100], radius: 40, strokeColor: 'black', strokeScaling: false },
{ id: 2, center: [150,150], radius: 30, strokeColor: 'red', strokeScaling: false },
{ id: 3, center: [200,200], radius: 35, strokeColor: 'green', strokeScaling: false },
],
}
}
addCircle = () => {
const { circles, height, width } = this.state
const newCircles = [...circles, {
id: circles.length + 1,
center: [getRandomInt(0,width),getRandomInt(0,height)],
radius: getRandomInt(10,60),
strokeColor: COLORS[getRandomInt(0,COLORS.length-1)],
strokeScaling: false,
}]
this.setState({ circles: newCircles })
}
changePathColor = () => {
const { strokeColor } = this.state
const index = COLORS.indexOf(strokeColor)
const nextColor = COLORS[index+1] || COLORS[0]
this.setState({ strokeColor: nextColor }, () => { // <<<<<<<<< change strokeColor
console.log('strokeColor should be', nextColor)
})
}
onWheel = (e) => {
const { activeTool, zoom } = this.state
if (activeTool === 'move') {
const delta = e.wheelDelta || -e.deltaY
const newZoom = delta > 0 ? zoom * 1.1 : zoom / 1.1
this.setState({ zoom: newZoom })
}
}
onMoveMouseDown = (e, scope) => {
this.point = e.point
}
onMoveMouseDrag = (e) => {
// TODO this.setState({ center: ... })
e.tool.view.scrollBy(this.point.subtract(e.point))
}
onMoveMouseUp = (e) => {
this.point = null
}
onPenMouseDown = (e) => {
if (this.path) {
this.path.selected = false
}
this.path = new paper.Path({
segments: [e.point],
strokeColor: 'black',
fullySelected: true
})
}
onPenMouseDrag = (e) => {
this.path.add(e.point)
}
onPenMouseUp = (e) => {
this.path.simplify(10)
this.path.fullySelected = true
}
render() {
const { activeTool, circles, strokeColor, height, width, zoom } = this.state
const paperProps = {
activeTool,
strokeColor,
height,
width,
zoom,
onWheel: this.onWheel,
}
return (
<div>
<h1>App</h1>
<div>
<span>Tools: </span>
<button onClick={() => this.setState({ activeTool: 'move' })}>Move</button>
<button onClick={() => this.setState({ activeTool: 'pen' })}>Pen</button>
<span> | Active tool: </span>
<b>{activeTool}</b>
<span> | Color: </span>
<button onClick={this.changePathColor}>{strokeColor}</button>
<span> | </span>
<button onClick={this.addCircle}>Add Circle</button>
</div>
<Paper {...paperProps}>
<Layer>
<Path segments={SEGMENTS} strokeColor={strokeColor} strokeScaling={false} />
<Group>
<Circle center={[333,333]} radius={20} strokeColor={'black'} fillColor={'green'} strokeScaling={false} />
</Group>
</Layer>
<Layer>
<Path dashArray={[6,4]} segments={SEGMENTS2} strokeColor={strokeColor} strokeScaling={false} />
<Group>
<Circle center={[464,444]} radius={20} strokeColor={'black'} fillColor={'orange'} strokeScaling={false} />
</Group>
</Layer>
<Layer>
{circles.map(circle => <Circle key={circle.id} {...circle} />)}
</Layer>
<Tool
name={'move'}
onMouseDown={this.onMoveMouseDown}
onMouseDrag={this.onMoveMouseDrag}
onMouseUp={this.onMoveMouseUp}
/>
<Tool
name={'pen'}
onMouseDown={this.onPenMouseDown}
onMouseDrag={this.onPenMouseDrag}
onMouseUp={this.onPenMouseUp}
/>
</Paper>
</div>
)
}
}
class Paper extends Component {
componentDidMount() {
const { activeTool, children, height, width, zoom } = this.props
this._paper = new paper.PaperScope()
this._paper.setup(this._canvas)
this._paper.view.viewSize = new paper.Size(width, height)
this._paper.view.zoom = zoom
this._mountNode = PaperRenderer.createContainer(this._paper)
PaperRenderer.updateContainer(
children,
this._mountNode,
this,
)
this._paper.view.draw()
if (activeTool) {
this._paper.tools.forEach(tool => {
if (tool.name === activeTool) {
tool.activate()
}
})
}
}
componentDidUpdate(prevProps, prevState) {
const { activeTool, children, height, width, zoom } = this.props
if (width !== prevProps.width || height !== prevProps.height) {
this._paper.view.viewSize = new paper.Size(width, height)
}
if (zoom !== prevProps.zoom) {
this._paper.view.zoom = zoom
}
PaperRenderer.updateContainer(
children,
this._mountNode,
this,
)
this._paper.view.draw()
if (activeTool !== prevProps.activeTool) {
this._paper.tools.forEach(tool => {
if (tool.name === activeTool) {
tool.activate()
}
})
}
}
componentWillUnmount() {
PaperRenderer.updateContainer(
null,
this._mountNode,
this,
)
}
render() {
console.log(this.props.strokeColor);
const { height, onWheel, width } = this.props
const canvasProps = {
ref: ref => this._canvas = ref,
height,
onWheel,
width,
}
return (
<canvas {...canvasProps} />
)
}
}
function applyLayerProps(instance, props, prevProps = {}) {
// TODO
}
function applyToolProps(tool, props, prevProps = {}) {
// TODO
}
function applyGroupProps(tool, props, prevProps = {}) {
// TODO
}
function applyCircleProps(instance, props, prevProps = {}) {
if (props.center !== prevProps.center) {
instance.center = new paper.Point(props.center)
}
if (props.strokeColor !== prevProps.strokeColor) {
instance.strokeColor = props.strokeColor
}
if (props.strokeWidth !== prevProps.strokeWidth) {
instance.strokeWidth = props.strokeWidth
}
if (props.fillColor !== prevProps.fillColor) {
instance.fillColor = props.fillColor
}
}
function applyPathProps(instance, props, prevProps = {}) {
console.log('applyPathProps', props.strokeColor) // <<<<<<<< strokeColor does not change
if (props.strokeColor !== prevProps.strokeColor) {
instance.strokeColor = props.strokeColor
}
if (props.strokeWidth !== prevProps.strokeWidth) {
instance.strokeWidth = props.strokeWidth
}
}
const PaperRenderer = ReactFiberReconciler({
appendChild(parentInstance, child) {
if (child.parentNode === parentInstance) {
child.remove()
}
if (
child instanceof paper.Path &&
(
parentInstance instanceof paper.Layer ||
parentInstance instanceof paper.Group
)
) {
child.addTo(parentInstance)
}
},
appendInitialChild(parentInstance, child) {
if (typeof child === 'string') {
// Noop for string children of Text (eg <Text>{'foo'}{'bar'}</Text>)
invariant(false, 'Text children should already be flattened.')
return
}
if (
child instanceof paper.Path &&
(
parentInstance instanceof paper.Layer ||
parentInstance instanceof paper.Group
)
) {
child.addTo(parentInstance)
}
},
commitTextUpdate(textInstance, oldText, newText) {
// Noop
},
commitMount(instance, type, newProps) {
// Noop
},
commitUpdate(instance, type, oldProps, newProps) {
console.log('commitUpdate', instance, type, newProps)
instance._applyProps(instance, newProps, oldProps)
},
createInstance(type, props, internalInstanceHandle) {
//console.log('createInstance', type, props)
const { children, ...paperProps } = props
let instance
switch (type) {
case TYPES.TOOL:
instance = new paper.Tool(paperProps)
instance._applyProps = applyToolProps
break
case TYPES.LAYER:
instance = new paper.Layer(paperProps)
instance._applyProps = applyLayerProps
break
case TYPES.GROUP:
instance = new paper.Group(paperProps)
instance._applyProps = applyGroupProps
break
case TYPES.PATH:
instance = new paper.Path(paperProps)
instance._applyProps = applyPathProps
break
case TYPES.CIRCLE:
instance = new paper.Path.Circle(paperProps)
instance._applyProps = applyCircleProps
break
}
invariant(instance, 'PaperReact does not support the type "%s"', type)
instance._applyProps(instance, props)
return instance
},
createTextInstance(text, rootContainerInstance, internalInstanceHandle) {
return text
},
finalizeInitialChildren(domElement, type, props) {
return false
},
insertBefore(parentInstance, child, beforeChild) {
invariant(
child !== beforeChild,
'PaperReact: Can not insert node before itself'
)
child.insertAbove(beforeChild)
},
prepareForCommit() {
// Noop
},
prepareUpdate(domElement, type, oldProps, newProps) {
return true
},
removeChild(parentInstance, child) {
//destroyEventListeners(child)
child.remove()
},
resetAfterCommit() {
// Noop
},
resetTextContent(domElement) {
// Noop
},
getRootHostContext() {
return emptyObject
},
getChildHostContext() {
return emptyObject
},
scheduleAnimationCallback: window.requestAnimationFrame,
scheduleDeferredCallback: window.requestIdleCallback,
shouldSetTextContent(props) {
return (
typeof props.children === 'string' ||
typeof props.children === 'number'
)
},
useSyncScheduling: true,
})
export default App