如何结交Fabric.js和Redux的朋友? [英] How to make friends Fabric.js and Redux?

查看:76
本文介绍了如何结交Fabric.js和Redux的朋友?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有可以同时使用Fabric.js和Redux的方法? Fabric.js状态应该用作存储的一部分,但它不是不可变的,并且可以通过用户画布交互来使其自身发生变化.任何的想法?谢谢.

Is there any approach to use Fabric.js and Redux together? Fabric.js state should be used as part of store, but it isn't immutable and can mutate itself by user canvas interaction. Any idea? Thanks.

推荐答案

我从我的React-Redux和Fabric.js的实现中提取了一个小例子.

I have extracted small example from my implementation of React-Redux and Fabric.js.

它的工作原理是简单地通过fabric.toObject()获取整个fabric对象,将其保存为状态并通过fabric.loadFromJSON()撤销.您可以使用 Redux DevTools 并在州内游玩.

It works by simply getting whole fabric object by fabric.toObject(), saving it into state and revoking by fabric.loadFromJSON(). You can play around by using Redux DevTools and traveling through the state.

在任何情况下,都可以使用jsfiddle: https://jsfiddle.net/radomeer/74t5y1r0/

For any case, there is also jsfiddle available: https://jsfiddle.net/radomeer/74t5y1r0/

// don't be scared, just some initial objects to play with (fabric's serialized JSON)
const initialState = {
   canvasObject: {
      "objects": [{
         "type": "circle",
         "originX": "center",
         "originY": "center",
         "left": 50,
         "top": 50,
         "width": 100,
         "height": 100,
         "fill": "#FF00FF",
         "stroke": null,
         "strokeWidth": 1,
         "strokeDashArray": null,
         "strokeLineCap": "butt",
         "strokeLineJoin": "miter",
         "strokeMiterLimit": 10,
         "scaleX": 1,
         "scaleY": 1,
         "angle": 0,
         "flipX": false,
         "flipY": false,
         "opacity": 1,
         "shadow": null,
         "visible": true,
         "clipTo": null,
         "backgroundColor": "",
         "fillRule": "nonzero",
         "globalCompositeOperation": "source-over",
         "transformMatrix": null,
         "radius": 50,
         "startAngle": 0,
         "endAngle": 6.283185307179586
      }, {
         "type": "rect",
         "originX": "center",
         "originY": "center",
         "left": 126,
         "top": 210,
         "width": 100,
         "height": 100,
         "fill": "#FF0000",
         "stroke": null,
         "strokeWidth": 1,
         "strokeDashArray": null,
         "strokeLineCap": "butt",
         "strokeLineJoin": "miter",
         "strokeMiterLimit": 10,
         "scaleX": 1,
         "scaleY": 1,
         "angle": 0,
         "flipX": false,
         "flipY": false,
         "opacity": 1,
         "shadow": null,
         "visible": true,
         "clipTo": null,
         "backgroundColor": "",
         "fillRule": "nonzero",
         "globalCompositeOperation": "source-over",
         "transformMatrix": null,
         "radius": 50,
         "startAngle": 0,
         "endAngle": 6.283185307179586
      }, {
         "type": "triangle",
         "originX": "center",
         "originY": "center",
         "left": 250,
         "top": 100,
         "width": 100,
         "height": 100,
         "fill": "#00F00F",
         "stroke": null,
         "strokeWidth": 1,
         "strokeDashArray": null,
         "strokeLineCap": "butt",
         "strokeLineJoin": "miter",
         "strokeMiterLimit": 10,
         "scaleX": 1,
         "scaleY": 1,
         "angle": 0,
         "flipX": false,
         "flipY": false,
         "opacity": 1,
         "shadow": null,
         "visible": true,
         "clipTo": null,
         "backgroundColor": "",
         "fillRule": "nonzero",
         "globalCompositeOperation": "source-over",
         "transformMatrix": null,
         "radius": 50,
         "startAngle": 0,
         "endAngle": 6.283185307179586
      }],
      "background": ""
   }
};
// Redux part
const canvasObjectReducer = function(state = initialState, action) {
   switch (action.type) {
      case "OBJECTS_CANVAS_CHANGE":
         return Object.assign({}, state, {
            canvasObject: action.payload.canvasObject,
            selectedObject: action.payload.selectedObject
         });
      default:
         return state
   }
   return state;
}
// standard react-redux boilerplate
const reducers = Redux.combineReducers({
   canvasObjectState: canvasObjectReducer
});
const { createStore } = Redux;
const store = createStore(reducers, window.devToolsExtension && window.devToolsExtension());

const { Provider } = ReactRedux;
const { Component } = React;
const MyProvider = React.createClass({
   render: function() {
      return ( 
			<div>
				<Provider store={store}>
					<FabricCanvasReduxed/>
				</Provider>
			</div>
      );
   }
});

// Fabric part
var fabricCanvas = new fabric.Canvas();

// class which takes care about instantiating fabric and passing state to component with actual canvas
const FabricCanvas = React.createClass({
   componentDidMount() {
			// we need to get canvas element by ref to initialize fabric
         var el = this.refs.canvasContainer.refs.objectsCanvas;
         fabricCanvas.initialize(el, {
            height: 400,
            width: 400,
         });
			// initial call to load objects in store and render canvas
         this.refs.canvasContainer.loadAndRender();
			
         fabricCanvas.on('mouse:up', () => {
            store.dispatch({
               type: 'OBJECTS_CANVAS_CHANGE',
               payload: {
						// send complete fabric canvas object to store
                  canvasObject: fabricCanvas.toObject(),
						// also keep lastly active (selected) object
                  selectedObject: fabricCanvas.getObjects().indexOf(fabricCanvas.getActiveObject())
               }
            });
            this.refs.canvasContainer.loadAndRender();
         });
      },
      render: function() {
         return (
				<div>
					{/* send store and fabricInstance viac refs (maybe not the cleanest way, but I was not able to create global instance of fabric due to use of ES6 modules) */}
            	<CanvasContainer ref="canvasContainer" canvasObjectState={this.props.objects} fabricInstance={fabricCanvas}/>
				</div>
         )
      }
});
const mapStateToProps = function(store) {
   return {
      objects: store.canvasObjectState
   };
};

// we can not use export default on jsfiddle so we need react class with mapped state in separate constant 
const FabricCanvasReduxed = ReactRedux.connect(mapStateToProps)(FabricCanvas);

const CanvasContainer = React.createClass({
   loadAndRender: function() {
      var fabricCanvas = this.props.fabricInstance;
		fabricCanvas.loadFromJSON(this.props.canvasObjectState.canvasObject);
		fabricCanvas.renderAll();
		// if there is any previously active object, we need to re-set it after rendering canvas
		var selectedObject = this.props.canvasObjectState.selectedObject;
		if (selectedObject > -1) {
			fabricCanvas.setActiveObject(fabricCanvas.getObjects()[this.props.canvasObjectState.selectedObject]);
		}

   },
   render: function() {
      this.loadAndRender();
      return ( 
			<canvas ref="objectsCanvas">
         </canvas>
      );
   }
});

var App = React.createClass({
         render: function() {
            return ( 
					<div>
	               <MyProvider/>
               </div>
				);
			}
	});
	
ReactDOM.render( <App/>, document.getElementById('container'));

<!-- 
	Please use Redux DevTools for Chrome or Firefox to see the store changes and time traveling
	https://github.com/zalmoxisus/redux-devtools-extension
	Inspired by https://jsfiddle.net/STHayden/2pncoLb5/
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.4.5/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.js"></script>
<div id="container">
</div>

这篇关于如何结交Fabric.js和Redux的朋友?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆