annotation-box.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <template>
  2. <div class="annotation-box"
  3. :style="style"
  4. :class="{
  5. draggable,
  6. deletable,
  7. taggable,
  8. confirmable,
  9. croppable,
  10. }"
  11. @contextmenu.prevent
  12. @mousedown.prevent="click" @touchstart.prevent="click">
  13. <template v-if="draggable">
  14. <div class="nw" @mousedown.prevent.stop="resize('nw')" @touchstart.prevent.stop="resize('nw')"/>
  15. <div class="nn" @mousedown.prevent.stop="resize('nn')" @touchstart.prevent.stop="resize('nn')"/>
  16. <div class="ne" @mousedown.prevent.stop="resize('ne')" @touchstart.prevent.stop="resize('ne')"/>
  17. <div class="ww" @mousedown.prevent.stop="resize('ww')" @touchstart.prevent.stop="resize('ww')"/>
  18. <div class="ee" @mousedown.prevent.stop="resize('ee')" @touchstart.prevent.stop="resize('ee')"/>
  19. <div class="sw" @mousedown.prevent.stop="resize('sw')" @touchstart.prevent.stop="resize('sw')"/>
  20. <div class="ss" @mousedown.prevent.stop="resize('ss')" @touchstart.prevent.stop="resize('ss')"/>
  21. <div class="se" @mousedown.prevent.stop="resize('se')" @touchstart.prevent.stop="resize('se')"/>
  22. </template>
  23. <div v-if="labelName" class="label">
  24. {{ labelName }}
  25. </div>
  26. </div>
  27. </template>
  28. <script>
  29. export default {
  30. name: "annotation-box",
  31. data: function(){
  32. return {
  33. clickCounter: 0,
  34. dblClickInterval: 200, // in ms
  35. colors: {
  36. default: '255, 255, 255',
  37. pipeline: '0, 0, 255',
  38. user: '255, 0, 0',
  39. other_user: '136, 0, 136',
  40. confirmed: '0, 180, 0',
  41. other_confirmed: '180, 180, 136',
  42. }
  43. };
  44. },
  45. props: [
  46. 'box',
  47. 'position',
  48. 'draggable',
  49. 'deletable',
  50. 'taggable',
  51. 'confirmable',
  52. 'zoomable',
  53. 'croppable',
  54. 'labels',
  55. 'shine'
  56. ],
  57. computed: {
  58. labelName: function () {
  59. if (!this.box || !this.box.label_id)
  60. return false;
  61. for (let label of this.labels) {
  62. if (label.identifier === this.box.label_id) {
  63. return label.name;
  64. }
  65. }
  66. return 'unknown';
  67. },
  68. style: function () {
  69. let color = this.color();
  70. return {
  71. backgroundColor: `rgba(${color}, ${this.shine ? 0.6 : 0.3})`,
  72. borderColor: `rgba(${color}, ${this.shine ? 0.8 : 0.4})`,
  73. top: this.position.y * 100 + '%',
  74. left: this.position.x * 100 + '%',
  75. width: this.position.w * 100 + '%',
  76. height: this.position.h * 100 + '%'
  77. }
  78. },
  79. },
  80. methods: {
  81. color: function() {
  82. let box = this.box;
  83. let current_user = this.$root.socket.username
  84. if(!box)
  85. return this.colors.default;
  86. switch(box.origin) {
  87. case 'user':
  88. for (let confirmer of box.confirmations)
  89. if (confirmer.confirming_user == current_user)
  90. return this.colors.confirmed;
  91. if (box.confirmations.length !== 0)
  92. return this.colors.other_confirmed;
  93. if (box.origin_user === current_user)
  94. return this.colors.user;
  95. return this.colors.other_user;
  96. case 'pipeline':
  97. return this.colors.pipeline;
  98. }
  99. },
  100. doubleLeftClick(event) {
  101. console.debug("double left click", event);
  102. this.$emit('interaction', "move-box");
  103. this.$emit('crop', this.box);
  104. },
  105. doubleRightClick(event) {
  106. console.debug("double right click", event);
  107. },
  108. singleLeftClick(event) {
  109. console.debug("single left click", event);
  110. if (this.deletable) {
  111. // TODO then / error
  112. this.$emit("remove", this.box.identifier)
  113. }
  114. else if (this.draggable) {
  115. // TODO then / error
  116. this.$emit('move', event, this.position, this.update);
  117. }
  118. else if (this.taggable) {
  119. // TODO then / error
  120. this.$emit("labelBox", this.box.identifier, this.taggable.identifier)
  121. this.selectMe();
  122. }
  123. else if (this.confirmable) {
  124. // TODO then / error
  125. this.$emit("confirm", this.box.identifier)
  126. }
  127. else if (this.zoomable) {
  128. this.$emit('zoom', this.position);
  129. }
  130. else if (this.croppable) {
  131. this.selectMe();
  132. }
  133. event.stopPropagation();
  134. },
  135. singleRightClick() {
  136. this.selectMe();
  137. this.$emit('labelSelector', this.box);
  138. },
  139. singleClick(event) {
  140. if (event.which == 1){
  141. this.singleLeftClick(event);
  142. }
  143. else if (event.which == 2){
  144. console.debug("single middle click");
  145. }
  146. else if (event.which == 3){
  147. this.singleRightClick(event);
  148. }
  149. },
  150. doubleClick(event) {
  151. if (event.which == 1){
  152. this.doubleLeftClick(event);
  153. }
  154. else if (event.which == 2){
  155. console.debug("double middle click");
  156. }
  157. else if (event.which == 3){
  158. this.doubleRightClick(event);
  159. }
  160. },
  161. selectMe(){
  162. this.$emit('crop', this.box);
  163. },
  164. click: function (event) {
  165. this.clickCounter++;
  166. event.stopPropagation();
  167. if (this.clickCounter == 1) {
  168. this.timer = setTimeout( () => {
  169. this.clickCounter = 0;
  170. this.singleClick(event);
  171. }, this.dblClickInterval);
  172. return;
  173. }
  174. clearTimeout(this.timer);
  175. this.clickCounter = 0;
  176. this.doubleClick(event);
  177. },
  178. resize: function (mode) {
  179. this.$emit('resize', mode, this.position, this.update);
  180. },
  181. update: function (value) {
  182. this.$emit("updateBox", this.box.identifier, value)
  183. }
  184. }
  185. }
  186. </script>
  187. <style scoped>
  188. .annotation-box {
  189. position: absolute;
  190. border: 1px solid transparent;
  191. }
  192. .label {
  193. position: absolute;
  194. bottom: 0.25rem;
  195. left: 50%;
  196. transform: translateX(-50%);
  197. font-size: 80%;
  198. color: whitesmoke;
  199. text-shadow: 0 0 1px #000000;
  200. }
  201. .nw {
  202. position: absolute;
  203. top: -0.3rem;
  204. left: -0.3rem;
  205. width: 0.6rem;
  206. height: 0.6rem;
  207. cursor: nwse-resize;
  208. /* background-color: blue; */
  209. }
  210. .nn {
  211. position: absolute;
  212. top: -0.3rem;
  213. left: 0.3rem;
  214. right: 0;
  215. height: 0.6rem;
  216. cursor: ns-resize;
  217. /* background-color: orangered; */
  218. }
  219. .ne {
  220. position: absolute;
  221. top: -0.3rem;
  222. right: -0.3rem;
  223. width: 0.6rem;
  224. height: 0.6rem;
  225. cursor: nesw-resize;
  226. /* background-color: cadetblue; */
  227. }
  228. .ww {
  229. position: absolute;
  230. top: 0.3rem;
  231. left: -0.3rem;
  232. width: 0.6rem;
  233. bottom: 0.3rem;
  234. cursor: ew-resize;
  235. /* background-color: fuchsia; */
  236. }
  237. .ee {
  238. position: absolute;
  239. top: 0.3rem;
  240. right: -0.3rem;
  241. width: 0.6rem;
  242. bottom: 0.3rem;
  243. cursor: ew-resize;
  244. /* background-color: midnightblue; */
  245. }
  246. .sw {
  247. position: absolute;
  248. bottom: -0.3rem;
  249. left: -0.3rem;
  250. width: 0.6rem;
  251. height: 0.6rem;
  252. cursor: nesw-resize;
  253. /* background-color: aqua; */
  254. }
  255. .ss {
  256. position: absolute;
  257. bottom: -0.3rem;
  258. left: 0.3rem;
  259. right: 0.3rem;
  260. height: 0.6rem;
  261. cursor: ns-resize;
  262. /* background-color: gold; */
  263. }
  264. .se {
  265. position: absolute;
  266. bottom: -0.3rem;
  267. right: -0.3rem;
  268. width: 0.6rem;
  269. height: 0.6rem;
  270. cursor: nwse-resize;
  271. /* background-color: brown; */
  272. }
  273. .draggable {
  274. cursor: url('~@/assets/icons/four-directions.svg') 8 8, move;
  275. }
  276. .deletable {
  277. cursor: url('~@/assets/icons/trash.svg') 8 8, not-allowed;
  278. }
  279. .taggable {
  280. cursor: url('~@/assets/icons/tag.svg') 8 8, alias;
  281. }
  282. .confirmable {
  283. cursor: url('~@/assets/icons/check.svg') 8 8, pointer;
  284. }
  285. .croppable {
  286. cursor: url('~@/assets/icons/info.svg') 8 8, move;
  287. }
  288. </style>