OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @fileoverview Behavior for handling dragging elements in a container. |
| 7 * Draggable elements must have the 'draggable' attribute set. |
| 8 */ |
| 9 |
| 10 /** |
| 11 * @typedef {{ |
| 12 * x: number, |
| 13 * y: number |
| 14 * }} |
| 15 */ |
| 16 var DragPosition; |
| 17 |
| 18 /** @polymerBehavior */ |
| 19 var DragBehavior = { |
| 20 /** |
| 21 * The id of the element being dragged, or empty if not dragging. |
| 22 * @private {string} |
| 23 */ |
| 24 dragId_: '', |
| 25 |
| 26 /** @private {boolean} */ |
| 27 enabled_: false, |
| 28 |
| 29 /** @private {!HTMLDivElement|undefined} */ |
| 30 container_: undefined, |
| 31 |
| 32 /** @private {?function(string, ?DragPosition):void} */ |
| 33 callback_: null, |
| 34 |
| 35 /** @private {!DragPosition} */ |
| 36 dragStartLocation_: {x: 0, y: 0}, |
| 37 |
| 38 /** |
| 39 * Used to ignore unnecessary drag events. |
| 40 * @private {?DragPosition} |
| 41 */ |
| 42 lastTouchLocation_: null, |
| 43 |
| 44 /** @private {?function(!Event)} */ |
| 45 mouseDownListener_: null, |
| 46 |
| 47 /** @private {?function(!Event)} */ |
| 48 mouseMoveListener_: null, |
| 49 |
| 50 /** @private {?function(!Event)} */ |
| 51 touchStartListener_: null, |
| 52 |
| 53 /** @private {?function(!Event)} */ |
| 54 touchMoveListener_: null, |
| 55 |
| 56 /** @private {?function(!Event)} */ |
| 57 endDragListener_: null, |
| 58 |
| 59 /** |
| 60 * @param {boolean} enabled |
| 61 * @param {!HTMLDivElement=} opt_container |
| 62 * @param {!function(string, ?DragPosition):void=} opt_callback |
| 63 */ |
| 64 initializeDrag: function(enabled, opt_container, opt_callback) { |
| 65 this.enabled_ = enabled; |
| 66 if (!enabled) { |
| 67 if (this.container) { |
| 68 this.container.removeEventListener('mousdown', this.mouseDownListener_); |
| 69 this.mouseDownListener_ = null; |
| 70 this.container.removeEventListener( |
| 71 'mousemove', this.mouseMoveListener_); |
| 72 this.mouseMoveListener_ = null; |
| 73 this.container.removeEventListener( |
| 74 'touchstart', this.touchStartListener_); |
| 75 this.touchStartListener_ = null; |
| 76 this.container.removeEventListener( |
| 77 'touchmove', this.touchMoveListener_); |
| 78 this.touchMoveListener_ = null; |
| 79 this.container.removeEventListener('touchend', this.endDragListener_); |
| 80 } |
| 81 if (this.mouseUpListener_) |
| 82 window.removeEventListener('mouseup', this.endDragListener_); |
| 83 this.endDragListener_ = null; |
| 84 return; |
| 85 } |
| 86 |
| 87 if (opt_container !== undefined) |
| 88 this.container_ = opt_container; |
| 89 var container = this.container_; |
| 90 assert(container); |
| 91 |
| 92 this.mouseDownListener_ = this.onMouseDown_.bind(this); |
| 93 container.addEventListener('mousedown', this.mouseDownListener_, true); |
| 94 |
| 95 this.mouseMoveListener_ = this.onMouseMove_.bind(this); |
| 96 container.addEventListener('mousemove', this.mouseMoveListener_, true); |
| 97 |
| 98 this.touchStartListener_ = this.onTouchStart_.bind(this); |
| 99 container.addEventListener('touchstart', this.touchStartListener_, true); |
| 100 |
| 101 this.touchMoveListener_ = this.onTouchMove_.bind(this); |
| 102 container.addEventListener('touchmove', this.touchMoveListener_, true); |
| 103 |
| 104 this.endDragListener_ = this.endDrag_.bind(this); |
| 105 window.addEventListener('mouseup', this.endDragListener_, true); |
| 106 container.addEventListener('touchend', this.endDragListener_, true); |
| 107 |
| 108 if (opt_callback !== undefined) |
| 109 this.callback_ = opt_callback; |
| 110 }, |
| 111 |
| 112 /** |
| 113 * @param {Event} e The mouse down event. |
| 114 * @return {boolean} |
| 115 * @private |
| 116 */ |
| 117 onMouseDown_: function(e) { |
| 118 if (e.button != 0) |
| 119 return true; |
| 120 if (!e.target.getAttribute('draggable')) |
| 121 return true; |
| 122 e.preventDefault(); |
| 123 var target = assertInstanceof(e.target, HTMLElement); |
| 124 return this.startDrag_(target, {x: e.pageX, y: e.pageY}); |
| 125 }, |
| 126 |
| 127 /** |
| 128 * @param {Event} e The mouse move event. |
| 129 * @return {boolean} |
| 130 * @private |
| 131 */ |
| 132 onMouseMove_: function(e) { |
| 133 e.preventDefault(); |
| 134 return this.processDrag_(e, {x: e.pageX, y: e.pageY}); |
| 135 }, |
| 136 |
| 137 /** |
| 138 * @param {Event} e The touch start event. |
| 139 * @return {boolean} |
| 140 * @private |
| 141 */ |
| 142 onTouchStart_: function(e) { |
| 143 if (e.touches.length != 1) |
| 144 return false; |
| 145 |
| 146 e.preventDefault(); |
| 147 var touch = e.touches[0]; |
| 148 this.lastTouchLocation_ = {x: touch.pageX, y: touch.pageY}; |
| 149 var target = assertInstanceof(e.target, HTMLElement); |
| 150 return this.startDrag_(target, this.lastTouchLocation_); |
| 151 }, |
| 152 |
| 153 /** |
| 154 * @param {Event} e The touch move event. |
| 155 * @return {boolean} |
| 156 * @private |
| 157 */ |
| 158 onTouchMove_: function(e) { |
| 159 if (e.touches.length != 1) |
| 160 return true; |
| 161 |
| 162 var touchLocation = {x: e.touches[0].pageX, y: e.touches[0].pageY}; |
| 163 // Touch move events can happen even if the touch location doesn't change |
| 164 // and on small unintentional finger movements. Ignore these small changes. |
| 165 if (this.lastTouchLocation_) { |
| 166 /** @const */ var IGNORABLE_TOUCH_MOVE_PX = 1; |
| 167 var xDiff = Math.abs(touchLocation.x - this.lastTouchLocation_.x); |
| 168 var yDiff = Math.abs(touchLocation.y - this.lastTouchLocation_.y); |
| 169 if (xDiff <= IGNORABLE_TOUCH_MOVE_PX && yDiff <= IGNORABLE_TOUCH_MOVE_PX) |
| 170 return true; |
| 171 } |
| 172 this.lastTouchLocation_ = touchLocation; |
| 173 e.preventDefault(); |
| 174 return this.processDrag_(e, touchLocation); |
| 175 }, |
| 176 |
| 177 /** |
| 178 * @param {!HTMLElement} target |
| 179 * @param {!DragPosition} eventLocation |
| 180 * @return {boolean} |
| 181 * @private |
| 182 */ |
| 183 startDrag_: function(target, eventLocation) { |
| 184 this.dragId_ = target.id; |
| 185 this.dragStartLocation_ = eventLocation; |
| 186 return false; |
| 187 }, |
| 188 |
| 189 /** |
| 190 * @param {Event} e |
| 191 * @return {boolean} |
| 192 * @private |
| 193 */ |
| 194 endDrag_: function(e) { |
| 195 if (this.dragId_ && this.callback_) |
| 196 this.callback_(this.dragId_, null); |
| 197 this.dragId_ = ''; |
| 198 this.lastTouchLocation_ = null; |
| 199 return false; |
| 200 }, |
| 201 |
| 202 /** |
| 203 * @param {Event} e The event which triggers this drag. |
| 204 * @param {DragPosition} eventLocation The location of the event. |
| 205 * @return {boolean} |
| 206 * @private |
| 207 */ |
| 208 processDrag_: function(e, eventLocation) { |
| 209 if (!this.dragId_) |
| 210 return true; |
| 211 if (this.callback_) { |
| 212 var delta = { |
| 213 x: eventLocation.x - this.dragStartLocation_.x, |
| 214 y: eventLocation.y - this.dragStartLocation_.y, |
| 215 }; |
| 216 this.callback_(this.dragId_, delta); |
| 217 } |
| 218 return false; |
| 219 }, |
| 220 }; |
OLD | NEW |