Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions examples/test/embedded-cursor/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cursor</title>
<meta name="description" content="Cursor - A-Frame">
<script src="../../../dist/aframe-master.js"></script>
<style type="text/css">
a-scene{
width: 50vw;
height: 50vh;
margin-top: 25vh;
margin-left: 25vw;
}
</style>
</head>
<body>
<a-scene embedded="true" cursor="rayOrigin: mouse">
<a-assets>
<audio id="blip1" src="../../showcase/anime-UI/audio/321103__nsstudios__blip1.wav"></audio>
<audio id="blip2" src="../../showcase/anime-UI/audio/321104__nsstudios__blip2.wav"></audio>
<audio id="shot" src="../cursor/shot.ogg"></audio>
<a-mixin id="cube" geometry="primitive: box"></a-mixin>
<a-mixin id="cube-hovered" material="color: #CC435F"></a-mixin>
<a-mixin id="cube-selected" material="color: #876A96"></a-mixin>
<a-mixin id="red" material="color: #F35F5F"></a-mixin>
<a-mixin id="green" material="color: #9DE0AD"></a-mixin>
<a-mixin id="blue" material="color: #36A3EB"></a-mixin>
<a-mixin id="yellow" material="color: #F1EA65"></a-mixin>
<a-mixin id="sphere" geometry="primitive: sphere"></a-mixin>
</a-assets>

<a-sky color="#FBE0D8"></a-sky>

<a-entity position="0 .6 4">
<a-camera>
<a-entity raycaster="far: 30; objects: .intersectable"
cursor
geometry="primitive: ring; radiusOuter: 0.015;
radiusInner: 0.01; segmentsTheta: 32"
material="color: #283644; shader: flat"
position="0 0 -0.75"></a-entity>
</a-camera>
</a-entity>

<a-entity position="-3.5 1 0">
<a-entity mixin="red cube"
class="intersectable">
<a-animation begin="click" attribute="position" from="0 0 0"
to="0 0 -10" dur="2000" fill="both"></a-animation>
</a-entity>
</a-entity>

<a-entity position="0 1 0">
<a-entity id="foregroundCube"
class="intersectable"
mixin="green cube"
sound__1="on: click; src: #blip1;"
sound__2="on: mouseenter; src: #blip2;">
<a-animation begin="click" attribute="rotation" to="0 360 0"
easing="linear" dur="2000" fill="backwards"></a-animation>
</a-entity>
</a-entity>

<a-entity position="1 1 0">
<a-entity mixin="yellow cube"
sound__1="on: click; src: #shot; poolSize: 6"
sound__2="src: #blip2; autoplay: true">
<a-animation begin="click" attribute="scale" to="1.2 1.2 1.2"
easing="linear" dur="200" fill="backwards"></a-animation>
</a-entity>
</a-entity>

<a-entity position="0 1 -3">
<a-entity id="backgroundCube"
class="intersectable"
mixin="green cube">
<a-animation begin="click" attribute="rotation" to="0 360 0"
easing="linear" dur="2000" fill="backwards"></a-animation>
</a-entity>
</a-entity>

<a-entity position="3.5 1 0" rotation="0 45 0">
<a-entity mixin="blue cube"
class="intersectable">
<a-animation begin="click" fill="forwards" repeat="1"
direction="alternate" attribute="position" from="0 0 0"
to="15 0 0" dur="2000"></a-animation>
</a-entity>
</a-entity>

<a-entity mixin="yellow cube" position="0 3 -3" class="intersectable clickable"
rotation="0 45 0" scale=".5 .5 .5"></a-entity>
</a-scene>

<script>
(function () {
// Custom states.
var clickedEl = null;
var els = document.querySelectorAll('a-entity');
Array.prototype.forEach.call(els, function (el) {
el.addEventListener('click', function () {
if (clickedEl && clickedEl !== el) {
clickedEl.removeState('selected');
}
if (!el.classList.contains('clickable')) { return; }
clickedEl = el;
clickedEl.addState('selected');
});
});

// Responding to mouse events.
var cubes = document.querySelectorAll('a-entity[mixin*=cube]');
var i;
for (i = 0; i < cubes.length; ++i) {
cubes[i].addEventListener('click', function () {
var href = this.getAttribute('href');
if (!href) { return; }
window.top.postMessage({type: 'navigate', data: {url: href}}, '*');
})
}

// testing mouseenter and mouseleave
var foregroundCube = document.querySelector('#foregroundCube');
foregroundCube.addEventListener('mouseenter', function (evt) {
foregroundCube.setAttribute('mixin','cube cube-hovered')
})
foregroundCube.addEventListener('mouseleave', function (evt) {
foregroundCube.setAttribute('mixin','green cube')
})
foregroundCube.addEventListener('mousedown', function (evt) {
foregroundCube.setAttribute('mixin','cube cube-selected')
})
foregroundCube.addEventListener('mouseup', function (evt) {
foregroundCube.setAttribute('mixin','cube cube-hovered')
})
foregroundCube.addEventListener('click', function (evt) {
var scene = document.querySelector('a-scene');
var clickRing = document.createElement('a-entity');
clickRing.id = 'clickRing-'+new Date().getTime();
clickRing.setAttribute('material','color: magenta; transparent: true; opacity: 0.6');
clickRing.setAttribute('geometry','primitive: ring; radius-inner: 0.5; radius-outer: 0.6');
clickRing.setAttribute('position',evt.detail.intersection.point);
var opacityAnimation = document.createElement('a-animation');
opacityAnimation.setAttribute('attribute','material.opacity');
opacityAnimation.setAttribute('to',0);
opacityAnimation.setAttribute('duration','250');
opacityAnimation.setAttribute('easing','ease-out-quad');
var scaleAnimation = document.createElement('a-animation');
scaleAnimation.setAttribute('attribute','scale');
scaleAnimation.setAttribute('to','3 3 3');
scaleAnimation.setAttribute('duration','250');
scaleAnimation.setAttribute('easing','ease-out-quad');
var onAnimationEnd = function () {
scene.removeChild(document.querySelector('#'+clickRing.id))
}
scaleAnimation.addEventListener('animationend', onAnimationEnd.bind(this))
clickRing.appendChild(opacityAnimation);
clickRing.appendChild(scaleAnimation);
scene.appendChild(clickRing);
})
})();
</script>
</body>
</html>
22 changes: 20 additions & 2 deletions src/components/cursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ module.exports.Component = registerComponent('cursor', {
},

init: function () {
var self = this;

this.fuseTimeout = undefined;
this.cursorDownEl = null;
this.intersection = null;
this.intersectedEl = null;
this.canvasBounds = document.body.getBoundingClientRect();

// Debounce.
this.updateCanvasBounds = utils.debounce(function updateCanvasBounds () {
self.canvasBounds = self.el.sceneEl.canvas.getBoundingClientRect();
}, 200);

// Bind methods.
this.onCursorDown = bind(this.onCursorDown, this);
Expand Down Expand Up @@ -104,6 +112,8 @@ module.exports.Component = registerComponent('cursor', {
});
el.addEventListener('raycaster-intersection', this.onIntersection);
el.addEventListener('raycaster-intersection-cleared', this.onIntersectionCleared);

window.addEventListener('resize', this.updateCanvasBounds);
},

removeEventListeners: function () {
Expand All @@ -127,6 +137,7 @@ module.exports.Component = registerComponent('cursor', {
el.removeEventListener('raycaster-intersection', this.onIntersection);
el.removeEventListener('raycaster-intersection-cleared', this.onIntersectionCleared);
window.removeEventListener('mousemove', this.onMouseMove);
window.removeEventListener('resize', this.updateCanvasBounds);
},

updateMouseEventListeners: function () {
Expand All @@ -136,6 +147,7 @@ module.exports.Component = registerComponent('cursor', {
if (this.data.rayOrigin !== 'mouse') { return; }
window.addEventListener('mousemove', this.onMouseMove, false);
el.setAttribute('raycaster', 'useWorldCoordinates', true);
this.updateCanvasBounds();
},

onMouseMove: (function () {
Expand All @@ -150,8 +162,14 @@ module.exports.Component = registerComponent('cursor', {
var camera = this.el.sceneEl.camera;
camera.parent.updateMatrixWorld();
camera.updateMatrixWorld();
mouse.x = (evt.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(evt.clientY / window.innerHeight) * 2 + 1;

// Calculate mouse position based on the canvas element
var bounds = this.canvasBounds;
var left = evt.clientX - bounds.left;
var top = evt.clientY - bounds.top;
mouse.x = (left / bounds.width) * 2 - 1;
mouse.y = -(top / bounds.height) * 2 + 1;

origin.setFromMatrixPosition(camera.matrixWorld);
direction.set(mouse.x, mouse.y, 0.5).unproject(camera).sub(origin).normalize();
this.el.setAttribute('raycaster', rayCasterConfig);
Expand Down
24 changes: 24 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,30 @@ module.exports.throttleTick = function (functionToThrottle, minimumInterval, opt
};
};

/**
* Returns debounce function that gets called only once after a set of repeated calls.
*
* @param {function} functionToDebounce
* @param {number} wait - Time to wait for repeated function calls (milliseconds).
* @param {boolean} immediate - Calls the function immediately regardless of if it should be waiting.
* @returns {function} Debounced function.
*/
module.exports.debounce = function (func, wait, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};

/**
* Fires a custom DOM event.
*
Expand Down