├── package.json ├── example └── index.html ├── dist ├── index.html ├── index.js ├── src.7ed060e2.js └── src.7ed060e2.js.map ├── LICENSE ├── .gitignore ├── README.md └── src └── index.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-mouse-dragndrop-component", 3 | "version": "1.1.0", 4 | "description": "Drag-n-drop entites using mouse cursor.", 5 | "keywords": [ 6 | "aframe", 7 | "drag-n-drop", 8 | "aframe-vr", 9 | "webvr", 10 | "aframe-component" 11 | ], 12 | "homepage": "https://github.com/extraymond/aframe-mouse-dragndrop", 13 | "repository": "extraymond/aframe-mouse-dragndrop", 14 | "main": "src/index.js", 15 | "scripts": { 16 | "test": "parcel example/index.html", 17 | "build": "parcel build src/index.js --no-source-maps" 18 | }, 19 | "author": "Raymond Yeh ", 20 | "license": "MIT", 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "aframe": "^0.9.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 extraymond 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .cache/ 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badgen.net/npm/v/aframe-mouse-dragndrop-component)](https://www.npmjs.com/package/aframe-mouse-dragndrop-component) 2 | [![jsdelivr version](https://badgen.net/jsdelivr/v/npm/aframe-mouse-dragndrop-component)](https://cdn.jsdelivr.net/npm/aframe-mouse-dragndrop-component@1.0.4/dist/index.js) 3 | 4 | # aframe-mouse-dragndrop 5 | 6 | Drag-n-drop entites using mouse cursor. 7 | 8 | ## implementation 9 | 10 | There's a new parameter in cursor component that we can use to monitor mouse interaction. 11 | 12 | ```html 13 | 14 | ``` 15 | 16 | Based on this feature, we can intercept the event data emitting by the cursor component. [read more about cursor component](https://aframe.io/docs/0.9.0/components/cursor.html#intersection-data) 17 | 18 | I've sperated this module into two sepearte component, **track-cursor** and **dragndrop**. And use dependencies to chain component startup. [read more about component dependencies](https://aframe.io/docs/0.9.0/core/component.html#dependencies) 19 | So you can add features like animation or hovering effects before draggin it. 20 | 21 | When a draggable component is being hovered by the cursor, it will make the entity into tracking state. You can check it with 22 | 23 | ```js 24 | if (this.el.is("tracking")) { 25 | ... 26 | } 27 | ``` 28 | 29 | If a draggable component is being pressed, it will enter the dragging state. Likewise you can check it with: 30 | 31 | ```js 32 | if (this.el.is("dragging")) { 33 | ... 34 | } 35 | ``` 36 | 37 | If you want to do some startup/cleanup before entering/exiting these two states, be sure to listener to "stateadded"/"stateremoved" events. 38 | 39 | ## usage 40 | 41 | ```html 42 | 43 | ``` 44 | 45 | After adding the component, you can drag and drop it with mouse click. 46 | 47 | ## additional features 48 | 49 | 1. When dragging the entity, look-controls on the camera will be temporary disabled. 50 | 2. You can use mousewheel to send it further/closer when dragging. 51 | 52 | Check out the live demo: [link](https://sassy-piper.glitch.me) 53 | 54 | Appreciate any suggestions or feedback. 55 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | if (window.AFRAME == null) { 2 | console.error("aframe not found, please import it before this component.") 3 | } 4 | 5 | AFRAME.registerSystem("track-cursor", { 6 | init: function() { 7 | this.el.setAttribute("cursor", { rayOrigin: "mouse" }); 8 | } 9 | }); 10 | 11 | AFRAME.registerComponent("track-cursor", { 12 | init: function() { 13 | this.el.addEventListener("mousedown", e => { 14 | if (this.el.is("cursor-hovered")) { 15 | this.el.sceneEl.camera.el.setAttribute("look-controls", { 16 | enabled: false 17 | }); 18 | this.el.addState("dragging"); 19 | } 20 | }) 21 | this.el.addEventListener("click", e => { 22 | if (this.el.is("dragging")) { 23 | this.el.sceneEl.camera.el.setAttribute("look-controls", { 24 | enabled: true 25 | }); 26 | this.el.removeState("dragging"); 27 | } 28 | }) 29 | }, 30 | }); 31 | 32 | AFRAME.registerComponent("dragndrop", { 33 | dependencies: ["track-cursor"], 34 | init: function() { 35 | this.range = 0; 36 | this.dist = 0; 37 | 38 | this.el.addEventListener("stateadded", e => { 39 | if (e.detail == "dragging") { 40 | this.range = 0; 41 | this.dist = this.el.object3D.position 42 | .clone() 43 | .sub(this.el.sceneEl.camera.el.object3D.position) 44 | .length(); 45 | } 46 | }) 47 | 48 | this.direction = new AFRAME.THREE.Vector3(); 49 | this.target = new AFRAME.THREE.Vector3(); 50 | document.addEventListener("wheel", e => { 51 | if (e.deltaY < 0) { 52 | this.range += 0.1; 53 | } else { 54 | this.range -= 0.1; 55 | } 56 | }); 57 | }, 58 | updateDirection: function() { 59 | this.direction.copy(this.el.sceneEl.getAttribute("raycaster").direction); 60 | }, 61 | updateTarget: function() { 62 | let camera = this.el.sceneEl.camera.el 63 | this.target.copy( 64 | camera.object3D.position 65 | .clone() 66 | .add(this.direction.clone().multiplyScalar(this.dist + this.range)) 67 | ); 68 | }, 69 | tick: function() { 70 | if (this.el.is("dragging")) { 71 | this.updateDirection(); 72 | this.updateTarget(); 73 | this.el.object3D.position.copy(this.target); 74 | } 75 | } 76 | }); 77 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c numeric require 5 | // 6 | // anything defined in a previous bundle is accessed via the 7 | // orig method which is the require for previous bundles 8 | parcelRequire = (function (modules, cache, entry, globalName) { 9 | // Save the require from previous bundle to this closure if any 10 | var previousRequire = typeof parcelRequire === 'function' && parcelRequire; 11 | var nodeRequire = typeof require === 'function' && require; 12 | 13 | function newRequire(name, jumped) { 14 | if (!cache[name]) { 15 | if (!modules[name]) { 16 | // if we cannot find the module within our internal map or 17 | // cache jump to the current global require ie. the last bundle 18 | // that was added to the page. 19 | var currentRequire = typeof parcelRequire === 'function' && parcelRequire; 20 | if (!jumped && currentRequire) { 21 | return currentRequire(name, true); 22 | } 23 | 24 | // If there are other bundles on this page the require from the 25 | // previous one is saved to 'previousRequire'. Repeat this as 26 | // many times as there are bundles until the module is found or 27 | // we exhaust the require chain. 28 | if (previousRequire) { 29 | return previousRequire(name, true); 30 | } 31 | 32 | // Try the node require function if it exists. 33 | if (nodeRequire && typeof name === 'string') { 34 | return nodeRequire(name); 35 | } 36 | 37 | var err = new Error('Cannot find module \'' + name + '\''); 38 | err.code = 'MODULE_NOT_FOUND'; 39 | throw err; 40 | } 41 | 42 | localRequire.resolve = resolve; 43 | localRequire.cache = {}; 44 | 45 | var module = cache[name] = new newRequire.Module(name); 46 | 47 | modules[name][0].call(module.exports, localRequire, module, module.exports, this); 48 | } 49 | 50 | return cache[name].exports; 51 | 52 | function localRequire(x){ 53 | return newRequire(localRequire.resolve(x)); 54 | } 55 | 56 | function resolve(x){ 57 | return modules[name][1][x] || x; 58 | } 59 | } 60 | 61 | function Module(moduleName) { 62 | this.id = moduleName; 63 | this.bundle = newRequire; 64 | this.exports = {}; 65 | } 66 | 67 | newRequire.isParcelRequire = true; 68 | newRequire.Module = Module; 69 | newRequire.modules = modules; 70 | newRequire.cache = cache; 71 | newRequire.parent = previousRequire; 72 | newRequire.register = function (id, exports) { 73 | modules[id] = [function (require, module) { 74 | module.exports = exports; 75 | }, {}]; 76 | }; 77 | 78 | var error; 79 | for (var i = 0; i < entry.length; i++) { 80 | try { 81 | newRequire(entry[i]); 82 | } catch (e) { 83 | // Save first error but execute all entries 84 | if (!error) { 85 | error = e; 86 | } 87 | } 88 | } 89 | 90 | if (entry.length) { 91 | // Expose entry point to Node, AMD or browser globals 92 | // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js 93 | var mainExports = newRequire(entry[entry.length - 1]); 94 | 95 | // CommonJS 96 | if (typeof exports === "object" && typeof module !== "undefined") { 97 | module.exports = mainExports; 98 | 99 | // RequireJS 100 | } else if (typeof define === "function" && define.amd) { 101 | define(function () { 102 | return mainExports; 103 | }); 104 | 105 | //