Ver código fonte

Add pointer events to interactive image (#2745)

* Add pointer events to interactive image

* Refactor interactive_image.js and interactive_image.py

* add event listeners for pointer events

* Add console log for pointer events in interactive_image.js

* improve documentation

* fixed the position of the image_X and image_y

* removed debug statements

* refactoring

* add element ID

---------

Co-authored-by: Falko Schindler <falko@zauberzeug.com>
frankvp 1 ano atrás
pai
commit
748af48609

+ 27 - 5
nicegui/elements/interactive_image.js

@@ -10,7 +10,7 @@ export default {
         v-on="onUserEvents"
         v-on="onUserEvents"
         draggable="false"
         draggable="false"
       />
       />
-      <svg style="position:absolute;top:0;left:0;pointer-events:none" :viewBox="viewBox">
+      <svg ref="svg" style="position:absolute;top:0;left:0;pointer-events:none" :viewBox="viewBox">
         <g v-if="cross" :style="{ display: showCross ? 'block' : 'none' }">
         <g v-if="cross" :style="{ display: showCross ? 'block' : 'none' }">
           <line :x1="x" y1="0" :x2="x" y2="100%" :stroke="cross === true ? 'black' : cross" />
           <line :x1="x" y1="0" :x2="x" y2="100%" :stroke="cross === true ? 'black' : cross" />
           <line x1="0" :y1="y" x2="100%" :y2="y" :stroke="cross === true ? 'black' : cross" />
           <line x1="0" :y1="y" x2="100%" :y2="y" :stroke="cross === true ? 'black' : cross" />
@@ -45,6 +45,18 @@ export default {
     };
     };
     this.$refs.img.addEventListener("load", handle_completion);
     this.$refs.img.addEventListener("load", handle_completion);
     this.$refs.img.addEventListener("error", handle_completion);
     this.$refs.img.addEventListener("error", handle_completion);
+    for (const event of [
+      "pointermove",
+      "pointerdown",
+      "pointerup",
+      "pointerover",
+      "pointerout",
+      "pointerenter",
+      "pointerleave",
+      "pointercancel",
+    ]) {
+      this.$refs.svg.addEventListener(event, (e) => this.onPointerEvent(event, e));
+    }
   },
   },
   updated() {
   updated() {
     this.compute_src();
     this.compute_src();
@@ -79,12 +91,12 @@ export default {
       this.$emit("loaded", { width: this.loaded_image_width, height: this.loaded_image_height, source: e.target.src });
       this.$emit("loaded", { width: this.loaded_image_width, height: this.loaded_image_height, source: e.target.src });
     },
     },
     onMouseEvent(type, e) {
     onMouseEvent(type, e) {
-      const width = this.src ? this.loaded_image_width : this.size ? this.size[0] : 1;
-      const height = this.src ? this.loaded_image_height : this.size ? this.size[1] : 1;
+      const imageWidth = this.src ? this.loaded_image_width : this.size ? this.size[0] : 1;
+      const imageHeight = this.src ? this.loaded_image_height : this.size ? this.size[1] : 1;
       this.$emit("mouse", {
       this.$emit("mouse", {
         mouse_event_type: type,
         mouse_event_type: type,
-        image_x: (e.offsetX * width) / e.target.clientWidth,
-        image_y: (e.offsetY * height) / e.target.clientHeight,
+        image_x: (e.offsetX * imageWidth) / this.$refs.img.clientWidth,
+        image_y: (e.offsetY * imageHeight) / this.$refs.img.clientHeight,
         button: e.button,
         button: e.button,
         buttons: e.buttons,
         buttons: e.buttons,
         altKey: e.altKey,
         altKey: e.altKey,
@@ -93,6 +105,16 @@ export default {
         shiftKey: e.shiftKey,
         shiftKey: e.shiftKey,
       });
       });
     },
     },
+    onPointerEvent(type, e) {
+      const imageWidth = this.src ? this.loaded_image_width : this.size ? this.size[0] : 1;
+      const imageHeight = this.src ? this.loaded_image_height : this.size ? this.size[1] : 1;
+      this.$emit(`svg:${type}`, {
+        type: type,
+        element_id: e.target.id,
+        image_x: (e.offsetX * imageWidth) / this.$refs.svg.clientWidth,
+        image_y: (e.offsetY * imageHeight) / this.$refs.svg.clientHeight,
+      });
+    },
   },
   },
   computed: {
   computed: {
     onCrossEvents() {
     onCrossEvents() {

+ 1 - 0
nicegui/elements/interactive_image.py

@@ -55,6 +55,7 @@ class InteractiveImage(SourceElement, ContentElement, component='interactive_ima
         :param on_mouse: callback for mouse events (contains image coordinates `image_x` and `image_y` in pixels)
         :param on_mouse: callback for mouse events (contains image coordinates `image_x` and `image_y` in pixels)
         :param events: list of JavaScript events to subscribe to (default: `['click']`)
         :param events: list of JavaScript events to subscribe to (default: `['click']`)
         :param cross: whether to show crosshairs or a color string (default: `False`)
         :param cross: whether to show crosshairs or a color string (default: `False`)
+        :param on_pointer: callback for pointer events (contains image coordinates `image_x` and `image_y` in pixels, and `type` of the event)
         """
         """
         super().__init__(source=source, content=content)
         super().__init__(source=source, content=content)
         self._props['events'] = events
         self._props['events'] = events

+ 22 - 0
website/documentation/content/interactive_image_documentation.py

@@ -70,4 +70,26 @@ def crosshairs():
     ui.interactive_image('https://picsum.photos/id/565/640/360', cross='red')
     ui.interactive_image('https://picsum.photos/id/565/640/360', cross='red')
 
 
 
 
+@doc.demo('SVG events', '''
+    You can subscribe to events of the SVG elements by using the `on` method with an "svg:" prefix.
+    Make sure to set `pointer-events="all"` for the SVG elements you want to receive events from.
+
+    Currently the following SVG events are supported:
+
+    - pointermove
+    - pointerdown
+    - pointerup
+    - pointerover
+    - pointerout
+    - pointerenter
+    - pointerleave
+    - pointercancel
+''')
+def svg_content():
+    ui.interactive_image('https://picsum.photos/id/565/640/360', cross=True, content='''
+        <rect id="A" x="85" y="70" width="80" height="60" fill="none" stroke="red" pointer-events="all" cursor="pointer" />
+        <rect id="B" x="180" y="70" width="80" height="60" fill="none" stroke="red" pointer-events="all" cursor="pointer" />
+    ''').on('svg:pointerdown', lambda e: ui.notify(f'SVG clicked: {e.args}'))
+
+
 doc.reference(ui.interactive_image)
 doc.reference(ui.interactive_image)