Canvas only draws outside the table, with the exception of the first one

I am facing an issue with placing multiple signature pads inside table cells.

Only the first canvas gets drawn, while the others remain blank. I have checked the mouse/touch events. The events are triggered (up/down/move) and the draw function is called, but the canvas does not get drawn.

On using toDataURL, the result is just a white image.

The canvas outside the table works perfectly fine and gets drawn as expected.

I have searched for documentation on this issue but couldn't find any. Any help would be appreciated.

Here is the code snippet:

// This is the draw function
const init = function (el) {
  let context = el.getContext("2d");
  context.strokeStyle = "#df4b26";
  context.lineJoin = "round";
  context.lineWidth = 2;

  let offset = {
    left: el.offsetLeft,
    top: el.offsetTop,
  }

  let down = false
  let points = []

  // Rest of the code for event handlers and Vue component
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
 <div id="app">
    <table>
      <tr>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
      </tr>
      <tr>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
      </tr>
    </table>
    <signature :width="200" :height="200"></signature>
    <signature :width="200" :height="200"></signature>
  </div>

Answer №1

There was an issue with the offset.

I mistakenly extracted the offset coordinates of the canvas, but it should refer to the border box of an element relative to the offsetParent.

The correct method to use is Element.getClientRects()

The code below has been adjusted.

// This is the draw function
const initialize = function (element) {
  let context = element.getContext("2d");
  context.strokeStyle = "#df4b26";
  context.lineJoin = "round";
  context.lineWidth = 2;

  let clientRect = element.getClientRects()

   let offset = {
     left : clientRect[0].left,
     top : clientRect[0].top,
   }

  let down = false
  let points = []

  function draw() {
    context.beginPath();

    for (let i = 0; i < points.length - 1; i++) {
      const p = points[i]
      const pn = points[i + 1]
      if (pn.x === p.x && p.y === pn.y && p.first && pn.last) {
        context.arc(pn.x, pn.y, 1, 0, 2 * Math.PI);
        context.fill()
      } else {
        context.moveTo(p.x, p.y)
        context.lineTo(pn.x, pn.y)
      }
    }

    context.stroke();
    context.closePath();
  }

  function addPoint(ev, setting) {
    setting = setting || {}

    let p = {
      x: ev.clientX - offset.left,
      y: ev.clientY - offset.top,
      ...setting,
    }
    points.push(p)
  }

  function downHandler(event) {
    down = true
    addPoint(event, {first: true})
    event.preventDefault()
  }

  function moveHandler(event) {
    if (!down) {
      return
    }

    addPoint(event, {drag: true})
    draw()
    event.preventDefault()
  }

  function upHandler(event) {
    down = false
    addPoint(event, {last: true})
    draw()
    points.splice(0, points.length)
    event.preventDefault()
  }

  element.addEventListener("pointerdown", downHandler, false);
  element.addEventListener("pointermove", moveHandler, false);
  element.addEventListener("pointerup", upHandler, false);
  element.style['touch-action'] = 'none'
}

Vue.component('signature', {
  props: {
    width: {
      type: Number,
      required: true,
    },
    height: {
      type: Number,
      required: true,
    },
    customStyle: {
      type: Object,
      default() {
        return {}
      },
    },
  },
  mounted() {
    initialize(this.$refs.signaturePadCanvas)
  },
  render(createElement) {
    const {width, height, customStyle} = this;

    return createElement(
      'div',
      {
        style: {
          width: `${width}px`,
          height: `${height}px`,
          ...customStyle,
        },
      },
      [
        createElement('canvas', {
          attrs: {
            width: width,
            height: height,
          },
          style: {
            width: '100%',
            height: '100%',
            'touch-action': 'none',
            'background': 'gray',
          },
          ref: 'signaturePadCanvas',
        }),
      ],
    );
  },
})

new Vue({
  el: '#app',
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
 <div id="app">
    <table>
      <tr>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
      </tr>
      <tr>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
        <td>
          <signature :width="200" :height="200"></signature>
        </td>
      </tr>
    </table>
    <signature :width="200" :height="200"></signature>
    <signature :width="200" :height="200"></signature>
  </div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to have a span update its color upon clicking a link?

I have 4 Spans and I'm looking to create an interactive feature where clicking a link in a span changes the color of that span. Additionally, when another link in a different span is clicked, the color of that span changes while reverting the previous ...

Learn how to dynamically populate a select2 dropdown with values retrieved from a database using JavaScript or jQuery

I am currently working on an edit form where I need to retrieve data from a MySql database using Ajax. The form includes two select2 dropdowns and I want the fetched data to be displayed inside those dropdowns. For example: Let's say I have created a ...

problem of keeping behat/selenium browser open after execution

I am attempting to execute the behat/selenium test with Chrome browser by running the following feature scenario. I would like to keep the browser window open instead of closing the Chrome immediately. Even though I have implemented the iWaitForSeconds ste ...

When utilizing div.load, jQuery and other JavaScript sources may not be defined

When you load a page using jQuery load: $("#myDiv").load("page.php",{foo: bar}); The head section is included in the index: <head> <script src="/assets/plugins/jQuery/jQuery-2.1.4.min.js"></script> <script src="/assets/plugi ...

How to manage access controls for the Admin Page in node.js

Hi everyone, I am facing an issue with my admin page access control on my application. I want to restrict access to all users except the one named John. In my app.js file, here is what I have: app.get('/admin', requireLogin, function(req, res){ ...

Fetching images using node.js via URL

I must apologize for my lack of knowledge about node.js. I am trying to read an image from a URL and resize it using sharp. Currently, my code only works for reading local images. For instance, I would like to read the following image from a URL: url= ...

Guide to Wrapping Inner or Wrapping All Elements Except for the Initial Div Class

I am looking to enclose all the elements below "name portlet-title" without including other elements within the "div class name portlet-title". I have attempted the following methods: 1) $("div.item").wrapAll('<div class="portlet-body"></div ...

Manipulate text with jQuery

Is there a way to remove 'http://' or 'https://' from text using javascript? I am looking for regex solutions as well. This is what I have tried so far: JSFIDDLE HTML: <div class="string"></div> JS: $text = $('.s ...

Is there a way to emphasize text within a string of object array items?

I am currently utilizing the data provided below to pass as props in React. The functionality is working smoothly, but I have a specific requirement to only emphasize the words "target audience" within the text property. Is there a feasible way to achieve ...

Leveraging the power of react routes for efficient navigation within a react-based application

I am currently facing an issue with setting up routes for a basic react application using react-router. The given routes don't seem to match and the switch defaults to displaying the 404 page. Below is the code for the routes: import { BrowserRout ...

Perform validation on input by monitoring checkbox changes in Angular 4

I am currently working on a project where I need to loop through an array of key values in order to create a list of checkboxes, each accompanied by a disabled input field as a sibling. The requirement is that upon checking a checkbox, the corresponding in ...

Navigate to a new route in Vue Router and pass parameters as part of the navigation

I have a component that handles programmatic routing based on external data retrieved in the App.vue component and passed down to child components as props. In the child component, the external data is accessed like this: props: { externalData: Array ...

Using scope in ng-style to manipulate a portion of a CSS property value in Angular

I've been attempting to add a border using ng-style, but I'm struggling to figure out how to concatenate the value from the scope variable. None of the methods below seem to be working for me: <div ng-style="{'border-top' :'1p ...

Concealing a Vuejs dropdown when clicking outside of the component

I am currently working on a Vuejs project where I am creating a menu component. This menu consists of 2 dropdowns, and I have already implemented some methods and used Vue directives to ensure that when one dropdown is clicked, the other hides and vice ver ...

Utilize selenium IDE to retrieve a specific portion of text and save it as a variable

I am trying to figure out how to extract the number 694575 from a text using Selenium Ide and store it in a variable for future use. Here is the text I am working with: <div class="loginBoxTitle">Edit Exhibition Centre - 694575, Exhibition Center1&l ...

Sharing React components in projects

Two of my upcoming projects will require sharing components such as headers, footers, and inputs. To facilitate this, I have moved these shared components into a separate repository, which has been working well so far. In the common repository, I have set ...

Executing TipTap commands from a script tag in Vue 3: A step-by-step guide

One of the components I'm working with includes the following: <button @click="$refs.image.click(); editor.chain().focus().setImage({ src: state.image }).run()"></button> <input type="file" ref="image" sty ...

What is the most effective way to send multiple values through the <option> value attribute?

Currently, I am in the process of creating an online shopping item page. To display all the necessary information about an item (such as price, image, name, etc.), I use the 'item_id' to loop through a database containing item info. Additionall ...

Exploring the World of Images with Javascript

I'm currently working on creating a slideshow using HTML and JavaScript. My goal is to have the image change each time I press a button that I've established (see code below). After reviewing my code multiple times, I still can't figure out ...

Adding the AJAX response to the specified element's ID

I'm having trouble inserting radio buttons into a <div> on my page using an AJAX response. The code I have looks like this: // AJAX form for updating tests after category selection $('#categories').change(function(){ category_id ...