annotation-box.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <template>
  2. <div class="annotation-box" :style="style" @mousedown.stop.prevent="moveSelf" @touchstart.stop.prevent="moveSelf">
  3. <div class="nw" @mousedown.stop.prevent="resizeSelf('nw')" @touchstart.stop.prevent="resizeSelf('nw')"></div>
  4. <div class="nn" @mousedown.stop.prevent="resizeSelf('nn')" @touchstart.stop.prevent="resizeSelf('nn')"></div>
  5. <div class="ne" @mousedown.stop.prevent="resizeSelf('ne')" @touchstart.stop.prevent="resizeSelf('ne')"></div>
  6. <div class="ww" @mousedown.stop.prevent="resizeSelf('ww')" @touchstart.stop.prevent="resizeSelf('ww')"></div>
  7. <div class="buttons">
  8. <template v-if="!immutable">
  9. <div v-if="position.label" class="label-text">{{ currentLabel.name }}</div>
  10. <img v-if="confirmationButton"
  11. alt="confirm" src="@/assets/icons/check.svg"
  12. @click.stop.prevent="confirmSelf"
  13. @touchstart.stop.prevent="confirmSelf">
  14. <img v-if="labelButton"
  15. alt="label" src="@/assets/icons/tag.svg"
  16. @click.stop.prevent="showLabelSelection = true"
  17. @touchstart.stop.prevent="showLabelSelection = true">
  18. <img alt="delete" src="@/assets/icons/trash.svg"
  19. @click.stop.prevent="deleteSelf"
  20. @touchstart.stop.prevent="deleteSelf">
  21. </template>
  22. </div>
  23. <select v-if="!immutable && showLabelSelection"
  24. @touchstart.stop @mousedown.stop
  25. @change="labelSelf"
  26. @focusout="showLabelSelection = false">
  27. <option value="">None</option>
  28. <option v-for="label in labelList"
  29. :key="label.id"
  30. :value="label.id"
  31. :selected="label === currentLabel">
  32. {{ label.name }}
  33. </option>
  34. </select>
  35. <div class="ee" @mousedown.stop.prevent="resizeSelf('ee')" @touchstart.stop.prevent="resizeSelf('ee')"></div>
  36. <div class="sw" @mousedown.stop.prevent="resizeSelf('sw')" @touchstart.stop.prevent="resizeSelf('sw')"></div>
  37. <div class="ss" @mousedown.stop.prevent="resizeSelf('ss')" @touchstart.stop.prevent="resizeSelf('ss')"></div>
  38. <div class="se" @mousedown.stop.prevent="resizeSelf('se')" @touchstart.stop.prevent="resizeSelf('se')"></div>
  39. </div>
  40. </template>
  41. <script>
  42. export default {
  43. name: "annotation-box",
  44. props: ['type', 'image', 'position', 'socket', 'boxUrl', 'labels'],
  45. data: function () {
  46. return {
  47. showLabelSelection: false
  48. }
  49. },
  50. computed: {
  51. immutable: function () {
  52. return !this.type;
  53. },
  54. backgroundColor: function () {
  55. switch (this.type) {
  56. case 'user':
  57. return 'rgba(255, 0, 0, 0.3)';
  58. case 'pipeline':
  59. return 'rgba(0, 0, 255, 0.3)';
  60. default:
  61. return 'rgba(255, 255, 255, 0.3)';
  62. }
  63. },
  64. confirmationButton: function () {
  65. return this.type === 'pipeline';
  66. },
  67. labelButton: function () {
  68. return this.type !== 'pipeline';
  69. },
  70. labelList: function () {
  71. return Object.keys(this.labels).map(key => this.labels[key]);
  72. },
  73. currentLabel: function () {
  74. return this.labels[this.position.label];
  75. },
  76. style: function () {
  77. const left = this.image.left;
  78. const top = this.image.top;
  79. const width = this.image.width;
  80. const height = this.image.height;
  81. return {
  82. left: (left + this.position.x * width) + 'px',
  83. top: (top + this.position.y * height) + 'px',
  84. width: (this.position.w * width) + 'px',
  85. height: (this.position.h * height) + 'px',
  86. backgroundColor: this.backgroundColor
  87. }
  88. }
  89. },
  90. methods: {
  91. confirmSelf: function () {
  92. this.updateSelf(this.position);
  93. },
  94. labelSelf: function (event) {
  95. const options = event.target.options;
  96. const option = options[options.selectedIndex];
  97. const value = option.value;
  98. if (value)
  99. this.position.label = value;
  100. else
  101. delete this.position.label;
  102. this.updateSelf(this.position);
  103. this.showLabelSelection = false;
  104. },
  105. deleteSelf: function () {
  106. this.socket.post(this.boxUrl, {delete: true});
  107. },
  108. moveSelf: function (event) {
  109. this.$emit('move', event, this.position, this.updateSelf);
  110. },
  111. resizeSelf: function (mode) {
  112. this.$emit('resize', this.position, mode, this.updateSelf);
  113. },
  114. updateSelf: function (value) {
  115. if ('label' in this.position && !('label' in value))
  116. value.label = this.position.label;
  117. this.socket.post(this.boxUrl, value);
  118. }
  119. }
  120. }
  121. </script>
  122. <style scoped>
  123. .annotation-box {
  124. position: absolute;
  125. display: grid;
  126. grid-template-rows: 10px auto 10px;
  127. grid-template-columns: 10px auto 10px;
  128. }
  129. .buttons {
  130. display: flex;
  131. justify-content: center;
  132. align-items: center;
  133. position: relative;
  134. }
  135. img {
  136. width: 2rem;
  137. filter: invert(1);
  138. opacity: 0.9;
  139. }
  140. .nn, .ss {
  141. cursor: ns-resize;
  142. }
  143. .ww, .ee {
  144. cursor: ew-resize;
  145. }
  146. .ne, .sw {
  147. cursor: nesw-resize;
  148. }
  149. .nw, .se {
  150. cursor: nwse-resize;
  151. }
  152. select {
  153. position: absolute;
  154. left: 0;
  155. top: 0;
  156. width: 100%;
  157. height: 100%;
  158. }
  159. .label-text {
  160. position: absolute;
  161. bottom: 0;
  162. color: whitesmoke;
  163. font-size: 80%;
  164. }
  165. </style>