1 /** 2 * The Render Engine 3 * KeyboardInputComponent 4 * 5 * @fileoverview An extension of the input component to handle touch inputs from 6 * devices which support them. 7 * 8 * @author: Brett Fattori (brettf@renderengine.com) 9 * @author: $Author: bfattori $ 10 * @version: $Revision: 1555 $ 11 * 12 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 13 * 14 * Permission is hereby granted, free of charge, to any person obtaining a copy 15 * of this software and associated documentation files (the "Software"), to deal 16 * in the Software without restriction, including without limitation the rights 17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 * copies of the Software, and to permit persons to whom the Software is 19 * furnished to do so, subject to the following conditions: 20 * 21 * The above copyright notice and this permission notice shall be included in 22 * all copies or substantial portions of the Software. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 * THE SOFTWARE. 31 * 32 */ 33 34 // The class this file defines and its required classes 35 R.Engine.define({ 36 "class":"R.components.input.Touch", 37 "requires":[ 38 "R.components.Input", 39 "R.engine.Events", 40 "R.struct.Touch" 41 ] 42 }); 43 44 /** 45 * @class A component which responds to touch events and notifies 46 * its {@link R.engine.GameObject} by triggering one of three events. The <tt>R.engine.GameObject</tt> 47 * should add event handlers for any one of: 48 * <ul> 49 * <li><tt>touchstart</tt> - A touch event started</li> 50 * <li><tt>touchend</tt> - A touch event ended</li> 51 * <li><tt>touchmove</tt> - A movement occurred after a touch event started</li> 52 * </ul> 53 * Each event handler will be passed a {@link R.struct.TouchInfo TouchInfo} object which 54 * contains information about the event. The <tt>touchmove</tt> event is also passed a boolean 55 * flag indicating if the touch is within the world bounding box of the game object. 56 * <p/> 57 * <i>Note: The rendering context that the object is contained within needs to enable touch event 58 * capturing with the {@link R.rendercontexts.AbstractRenderContext#captureTouch} method.</i> 59 * 60 * @param name {String} The unique name of the component. 61 * @param [priority] {Number} The priority of the component among other input components. 62 * @extends R.components.Input 63 * @constructor 64 * @description Create an instance of a touch input component. 65 */ 66 R.components.input.Touch = function () { 67 "use strict"; 68 return R.components.Input.extend(/** @scope R.components.input.Touch.prototype */{ 69 70 hasTouchMethods:null, 71 72 /** 73 * @private 74 */ 75 constructor:function (name, passThru, priority) { 76 this.base(name, priority); 77 }, 78 79 /** 80 * Destroy this instance and remove all references. 81 */ 82 destroy:function () { 83 if (this.getGameObject()) { 84 delete this.getGameObject().getObjectDataModel()[R.components.input.Touch.TOUCH_DATA_MODEL]; 85 } 86 this.base(); 87 }, 88 89 /** 90 * Deprecated in favor of {@link #setGameObject} 91 * @deprecated 92 */ 93 setHostObject:function (hostObj) { 94 this.setGameObject(hostObj); 95 }, 96 97 /** 98 * Establishes the link between this component and its host object. 99 * When you assign components to a host object, it will call this method 100 * so that each component can refer to its host object, the same way 101 * a host object can refer to a component with {@link R.engine.GameObject#getComponent}. 102 * 103 * @param gameObject {R.engine.GameObject} The object which hosts this component 104 */ 105 setGameObject:function (gameObject) { 106 this.base(gameObject); 107 108 // Set some flags we can check 109 var dataModel = gameObject.setObjectDataModel(R.components.input.Mouse.TOUCH_DATA_MODEL, { 110 touchDown:false 111 }); 112 113 // Add event pass-thru for DOM objects 114 var el = gameObject.jQ(); 115 if (el) { 116 var tI = R.struct.TouchInfo.create(); 117 118 // Wire up event handlers for the DOM element to mimic what is done for 119 // canvas objects 120 el.bind("touchmove", function (evt) { 121 tI.lastPosition.set(tI.position); 122 tI.position.set(evt.pageX, evt.pageY); 123 gameObject.triggerEvent("touchmove", [tI]); 124 }); 125 126 el.bind("touchstart", function (evt) { 127 tI.touches = tI.processTouches(evt); 128 tI.button = R.engine.Events.MOUSE_LEFT_BUTTON; 129 tI.downPosition.set(evt.pageX, evt.pageY); 130 gameObject.triggerEvent("touchstart", [tI]); 131 }); 132 133 el.bind("touchend", function (evt) { 134 tI.touches = tI.processTouches(evt); 135 tI.button = R.engine.Events.MOUSE_NO_BUTTON; 136 tI.dragVec.set(0, 0); 137 gameObject.triggerEvent("touchend", [tI]); 138 }); 139 } 140 141 }, 142 143 /** 144 * Perform the checks on the touch info object, and also perform 145 * intersection tests to be able to call touch events. 146 * @param renderContext {R.rendercontexts.AbstractRenderContext} The render context 147 * @param time {Number} The current world time 148 * @param dt {Number} The delta between the world time and the last time the world was updated 149 * in milliseconds. 150 */ 151 execute:function (renderContext, time, dt) { 152 // Objects may be in motion. If so, we need to call the touch 153 // methods for just such a case. 154 var gameObject = this.getGameObject(); 155 156 157 // Only objects without an element will use this. For object WITH an element, 158 // this component will have intervened and wired up special handlers to fake 159 // the mouseInfo object. 160 if (!gameObject.getElement()) { 161 var touchInfo = renderContext.getTouchInfo(), 162 bBox = gameObject.getWorldBox(), 163 touchOn = false, 164 dataModel = gameObject.getObjectDataModel(R.components.input.Touch.TOUCH_DATA_MODEL); 165 166 if (touchInfo && bBox) { 167 touchOn = R.math.Math2D.boxPointCollision(bBox, touchInfo.position); 168 } 169 170 // Touched on object 171 if (touchOn && (touchInfo.button != R.engine.Events.MOUSE_NO_BUTTON)) { 172 dataModel.touchDown = true; 173 gameObject.triggerEvent("touchstart", [touchInfo]); 174 } 175 176 // Touch position changed 177 if (dataModel.touchDown && !touchInfo.position.equals(touchInfo.lastPosition)) { 178 gameObject.triggerEvent("touchmove", [touchInfo, touchOn]); 179 } 180 181 // Touch ended (and object was touched) 182 if (dataModel.touchDown && touchInfo.button == R.engine.Events.MOUSE_NO_BUTTON) { 183 dataModel.touchDown = false; 184 gameObject.triggerEvent("touchend", [touchInfo]); 185 } 186 } 187 } 188 189 }, /** @scope R.components.input.Touch.prototype */{ 190 /** 191 * Get the class name of this object 192 * 193 * @return {String} "R.components.input.Touch" 194 */ 195 getClassName:function () { 196 return "R.components.input.Touch"; 197 }, 198 199 /** 200 * @private 201 */ 202 TOUCH_DATA_MODEL:"TouchInputComponent" 203 }); 204 }; 205