Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

236 řádky
6.7 KiB

  1. /*! svg.draggable.js - v2.2.2 - 2019-01-08
  2. * https://github.com/svgdotjs/svg.draggable.js
  3. * Copyright (c) 2019 Wout Fierens; Licensed MIT */
  4. ;(function() {
  5. // creates handler, saves it
  6. function DragHandler(el){
  7. el.remember('_draggable', this)
  8. this.el = el
  9. }
  10. // Sets new parameter, starts dragging
  11. DragHandler.prototype.init = function(constraint, val){
  12. var _this = this
  13. this.constraint = constraint
  14. this.value = val
  15. this.el.on('mousedown.drag', function(e){ _this.start(e) })
  16. this.el.on('touchstart.drag', function(e){ _this.start(e) })
  17. }
  18. // transforms one point from screen to user coords
  19. DragHandler.prototype.transformPoint = function(event, offset){
  20. event = event || window.event
  21. var touches = event.changedTouches && event.changedTouches[0] || event
  22. this.p.x = touches.clientX - (offset || 0)
  23. this.p.y = touches.clientY
  24. return this.p.matrixTransform(this.m)
  25. }
  26. // gets elements bounding box with special handling of groups, nested and use
  27. DragHandler.prototype.getBBox = function(){
  28. var box = this.el.bbox()
  29. if(this.el instanceof SVG.Nested) box = this.el.rbox()
  30. if (this.el instanceof SVG.G || this.el instanceof SVG.Use || this.el instanceof SVG.Nested) {
  31. box.x = this.el.x()
  32. box.y = this.el.y()
  33. }
  34. return box
  35. }
  36. // start dragging
  37. DragHandler.prototype.start = function(e){
  38. // check for left button
  39. if(e.type == 'click'|| e.type == 'mousedown' || e.type == 'mousemove'){
  40. if((e.which || e.buttons) != 1){
  41. return
  42. }
  43. }
  44. var _this = this
  45. // fire beforedrag event
  46. this.el.fire('beforedrag', { event: e, handler: this })
  47. if(this.el.event().defaultPrevented) return;
  48. // prevent browser drag behavior as soon as possible
  49. e.preventDefault();
  50. // prevent propagation to a parent that might also have dragging enabled
  51. e.stopPropagation();
  52. // search for parent on the fly to make sure we can call
  53. // draggable() even when element is not in the dom currently
  54. this.parent = this.parent || this.el.parent(SVG.Nested) || this.el.parent(SVG.Doc)
  55. this.p = this.parent.node.createSVGPoint()
  56. // save current transformation matrix
  57. this.m = this.el.node.getScreenCTM().inverse()
  58. var box = this.getBBox()
  59. var anchorOffset;
  60. // fix text-anchor in text-element (#37)
  61. if(this.el instanceof SVG.Text){
  62. anchorOffset = this.el.node.getComputedTextLength();
  63. switch(this.el.attr('text-anchor')){
  64. case 'middle':
  65. anchorOffset /= 2;
  66. break
  67. case 'start':
  68. anchorOffset = 0;
  69. break;
  70. }
  71. }
  72. this.startPoints = {
  73. // We take absolute coordinates since we are just using a delta here
  74. point: this.transformPoint(e, anchorOffset),
  75. box: box,
  76. transform: this.el.transform()
  77. }
  78. // add drag and end events to window
  79. SVG.on(window, 'mousemove.drag', function(e){ _this.drag(e) })
  80. SVG.on(window, 'touchmove.drag', function(e){ _this.drag(e) })
  81. SVG.on(window, 'mouseup.drag', function(e){ _this.end(e) })
  82. SVG.on(window, 'touchend.drag', function(e){ _this.end(e) })
  83. // fire dragstart event
  84. this.el.fire('dragstart', {event: e, p: this.startPoints.point, m: this.m, handler: this})
  85. }
  86. // while dragging
  87. DragHandler.prototype.drag = function(e){
  88. var box = this.getBBox()
  89. , p = this.transformPoint(e)
  90. , x = this.startPoints.box.x + p.x - this.startPoints.point.x
  91. , y = this.startPoints.box.y + p.y - this.startPoints.point.y
  92. , c = this.constraint
  93. , gx = p.x - this.startPoints.point.x
  94. , gy = p.y - this.startPoints.point.y
  95. this.el.fire('dragmove', {
  96. event: e
  97. , p: p
  98. , m: this.m
  99. , handler: this
  100. })
  101. if(this.el.event().defaultPrevented) return p
  102. // move the element to its new position, if possible by constraint
  103. if (typeof c == 'function') {
  104. var coord = c.call(this.el, x, y, this.m)
  105. // bool, just show us if movement is allowed or not
  106. if (typeof coord == 'boolean') {
  107. coord = {
  108. x: coord,
  109. y: coord
  110. }
  111. }
  112. // if true, we just move. If !false its a number and we move it there
  113. if (coord.x === true) {
  114. this.el.x(x)
  115. } else if (coord.x !== false) {
  116. this.el.x(coord.x)
  117. }
  118. if (coord.y === true) {
  119. this.el.y(y)
  120. } else if (coord.y !== false) {
  121. this.el.y(coord.y)
  122. }
  123. } else if (typeof c == 'object') {
  124. // keep element within constrained box
  125. if (c.minX != null && x < c.minX) {
  126. x = c.minX
  127. gx = x - this.startPoints.box.x
  128. } else if (c.maxX != null && x > c.maxX - box.width) {
  129. x = c.maxX - box.width
  130. gx = x - this.startPoints.box.x
  131. } if (c.minY != null && y < c.minY) {
  132. y = c.minY
  133. gy = y - this.startPoints.box.y
  134. } else if (c.maxY != null && y > c.maxY - box.height) {
  135. y = c.maxY - box.height
  136. gy = y - this.startPoints.box.y
  137. }
  138. if (c.snapToGrid != null) {
  139. x = x - (x % c.snapToGrid)
  140. y = y - (y % c.snapToGrid)
  141. gx = gx - (gx % c.snapToGrid)
  142. gy = gy - (gy % c.snapToGrid)
  143. }
  144. if(this.el instanceof SVG.G)
  145. this.el.matrix(this.startPoints.transform).transform({x:gx, y: gy}, true)
  146. else
  147. this.el.move(x, y)
  148. }
  149. // so we can use it in the end-method, too
  150. return p
  151. }
  152. DragHandler.prototype.end = function(e){
  153. // final drag
  154. var p = this.drag(e);
  155. // fire dragend event
  156. this.el.fire('dragend', { event: e, p: p, m: this.m, handler: this })
  157. // unbind events
  158. SVG.off(window, 'mousemove.drag')
  159. SVG.off(window, 'touchmove.drag')
  160. SVG.off(window, 'mouseup.drag')
  161. SVG.off(window, 'touchend.drag')
  162. }
  163. SVG.extend(SVG.Element, {
  164. // Make element draggable
  165. // Constraint might be an object (as described in readme.md) or a function in the form "function (x, y)" that gets called before every move.
  166. // The function can return a boolean or an object of the form {x, y}, to which the element will be moved. "False" skips moving, true moves to raw x, y.
  167. draggable: function(value, constraint) {
  168. // Check the parameters and reassign if needed
  169. if (typeof value == 'function' || typeof value == 'object') {
  170. constraint = value
  171. value = true
  172. }
  173. var dragHandler = this.remember('_draggable') || new DragHandler(this)
  174. // When no parameter is given, value is true
  175. value = typeof value === 'undefined' ? true : value
  176. if(value) dragHandler.init(constraint || {}, value)
  177. else {
  178. this.off('mousedown.drag')
  179. this.off('touchstart.drag')
  180. }
  181. return this
  182. }
  183. })
  184. }).call(this);