├── .gitignore ├── .npmignore ├── LICENSE ├── bili.config.js ├── changelog.md ├── dist ├── interactable.native.js ├── interactable.native.js.map ├── interactable.native.min.js ├── interactable.native.min.js.map ├── interactable.noNative.js ├── interactable.noNative.js.map ├── interactable.noNative.min.js └── interactable.noNative.min.js.map ├── native.js ├── noNative.js ├── package-lock.json ├── package.json ├── playground ├── .gitignore ├── README.md ├── config │ ├── env.js │ ├── jest │ │ ├── cssTransform.js │ │ └── fileTransform.js │ ├── paths.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpackDevServer.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── scripts │ ├── build.js │ ├── start.js │ └── test.js └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── Screen.js │ ├── assets │ ├── airport-photo.jpg │ ├── calendar-body.png │ ├── calendar-header.png │ ├── card-photo.jpg │ ├── chatheads-delete.png │ ├── chatheads-face1.jpg │ ├── chatheads-face2.jpg │ ├── icon-check.png │ ├── icon-clock.png │ ├── icon-menu.png │ ├── icon-trash.png │ ├── icon-up.png │ ├── map-bg.jpg │ └── tinder-photo.jpg │ ├── examples │ ├── AlertAreas.js │ ├── ChangePosition.js │ ├── ChatHeads.js │ ├── CollapsingHeader.js │ ├── CollapsingHeaderWithScroll.js │ ├── HandleRelayout.js │ ├── HandleTouches.js │ ├── IconDrawer.js │ ├── MoreChatHeads.js │ ├── MoreDrawers.js │ ├── SideMenu.js │ ├── SnapTo.js │ ├── SwipeableCard.js │ ├── TouchesInside.js │ └── TouchesInsideStatic.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── react-interactable │ ├── AnimatedView.js │ ├── Animator.js │ ├── Behaviors.js │ ├── InteractableView.js │ ├── Utils.js │ ├── native.js │ └── noNative.js │ ├── real-life-examples │ ├── CollapsibleCalendar.js │ ├── CollapsibleFilter.js │ ├── MapPanel.js │ ├── NotifPanel.js │ ├── NowCard.js │ ├── RealChatHeads.js │ ├── RowActions1.js │ ├── RowActions2.js │ └── TinderCard.js │ └── serviceWorker.js └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | .expo/ 4 | .vscode/ 5 | styleGuide 6 | stats.web.json 7 | stats.chrome.json 8 | 9 | # dependencies 10 | node_modules 11 | 12 | # misc 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | build 23 | 24 | .DS_Store 25 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | playground/public 2 | playground/scripts 3 | playground/src/assets 4 | playground/src/examples 5 | playground/src/real-life-examples -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Javier Marquez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bili.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | input: ['playground/src/react-interactable/native.js', 'playground/src/react-interactable/noNative.js'], 3 | moduleName: 'Interactable', 4 | formats: ['umd', 'umd-min'], 5 | global: { 6 | react: 'React', 7 | animated: 'Animated', 8 | 'prop-types': 'PropTypes', 9 | 'react-native': 'ReactNative', 10 | 'react-dom': 'ReactDom', 11 | 'react-dom/unstable-native-dependencies': 'UnstableDependencies', 12 | 'react-panresponder-web': 'PanResponder', 13 | 'animated/lib/targets/react-dom': 'Animated' 14 | }, 15 | external: ['react-native'], 16 | banner: { name: '*//* eslint-disable */\n/*!\n * react-interactable' }, 17 | filename: 'interactable.[name][suffix].js', 18 | map: true 19 | } -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## 0.6.5 2 | * Removes peer dependencies to let library get installed with latest react. 3 | * Adds a fake shim for `requestAnimationFrame` to let code compile in the server. 4 | 5 | ## v0.6.4 6 | * Waits until the drag movement has gone 5 pts before capturing it. 7 | 8 | ## v0.6.3 9 | * Stop using document directly to make it work with react-native. 10 | 11 | ## v0.6.2 12 | * Adds sourcemaps to the dist files. 13 | * The main file in the page is now pointing to the dist version. 14 | 15 | ## v0.6.1 16 | * Updates the position and display of the animated component mimic the react-native behavior 17 | * Pass styles down to the animated component 18 | * Improves the performance of spring animations 19 | 20 | ## v0.6.0 21 | * ReactDom version working ok 22 | * Added dist files using bili 23 | * PanResponder for ReactDom extracted to its own repo 24 | 25 | ## v0.5.0 26 | * Adds support for non-native apps 27 | * Playground: Fixes for mobile browsers 28 | 29 | ## v0.4.0 30 | * Fixes updating dragEnabled prop while dragging 31 | * Creates this changelog file 32 | * Playground: Alert areas example working ok 33 | * Playground: Link to docs and inspiration working ok 34 | * Playground: Fixed slides for test settings 35 | * Playground: Prevent scrolling on mobile browsers 36 | 37 | ## v0.3.0 38 | * First public version, almost everything working ok, playground included -------------------------------------------------------------------------------- /dist/interactable.native.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * *//* eslint-disable */ 3 | /*! 4 | * react-interactable v0.6.5 5 | * (c) 2019-present Javier Marquez 6 | * Released under the MIT License. 7 | */ 8 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react"),require("prop-types"),require("react-native")):"function"==typeof define&&define.amd?define(["react","prop-types","react-native"],t):e.Interactable=t(e.React,e.PropTypes,e.ReactNative)}(this,function(e,t,n){"use strict";var i="default"in e?e.default:e;function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){for(var n=0;n=a.x&&n<=r.x&&i>=a.y&&i<=r.y},findClosest:function(e,t){var n=this,i=1/0,a=null;return t.forEach(function(t){var r=n.getDistance(t,e);r1&&void 0!==arguments[1]&&arguments[1];return{x0:e.x,y0:e.y,priority:1,isTemp:t,type:"anchor"}},doFrame:function(e,t,n,i){n.vx=(e.x0-i.x)/t,n.vy=(e.y0-i.y)/t}},bounce:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"bounce",bounce:l(e.bounce,.5),minPoint:e.influence.minPoint,maxPoint:e.influence.maxPoint,priority:3,isTemp:t}},doFrame:function(e,t,n,i,a){var r=e.minPoint,o=e.maxPoint,s=e.bounce,u=i.x,h=i.y;r.x>u&&a.setTranslationX(r.x),r.y>h&&a.setTranslationY(r.y),o.xu&&c<0&&(n.vx=-c*s),r.y>h&&d<0&&(n.vy=-d*s),o.x0&&(n.vx=-c*s),o.y0&&(n.vy=-d*s)}},friction:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"friction",damping:l(e.damping,.7),influence:v.createArea(e.influenceArea||{}),priority:2,isTemp:t}},doFrame:function(e,t,n,i){if(v.isPointInArea(i,e.influence)){var a=Math.pow(e.damping,60*t);n.vx=a*n.vx,n.vy=a*n.vy}}},gravity:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"gravity",x0:l(e.x,1/0),y0:l(e.y,1/0),strength:l(e.strength,400),falloff:l(e.falloff,40),damping:l(e.damping,0),influence:v.createArea(e.influenceArea||{}),isTemp:t,priority:1}},doFrame:function(e,t,n,i){if(v.isPointInArea(i,e.influence)){var a=e.x0!==1/0?i.x-e.x0:0,r=e.y0!==1/0?i.y-e.y0:0,o=Math.sqrt(a*a+r*r);if(o){var s=e.falloff,u=-e.strength*o*Math.exp(o*o*-.5/(s*s))/n.mass,h=a/o*u,c=r/o*u;n.vx+=t*h,n.vy+=t*c}}}},spring:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"spring",x0:l(e.x,0),y0:l(e.y,0),tension:l(e.tension,300),influence:v.createArea(e.influenceArea||{}),isTemp:t,priority:1}},doFrame:function(e,t,n,i){if(v.isPointInArea(i,e.influence)){var a=i.x-e.x0;a&&(n.vx+=t*(-1*e.tension*a/n.mass));var r=i.y-e.y0;r&&(n.vy+=t*(-1*e.tension*r/n.mass))}}}};"undefined"==typeof requestAnimationFrame&&(requestAnimationFrame=function(){});var p,y,m,g,b=function(){function e(t,n,i){if(a(this,e),s(this,"behaviors",[]),s(this,"physicsObject",{vx:0,vy:0,mass:1}),s(this,"consecutiveFramesWithNoMovement",0),s(this,"screenScale",1),s(this,"lastFrameTS",0),s(this,"isRunning",!1),s(this,"ticking",!1),s(this,"isDragging",!1),this.View=t,this.animatorListener=n,!i){var r=function(){};this.debugStart=r,this.debugEnd=r}}return o(e,[{key:"doFrame",value:function(e){var t=this;this.isRunning&&(this.lastFrameTS&&this.animateFrameWithDeltaTime(.001*(e-this.lastFrameTS)),this.lastFrameTS=e,this.animatorListener.onAnimationFrame(),requestAnimationFrame(function(){return t.doFrame(Date.now())}))}},{key:"debugStart",value:function(e){!0!==this.debug&&this.debug!==e.type||(this.debugB=e,this.debugInitialV=Object.assign({},this.physicsObject))}},{key:"debugEnd",value:function(){!this.debugB||!0!==this.debug&&this.debug!==this.debugB.type||console.log("Debug ".concat(this.debugB.type),{dvx:this.physicsObject.vx-this.debugInitialV.vx,dvy:this.physicsObject.vy-this.debugInitialV.vy})}},{key:"animateFrameWithDeltaTime",value:function(e){var t=this;if(e){var n=this.physicsObject,i=this.behaviors,a=this.View,r=!1,o=a.getTranslation();i.forEach(function(i){t.debugStart(i),f[i.type].doFrame(i,e,n,o,a),t.debugEnd()});var s=0,u=n.vx,h=n.vy;Math.abs(u)>1&&(s=e*u,r=!0);var c=0;Math.abs(h)>1&&(c=e*h,r=!0),a.animate(s,c);var d=r?0:this.consecutiveFramesWithNoMovement+1;this.consecutiveFramesWithNoMovement=d,d>=10&&!this.isDragging&&(this.stopRunning(),this.animatorListener.onAnimatorPause())}}},{key:"addBehavior",value:function(e,t,n){var i=f[e];if(i){for(var a=i.create(t,n),r=this.behaviors,o=0;r.length>o&&r[o].priority<=a.priority;)++o;return r.splice(o,0,a),this.ensureRunning(),a}}},{key:"remove",value:function(e){for(var t=this.behaviors,n=t.length;n-- >0;)e(t[n])&&t.splice(n,1)}},{key:"removeBehavior",value:function(e){this.remove(function(t){return t===e})}},{key:"removeTypeBehaviors",value:function(e){this.remove(function(t){return t.type===e})}},{key:"removeTempBehaviors",value:function(){this.remove(function(e){return e.isTemp})}},{key:"getVelocity",value:function(){return{x:this.physicsObject.vx,y:this.physicsObject.vy}}},{key:"ensureRunning",value:function(){this.isRunning||this.startRunning()}},{key:"startRunning",value:function(){var e=this;this.isRunning=!0,this.lastFrameTS=0,this.consecutiveFramesWithNoMovement=0,requestAnimationFrame(function(){return e.doFrame(Date.now())})}},{key:"stopRunning",value:function(){this.removeTempBehaviors(),this.physicsObject={vx:0,vy:0,mass:this.physicsObject.mass},this.isRunning=!1}}]),e}(),x={frictionAreas:"friction",gravityPoints:"gravity",springPoints:"spring"},P="undefined"!=typeof document;return{View:(p=n.Animated,y=n.PanResponder,g=m=function(t){function n(e){var t,i,r;a(this,n),i=this,t=!(r=h(n).call(this,e))||"object"!=typeof r&&"function"!=typeof r?d(i):r,s(d(d(t)),"initialPositionSet",!1),s(d(d(t)),"isDragging",!1);var o=e.initialPosition,u=o.x,c=void 0===u?0:u,v=o.y,l=void 0===v?0:v;t.animated=new p.ValueXY({x:c,y:l}),t.animator=t.createAnimator(e),t.insideAlertAreas={},t.propAreas={alert:[],boundaries:!1},t._pr=t.createPanResponder(e),t.setPropBehaviours({},e);var f=t.getAnimated(e);return f.x.setValue(c),f.y.setValue(l),f.x._startingValue=c,f.y._startingValue=l,t.lastEnd=Object.assign({},t.initialPosition),t}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&c(e,t)}(n,e.Component),o(n,[{key:"render",value:function(){var e=this.getAnimated(),t=e.x,n=e.y,a=this.props.style,r=Object.assign({transform:[{translateX:t},{translateY:n}].concat(a.transform||[])},a),o=this.props.dragEnabled?this._pr.panHandlers:{};return i.createElement(p.View,u({style:r},o),this.props.children)}},{key:"getTranslation",value:function(){var e=this.getAnimated(),t=e.x,n=e.y;return{x:t._value+t._offset,y:n._value+n._offset}}},{key:"setTranslationX",value:function(e){var t=this.props.animatedValueX||this.animated.x;t.setValue(e-t._offset)}},{key:"setTranslationY",value:function(e){var t=this.props.animatedValueY||this.animated.y;t.setValue(e-t._offset)}},{key:"setTranslation",value:function(e,t){this.setTranslationX(e),this.setTranslationY(t)}},{key:"createAnimator",value:function(){var e=this;return new b(this,{onAnimatorPause:function(){var t=e.getTranslation(),n=t.x,i=t.y;e.lastEnd={x:Math.round(n),y:Math.round(i)},e.props.onStop(e.lastEnd)},onAnimationFrame:function(){e.reportAlertEvent(e.getTranslation())}},!1)}},{key:"animate",value:function(e,t){if(e||t){var n=this.getTranslation(),i=n.x,a=n.y;this.setTranslation(i+e,a+t)}}},{key:"getAnimated",value:function(e){var t=e||this.props,n=t.animatedValueX,i=t.animatedValueY;return{x:n||this.animated.x,y:i||this.animated.y}}},{key:"createPanResponder",value:function(){var e=this,t=this.checkResponderCapture.bind(this);return y.create({onMoveShouldSetResponderCapture:t,onMoveShouldSetPanResponderCapture:t,onPanResponderGrant:function(t,n){var i=n.x0,a=n.y0;e._captured=!0,e.startDrag({x:i,y:a})},onPanResponderMove:function(t,n){e.onDragging(n)},onPanResponderRelease:function(){e._captured=!1,e.endDrag()}})}},{key:"checkResponderCapture",value:function(e,t){return this._captured||Math.abs(t.dx)>5||Math.abs(t.dy)>5}},{key:"reportAlertEvent",value:function(e){var t=this.insideAlertAreas,n=this.props.onAlert;this.propAreas.alert.forEach(function(i){var a=i.influence,r=i.id;a&&r&&(v.isPointInArea(e,a)?t[r]||(n({id:r,value:"enter"}),t[r]=1):t[r]&&(n({id:r,value:"leave"}),t[r]=0))})}},{key:"startDrag",value:function(e){var t=this.getAnimated(),n=t.x,i=t.y,a={x:n._value,y:i._value};if(n.setOffset(a.x),i.setOffset(a.y),n.setValue(0),i.setValue(0),this.lastEnd=a,this.dragBoundaries=this.propAreas.boundaries?this.propAreas.boundaries.influence:{},this.props.onDrag({state:"start",x:a.x,y:a.y}),this.dragStartLocation={x:e.x,y:e.y},this.animator.removeTempBehaviors(),this.animator.isDragging=!0,this.animator.vx=0,this.animator.vy=0,this.addTempDragBehavior(this.props.dragWithSpring),P){var r=document.body.style;this.userSelectCache=r.userSelect,r.userSelect="none"}}},{key:"onDragging",value:function(e){var t=e.dx,n=e.dy;if(!this.animator.isDragging)return!1;if(!this.props.dragEnabled)return this.endDrag();var i=this.lastEnd,a=t+i.x,r=n+i.y,o=this.dragBoundaries,s=o.minPoint,u=o.maxPoint;this.props.verticalOnly||(s&&(s.x>a&&(a=s.x),u.xr&&(r=s.y),u.y=n.length)){this.animator.removeTempBehaviors(),this.dragBehavior=null;var i=n[t];this.addTempSnapToPointBehavior(i),this.addTempBoundaries()}}},{key:"addTempBoundaries",value:function(){var e=this.propAreas.boundaries;e&&this.animator.addBehavior("bounce",e,!0)}},{key:"changePosition",value:function(e){this.dragBehavior||(this.setTranslation(e.x,e.y),this.endDrag())}},{key:"componentDidUpdate",value:function(e){this.setPropBehaviours(e,this.props)}},{key:"setPropBehaviours",value:function(e,t){var n=this;if(Object.keys(x).forEach(function(i){e[i]!==t[i]&&(n.animator.removeTypeBehaviors(x[i]),n.addTypeBehaviors(x[i],t[i]))}),e.alertAreas!==t.alertAreas){var i=[];t.alertAreas.forEach(function(e){i.push({id:e.id,influence:v.createArea(e.influenceArea)})}),this.propAreas.alert=i}if(e.boundaries!==t.boundaries)if(this.animator.removeBehavior(this.oldBoundariesBehavior),t.boundaries){var a={bounce:t.boundaries.bounce||0,influence:v.createArea(t.boundaries)};this.propAreas.boundaries=a,this.oldBoundariesBehavior=this.animator.addBehavior("bounce",a)}else this.propAreas.boundaries=!1;!this.props.dragEnabled&&e.dragEnabled&&this.dragBehavior&&this.endDrag()}},{key:"addTypeBehaviors",value:function(e,t,n){var i=this;t.forEach(function(t){return i.addBehavior(e,t,n)})}},{key:"addBehavior",value:function(e,t,n){if(this.animator.addBehavior(e,t,n),t.damping&&"friction"!==e){var i=this.animator.addBehavior("friction",t,n);"gravity"!==e||t.influenceArea||(i.influence=v.createAreaFromRadius(1.4*(t.falloff||40),t))}}}]),n}(),s(m,"propTypes",{snapPoints:t.array,frictionAreas:t.array,alertAreas:t.array,gravityPoints:t.array,horizontalOnly:t.bool,verticalOnly:t.bool,dragWithSpring:t.object,dragEnabled:t.bool,animatedValueX:t.instanceOf(p.Value),animatedValueY:t.instanceOf(p.Value),onSnap:t.func,onSnapStart:t.func,onEnd:t.func,onDrag:t.func,boundaries:t.object,initialPosition:t.object,dragToss:t.number}),s(m,"defaultProps",{snapPoints:[],frictionAreas:[],alertAreas:[],gravityPoints:[],boundaries:{},initialPosition:{x:0,y:0},dragToss:.1,dragEnabled:!0,onSnap:function(){},onSnapStart:function(){},onStop:function(){},onDrag:function(){},onAlert:function(){},style:{}}),g)}}); 9 | //# sourceMappingURL=interactable.native.min.js.map 10 | -------------------------------------------------------------------------------- /dist/interactable.noNative.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * *//* eslint-disable */ 3 | /*! 4 | * react-interactable v0.6.5 5 | * (c) 2019-present Javier Marquez 6 | * Released under the MIT License. 7 | */ 8 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react"),require("prop-types"),require("animated/lib/targets/react-dom"),require("react-panresponder-web")):"function"==typeof define&&define.amd?define(["react","prop-types","animated/lib/targets/react-dom","react-panresponder-web"],t):e.Interactable=t(e.React,e.PropTypes,e.Animated,e.PanResponder)}(this,function(e,t,n,i){"use strict";var a="default"in e?e.default:e;function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){for(var n=0;n=a.x&&n<=r.x&&i>=a.y&&i<=r.y},findClosest:function(e,t){var n=this,i=1/0,a=null;return t.forEach(function(t){var r=n.getDistance(t,e);r1&&void 0!==arguments[1]&&arguments[1];return{x0:e.x,y0:e.y,priority:1,isTemp:t,type:"anchor"}},doFrame:function(e,t,n,i){n.vx=(e.x0-i.x)/t,n.vy=(e.y0-i.y)/t}},bounce:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"bounce",bounce:f(e.bounce,.5),minPoint:e.influence.minPoint,maxPoint:e.influence.maxPoint,priority:3,isTemp:t}},doFrame:function(e,t,n,i,a){var r=e.minPoint,o=e.maxPoint,s=e.bounce,u=i.x,h=i.y;r.x>u&&a.setTranslationX(r.x),r.y>h&&a.setTranslationY(r.y),o.xu&&c<0&&(n.vx=-c*s),r.y>h&&d<0&&(n.vy=-d*s),o.x0&&(n.vx=-c*s),o.y0&&(n.vy=-d*s)}},friction:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"friction",damping:f(e.damping,.7),influence:v.createArea(e.influenceArea||{}),priority:2,isTemp:t}},doFrame:function(e,t,n,i){if(v.isPointInArea(i,e.influence)){var a=Math.pow(e.damping,60*t);n.vx=a*n.vx,n.vy=a*n.vy}}},gravity:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"gravity",x0:f(e.x,1/0),y0:f(e.y,1/0),strength:f(e.strength,400),falloff:f(e.falloff,40),damping:f(e.damping,0),influence:v.createArea(e.influenceArea||{}),isTemp:t,priority:1}},doFrame:function(e,t,n,i){if(v.isPointInArea(i,e.influence)){var a=e.x0!==1/0?i.x-e.x0:0,r=e.y0!==1/0?i.y-e.y0:0,o=Math.sqrt(a*a+r*r);if(o){var s=e.falloff,u=-e.strength*o*Math.exp(o*o*-.5/(s*s))/n.mass,h=a/o*u,c=r/o*u;n.vx+=t*h,n.vy+=t*c}}}},spring:{create:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return{type:"spring",x0:f(e.x,0),y0:f(e.y,0),tension:f(e.tension,300),influence:v.createArea(e.influenceArea||{}),isTemp:t,priority:1}},doFrame:function(e,t,n,i){if(v.isPointInArea(i,e.influence)){var a=i.x-e.x0;a&&(n.vx+=t*(-1*e.tension*a/n.mass));var r=i.y-e.y0;r&&(n.vy+=t*(-1*e.tension*r/n.mass))}}}};"undefined"==typeof requestAnimationFrame&&(requestAnimationFrame=function(){});var y,m,g,b,x=function(){function e(t,n,i){if(r(this,e),u(this,"behaviors",[]),u(this,"physicsObject",{vx:0,vy:0,mass:1}),u(this,"consecutiveFramesWithNoMovement",0),u(this,"screenScale",1),u(this,"lastFrameTS",0),u(this,"isRunning",!1),u(this,"ticking",!1),u(this,"isDragging",!1),this.View=t,this.animatorListener=n,!i){var a=function(){};this.debugStart=a,this.debugEnd=a}}return s(e,[{key:"doFrame",value:function(e){var t=this;this.isRunning&&(this.lastFrameTS&&this.animateFrameWithDeltaTime(.001*(e-this.lastFrameTS)),this.lastFrameTS=e,this.animatorListener.onAnimationFrame(),requestAnimationFrame(function(){return t.doFrame(Date.now())}))}},{key:"debugStart",value:function(e){!0!==this.debug&&this.debug!==e.type||(this.debugB=e,this.debugInitialV=Object.assign({},this.physicsObject))}},{key:"debugEnd",value:function(){!this.debugB||!0!==this.debug&&this.debug!==this.debugB.type||console.log("Debug ".concat(this.debugB.type),{dvx:this.physicsObject.vx-this.debugInitialV.vx,dvy:this.physicsObject.vy-this.debugInitialV.vy})}},{key:"animateFrameWithDeltaTime",value:function(e){var t=this;if(e){var n=this.physicsObject,i=this.behaviors,a=this.View,r=!1,o=a.getTranslation();i.forEach(function(i){t.debugStart(i),p[i.type].doFrame(i,e,n,o,a),t.debugEnd()});var s=0,u=n.vx,h=n.vy;Math.abs(u)>1&&(s=e*u,r=!0);var c=0;Math.abs(h)>1&&(c=e*h,r=!0),a.animate(s,c);var d=r?0:this.consecutiveFramesWithNoMovement+1;this.consecutiveFramesWithNoMovement=d,d>=10&&!this.isDragging&&(this.stopRunning(),this.animatorListener.onAnimatorPause())}}},{key:"addBehavior",value:function(e,t,n){var i=p[e];if(i){for(var a=i.create(t,n),r=this.behaviors,o=0;r.length>o&&r[o].priority<=a.priority;)++o;return r.splice(o,0,a),this.ensureRunning(),a}}},{key:"remove",value:function(e){for(var t=this.behaviors,n=t.length;n-- >0;)e(t[n])&&t.splice(n,1)}},{key:"removeBehavior",value:function(e){this.remove(function(t){return t===e})}},{key:"removeTypeBehaviors",value:function(e){this.remove(function(t){return t.type===e})}},{key:"removeTempBehaviors",value:function(){this.remove(function(e){return e.isTemp})}},{key:"getVelocity",value:function(){return{x:this.physicsObject.vx,y:this.physicsObject.vy}}},{key:"ensureRunning",value:function(){this.isRunning||this.startRunning()}},{key:"startRunning",value:function(){var e=this;this.isRunning=!0,this.lastFrameTS=0,this.consecutiveFramesWithNoMovement=0,requestAnimationFrame(function(){return e.doFrame(Date.now())})}},{key:"stopRunning",value:function(){this.removeTempBehaviors(),this.physicsObject={vx:0,vy:0,mass:this.physicsObject.mass},this.isRunning=!1}}]),e}(),P={frictionAreas:"friction",gravityPoints:"gravity",springPoints:"spring"},A="undefined"!=typeof document;return n.View=function(e){var t=e.style||{},i=Object.assign({position:"relative",display:"flex"},t);return a.createElement(n.div,h({},e,{style:i}),e.children)},{View:(y=n,m=i,b=g=function(t){function n(e){var t,i,a;r(this,n),i=this,t=!(a=c(n).call(this,e))||"object"!=typeof a&&"function"!=typeof a?l(i):a,u(l(l(t)),"initialPositionSet",!1),u(l(l(t)),"isDragging",!1);var o=e.initialPosition,s=o.x,h=void 0===s?0:s,d=o.y,v=void 0===d?0:d;t.animated=new y.ValueXY({x:h,y:v}),t.animator=t.createAnimator(e),t.insideAlertAreas={},t.propAreas={alert:[],boundaries:!1},t._pr=t.createPanResponder(e),t.setPropBehaviours({},e);var f=t.getAnimated(e);return f.x.setValue(h),f.y.setValue(v),f.x._startingValue=h,f.y._startingValue=v,t.lastEnd=Object.assign({},t.initialPosition),t}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&d(e,t)}(n,e.Component),s(n,[{key:"render",value:function(){var e=this.getAnimated(),t=e.x,n=e.y,i=this.props.style,r=Object.assign({transform:[{translateX:t},{translateY:n}].concat(i.transform||[])},i),o=this.props.dragEnabled?this._pr.panHandlers:{};return a.createElement(y.View,h({style:r},o),this.props.children)}},{key:"getTranslation",value:function(){var e=this.getAnimated(),t=e.x,n=e.y;return{x:t._value+t._offset,y:n._value+n._offset}}},{key:"setTranslationX",value:function(e){var t=this.props.animatedValueX||this.animated.x;t.setValue(e-t._offset)}},{key:"setTranslationY",value:function(e){var t=this.props.animatedValueY||this.animated.y;t.setValue(e-t._offset)}},{key:"setTranslation",value:function(e,t){this.setTranslationX(e),this.setTranslationY(t)}},{key:"createAnimator",value:function(){var e=this;return new x(this,{onAnimatorPause:function(){var t=e.getTranslation(),n=t.x,i=t.y;e.lastEnd={x:Math.round(n),y:Math.round(i)},e.props.onStop(e.lastEnd)},onAnimationFrame:function(){e.reportAlertEvent(e.getTranslation())}},!1)}},{key:"animate",value:function(e,t){if(e||t){var n=this.getTranslation(),i=n.x,a=n.y;this.setTranslation(i+e,a+t)}}},{key:"getAnimated",value:function(e){var t=e||this.props,n=t.animatedValueX,i=t.animatedValueY;return{x:n||this.animated.x,y:i||this.animated.y}}},{key:"createPanResponder",value:function(){var e=this,t=this.checkResponderCapture.bind(this);return m.create({onMoveShouldSetResponderCapture:t,onMoveShouldSetPanResponderCapture:t,onPanResponderGrant:function(t,n){var i=n.x0,a=n.y0;e._captured=!0,e.startDrag({x:i,y:a})},onPanResponderMove:function(t,n){e.onDragging(n)},onPanResponderRelease:function(){e._captured=!1,e.endDrag()}})}},{key:"checkResponderCapture",value:function(e,t){return this._captured||Math.abs(t.dx)>5||Math.abs(t.dy)>5}},{key:"reportAlertEvent",value:function(e){var t=this.insideAlertAreas,n=this.props.onAlert;this.propAreas.alert.forEach(function(i){var a=i.influence,r=i.id;a&&r&&(v.isPointInArea(e,a)?t[r]||(n({id:r,value:"enter"}),t[r]=1):t[r]&&(n({id:r,value:"leave"}),t[r]=0))})}},{key:"startDrag",value:function(e){var t=this.getAnimated(),n=t.x,i=t.y,a={x:n._value,y:i._value};if(n.setOffset(a.x),i.setOffset(a.y),n.setValue(0),i.setValue(0),this.lastEnd=a,this.dragBoundaries=this.propAreas.boundaries?this.propAreas.boundaries.influence:{},this.props.onDrag({state:"start",x:a.x,y:a.y}),this.dragStartLocation={x:e.x,y:e.y},this.animator.removeTempBehaviors(),this.animator.isDragging=!0,this.animator.vx=0,this.animator.vy=0,this.addTempDragBehavior(this.props.dragWithSpring),A){var r=document.body.style;this.userSelectCache=r.userSelect,r.userSelect="none"}}},{key:"onDragging",value:function(e){var t=e.dx,n=e.dy;if(!this.animator.isDragging)return!1;if(!this.props.dragEnabled)return this.endDrag();var i=this.lastEnd,a=t+i.x,r=n+i.y,o=this.dragBoundaries,s=o.minPoint,u=o.maxPoint;this.props.verticalOnly||(s&&(s.x>a&&(a=s.x),u.xr&&(r=s.y),u.y=n.length)){this.animator.removeTempBehaviors(),this.dragBehavior=null;var i=n[t];this.addTempSnapToPointBehavior(i),this.addTempBoundaries()}}},{key:"addTempBoundaries",value:function(){var e=this.propAreas.boundaries;e&&this.animator.addBehavior("bounce",e,!0)}},{key:"changePosition",value:function(e){this.dragBehavior||(this.setTranslation(e.x,e.y),this.endDrag())}},{key:"componentDidUpdate",value:function(e){this.setPropBehaviours(e,this.props)}},{key:"setPropBehaviours",value:function(e,t){var n=this;if(Object.keys(P).forEach(function(i){e[i]!==t[i]&&(n.animator.removeTypeBehaviors(P[i]),n.addTypeBehaviors(P[i],t[i]))}),e.alertAreas!==t.alertAreas){var i=[];t.alertAreas.forEach(function(e){i.push({id:e.id,influence:v.createArea(e.influenceArea)})}),this.propAreas.alert=i}if(e.boundaries!==t.boundaries)if(this.animator.removeBehavior(this.oldBoundariesBehavior),t.boundaries){var a={bounce:t.boundaries.bounce||0,influence:v.createArea(t.boundaries)};this.propAreas.boundaries=a,this.oldBoundariesBehavior=this.animator.addBehavior("bounce",a)}else this.propAreas.boundaries=!1;!this.props.dragEnabled&&e.dragEnabled&&this.dragBehavior&&this.endDrag()}},{key:"addTypeBehaviors",value:function(e,t,n){var i=this;t.forEach(function(t){return i.addBehavior(e,t,n)})}},{key:"addBehavior",value:function(e,t,n){if(this.animator.addBehavior(e,t,n),t.damping&&"friction"!==e){var i=this.animator.addBehavior("friction",t,n);"gravity"!==e||t.influenceArea||(i.influence=v.createAreaFromRadius(1.4*(t.falloff||40),t))}}}]),n}(),u(g,"propTypes",{snapPoints:t.array,frictionAreas:t.array,alertAreas:t.array,gravityPoints:t.array,horizontalOnly:t.bool,verticalOnly:t.bool,dragWithSpring:t.object,dragEnabled:t.bool,animatedValueX:t.instanceOf(y.Value),animatedValueY:t.instanceOf(y.Value),onSnap:t.func,onSnapStart:t.func,onEnd:t.func,onDrag:t.func,boundaries:t.object,initialPosition:t.object,dragToss:t.number}),u(g,"defaultProps",{snapPoints:[],frictionAreas:[],alertAreas:[],gravityPoints:[],boundaries:{},initialPosition:{x:0,y:0},dragToss:.1,dragEnabled:!0,onSnap:function(){},onSnapStart:function(){},onStop:function(){},onDrag:function(){},onAlert:function(){},style:{}}),b)}}); 9 | //# sourceMappingURL=interactable.noNative.min.js.map 10 | -------------------------------------------------------------------------------- /native.js: -------------------------------------------------------------------------------- 1 | import Interactable from './playground/src/react-interactable/native' 2 | export default Interactable -------------------------------------------------------------------------------- /noNative.js: -------------------------------------------------------------------------------- 1 | import Interactable from './dist/interactable.noNative' 2 | export default Interactable -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-interactable", 3 | "version": "0.6.5", 4 | "description": "A port of react-native-interactable to make it work on the web", 5 | "main": "dist/interactable.native.js", 6 | "scripts": { 7 | "start": "cd playground && node ./scripts/start.js", 8 | "build": "./node_modules/.bin/bili", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "Javier Marquez", 12 | "license": "MIT", 13 | "dependencies": { 14 | "prop-types": "^15.8.1" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/arqex/react-interactable" 19 | }, 20 | "devDependencies": { 21 | "bili": "^3.4.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /playground/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /playground/README.md: -------------------------------------------------------------------------------- 1 | React Interactable Playground 2 | ----------------------------- 3 | 4 | A web app made with [react-native-web](https://github.com/necolas/react-native-web) with examples for [react-interactable](https://github.com/arqex/react-interactable). 5 | 6 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 7 | 8 | ## Available Scripts 9 | 10 | In the project directory, you can run: 11 | 12 | ### `npm start` 13 | 14 | Runs the app in the development mode.
15 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 16 | 17 | The page will reload if you make edits.
18 | You will also see any lint errors in the console. 19 | 20 | ### `npm test` 21 | 22 | Launches the test runner in the interactive watch mode.
23 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 24 | 25 | ### `npm run build` 26 | 27 | Builds the app for production to the `build` folder.
28 | It correctly bundles React in production mode and optimizes the build for the best performance. 29 | 30 | The build is minified and the filenames include the hashes.
31 | The playground is ready to be deployed! 32 | 33 | ## Learn More 34 | 35 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 36 | 37 | To learn React, check out the [React documentation](https://reactjs.org/). 38 | -------------------------------------------------------------------------------- /playground/config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | var dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. Variable expansion is supported in .env files. 31 | // https://github.com/motdotla/dotenv 32 | // https://github.com/motdotla/dotenv-expand 33 | dotenvFiles.forEach(dotenvFile => { 34 | if (fs.existsSync(dotenvFile)) { 35 | require('dotenv-expand')( 36 | require('dotenv').config({ 37 | path: dotenvFile, 38 | }) 39 | ); 40 | } 41 | }); 42 | 43 | // We support resolving modules according to `NODE_PATH`. 44 | // This lets you use absolute paths in imports inside large monorepos: 45 | // https://github.com/facebook/create-react-app/issues/253. 46 | // It works similar to `NODE_PATH` in Node itself: 47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 50 | // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 51 | // We also resolve them to make sure all tools using them work consistently. 52 | const appDirectory = fs.realpathSync(process.cwd()); 53 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 54 | .split(path.delimiter) 55 | .filter(folder => folder && !path.isAbsolute(folder)) 56 | .map(folder => path.resolve(appDirectory, folder)) 57 | .join(path.delimiter); 58 | 59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 60 | // injected into the application via DefinePlugin in Webpack configuration. 61 | const REACT_APP = /^REACT_APP_/i; 62 | 63 | function getClientEnvironment(publicUrl) { 64 | const raw = Object.keys(process.env) 65 | .filter(key => REACT_APP.test(key)) 66 | .reduce( 67 | (env, key) => { 68 | env[key] = process.env[key]; 69 | return env; 70 | }, 71 | { 72 | // Useful for determining whether we’re running in production mode. 73 | // Most importantly, it switches React into the correct mode. 74 | NODE_ENV: process.env.NODE_ENV || 'development', 75 | // Useful for resolving the correct path to static assets in `public`. 76 | // For example, . 77 | // This should only be used as an escape hatch. Normally you would put 78 | // images into the `src` and `import` them in code to get their paths. 79 | PUBLIC_URL: publicUrl, 80 | } 81 | ); 82 | // Stringify all values so we can feed into Webpack DefinePlugin 83 | const stringified = { 84 | 'process.env': Object.keys(raw).reduce((env, key) => { 85 | env[key] = JSON.stringify(raw[key]); 86 | return env; 87 | }, {}), 88 | }; 89 | 90 | return { raw, stringified }; 91 | } 92 | 93 | module.exports = getClientEnvironment; 94 | -------------------------------------------------------------------------------- /playground/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /playground/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | const assetFilename = JSON.stringify(path.basename(filename)); 11 | 12 | if (filename.match(/\.svg$/)) { 13 | return `module.exports = { 14 | __esModule: true, 15 | default: ${assetFilename}, 16 | ReactComponent: (props) => ({ 17 | $$typeof: Symbol.for('react.element'), 18 | type: 'svg', 19 | ref: null, 20 | key: null, 21 | props: Object.assign({}, props, { 22 | children: ${assetFilename} 23 | }) 24 | }), 25 | };`; 26 | } 27 | 28 | return `module.exports = ${assetFilename};`; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /playground/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(inputPath, needsSlash) { 15 | const hasSlash = inputPath.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return inputPath.substr(0, inputPath.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${inputPath}/`; 20 | } else { 21 | return inputPath; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right