annotated-image.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <template>
  2. <div class="annotated-image" v-on="events">
  3. <img alt="media" ref="image" draggable="false"
  4. :src="mediaUrl" :srcset="mediaUrlSet" :sizes="mediaUrlSizes"/>
  5. <div style="position: absolute; top: 0.5rem; left: 0.5rem">{{ data }}</div>
  6. <annotation-box v-if="current"
  7. :image="image"
  8. :position="current"/>
  9. <annotation-box v-for="result in data.predictionResults"
  10. v-bind:key="result"
  11. :image="image"
  12. :position="result"/>
  13. </div>
  14. </template>
  15. <script>
  16. import AnnotationBox from "@/components/media/annotation-box";
  17. export default {
  18. name: "annotated-image",
  19. components: {AnnotationBox},
  20. props: ['project', 'data', 'socket'],
  21. mounted: function() {
  22. window.addEventListener("resize", this.resizeEvent);
  23. if (this.$refs.image.complete)
  24. this.resizeEvent();
  25. else
  26. this.$refs.image.addEventListener('load', this.resizeEvent);
  27. },
  28. destroyed() {
  29. window.removeEventListener("resize", this.resizeEvent);
  30. },
  31. watch: {
  32. data: function() {
  33. this.current = false;
  34. this.resizeEvent();
  35. }
  36. },
  37. data: function() {
  38. return {
  39. sizes: [600, 800, 1200, 1600, 2000, 3000],
  40. image: {
  41. left: 0,
  42. top: 0,
  43. width: 0,
  44. height: 0
  45. },
  46. start: false,
  47. current: false
  48. }
  49. },
  50. computed: {
  51. mediaUrl: function() {
  52. return this.socket.media(this.project.id, this.data.id);
  53. },
  54. mediaUrlSet: function() {
  55. return this.sizes.map(e => this.mediaUrl + '/' + e + ' ' + e + 'w').join(',');
  56. },
  57. mediaUrlSizes: function() {
  58. return this.sizes.map(e => '(max-width: ' + e + 'px) ' + e + 'px').join(',');
  59. },
  60. events: function() {
  61. return {
  62. 'touchstart': this.press,
  63. 'touchmove': this.track,
  64. 'touchend': this.release,
  65. 'mousedown': this.press,
  66. 'mousemove': this.track,
  67. 'mouseup': this.release,
  68. 'dragstart': e => e.stopPropagation()
  69. }
  70. }
  71. },
  72. methods: {
  73. resizeEvent: function() {
  74. const element = this.$refs.image.getBoundingClientRect();
  75. const parent = this.$refs.image.parentElement.getBoundingClientRect();
  76. this.image.left = element.x - parent.x;
  77. this.image.top = element.y - parent.y;
  78. this.image.width = element.width;
  79. this.image.height = element.height;
  80. },
  81. get: function(event) {
  82. if ('clientX' in event)
  83. return event;
  84. if ('touches' in event && event.touches.length > 0)
  85. return event.touches[0];
  86. if ('changedTouches' in event && event.changedTouches.length > 0)
  87. return event.changedTouches[0];
  88. },
  89. getX: function(event) {
  90. const bcr = this.$refs.image.getBoundingClientRect();
  91. return (this.get(event).clientX - bcr.left) / bcr.width;
  92. },
  93. getY: function(event) {
  94. const bcr = this.$refs.image.getBoundingClientRect();
  95. return (this.get(event).clientY - bcr.top) / bcr.height;
  96. },
  97. buildRectangle: function(x1, y1, x2, y2) {
  98. const lx = Math.max(Math.min(x1, x2), 0);
  99. const hx = Math.min(Math.max(x1, x2), 1);
  100. const ly = Math.max(Math.min(y1, y2), 0);
  101. const hy = Math.min(Math.max(y1, y2), 1);
  102. return {
  103. x: lx,
  104. y: ly,
  105. w: hx - lx,
  106. h: hy - ly
  107. }
  108. },
  109. press: function(event) {
  110. this.start = {
  111. x: this.getX(event),
  112. y: this.getY(event)
  113. };
  114. },
  115. track: function(event) {
  116. if (this.start) {
  117. const x = this.getX(event);
  118. const y = this.getY(event);
  119. this.current = this.buildRectangle(this.start.x, this.start.y, x, y);
  120. }
  121. },
  122. release: function(event) {
  123. console.log('release', event);
  124. // TODO send to server
  125. this.start = false;
  126. // this.current = this.buildRectangle(this.start.x, this.start.y, this.getX(event), this.getY(event));
  127. }
  128. }
  129. }
  130. </script>
  131. <style scoped>
  132. img {
  133. max-width: 100%;
  134. max-height: 100%;
  135. }
  136. </style>