mobilechart.jsdraghammer.jschartjs-plugin-zoom

Using Hammer + chart + chart plugin zoom not work in mobile


I am encountering an issue with the drag functionality on mobile devices in a Chart.js chart that utilizes the chartjs-plugin-zoom and hammer.js. The chart works as expected on desktop browsers, but when viewed on mobile devices, dragging doesn't seem to work as intended.

Steps to Reproduce:

Open the chart on a mobile device.
Attempt to drag to pan the chart horizontally.
Observe that the drag functionality is not responsive.

Expected Behavior: I expect to be able to drag and pan the chart horizontally on mobile devices, just like on desktop browsers.

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.1.1/chartjs-plugin-zoom.min.js"></script>
<div class="container">
   <h3>Chart Example</h3>
  <div class="chart">
    <canvas id="cumulativeChart"></canvas>
  </div>
</div>
<script>const ctx = document.getElementById('cumulativeChart');
  const labels = [
  'Jul 13', 'Jul 14', 'Jul 15', 'Jul 16', 'Jul 17', 'Jul 18',
  'Jul 19', 'Jul 20', 'Jul 21', 'Jul 22', 'Jul 23', 'Jul 24',
  'Jul 25', 'Jul 26', 'Jul 27', 'Jul 28', 'Jul 29', 'Jul 30',
  'Jul 31', 'Aug 01', 'Aug 02', 'Aug 03', 'Aug 04', 'Aug 05',
  'Aug 06', 'Aug 07', 'Aug 08', 'Aug 09', 'Aug 10', 'Aug 11',
  ];
  
  const data = {
  labels: labels,
  datasets: [{
      label: 'Cumulative Data',
      borderColor: '#3a96fd',
      borderWidth: 2,
      data: [
          0.13299527, 0.16131551, 0.18795605, 0.20597476, 0.22560615, 0.23797296,
          0.25541133, 0.28006363, 0.30473082, 0.32418763, 0.33880094, 0.35331442,
          0.36290294, 0.38035896, 0.40230393, 0.42181523, 0.43855241, 0.4481249,
          0.4696107, 0.48147319, 0.4958352, 0.52931651, 0.55098815, 0.56771866,
          0.58238203, 0.60083731, 0.61222939, 0.6262777, 0.64303029, 0.65866165,
      ],
      pointRadius: 4,
      pointBackgroundColor: '#3a96fd',
      fill: true
  }]};
  
  const config = {
  type: 'line',
  data: data,
  options: {
      responsive: true,
      scales: {
          y: {
              grid: {
                  color: 'rgba(128,151,177, 0.3)',
                  borderDash: [3, 3],
                  drawTicks: false,
                  borderColor: '#424b5f',
              },
              ticks: {
                  align: "center",
                  padding: 10,
              },
          },
          x: {
              grid: {
                  color: 'rgba(128,151,177, 0.3)',
                  borderDash: [3, 5],
                  drawTicks: false,
                  borderColor: '#424b5f'
              },
              ticks: {
                  align: "center",
                  padding: 10,
              }
          }
      },
      plugins: {
          legend: {
              display: false
          },
          tooltip: {
              usePointStyle: true,
          },
          zoom: {
              zoom: {
                  wheel: {
                      enabled: true,
                  },
                  pinch: {
                      enabled: true
                  },
                  drag: {
                      enabled: true
                  },
                  mode: 'x',
              },
              pan: {
                  enabled: true,
                  mode: 'x',
              },
              limits: {
                  x: {
                      minRange: 3
                  },
              },
          }
      }
  }
  };
  
  const myChart = new Chart(
  ctx,
  config
  );</script>

Work perfectly in desktop, but in mobile just work the pinch zoom, drag zoom not work, there is any solution?, thanks

I have already checked the official Chart.js and chartjs-plugin-zoom documentation, but couldn't find a solution. Any insights or suggestions on how to enable drag functionality on mobile devices would be greatly appreciated.


Solution

  • Drag-zoom is not implemented for mobile; I suppose that it's considered something that mobile users are not accustomed to do, since they're all about pinching :). It is also possible that it might interfere with other usual gestures, like scrolling.

    If you want to test drag-zoom on mobile, it seems rather simple to map the mobile events touchstart -> mousedown; touchmove -> mousemove and touchend -> mouseup. I used this post for inspiration. Please check the comments therein for possible pitfalls (an important difference is that I map the events only for the canvas element, not for the whole document.

    const ctx = document.getElementById('cumulativeChart');
    const labels = [
        'Jul 13', 'Jul 14', 'Jul 15', 'Jul 16', 'Jul 17', 'Jul 18',
        'Jul 19', 'Jul 20', 'Jul 21', 'Jul 22', 'Jul 23', 'Jul 24',
        'Jul 25', 'Jul 26', 'Jul 27', 'Jul 28', 'Jul 29', 'Jul 30',
        'Jul 31', 'Aug 01', 'Aug 02', 'Aug 03', 'Aug 04', 'Aug 05',
        'Aug 06', 'Aug 07', 'Aug 08', 'Aug 09', 'Aug 10', 'Aug 11',
    ];
    
    const data = {
        labels: labels,
        datasets: [{
            label: 'Cumulative Data',
            borderColor: '#3a96fd',
            borderWidth: 2,
            data: [
                0.13299527, 0.16131551, 0.18795605, 0.20597476, 0.22560615, 0.23797296,
                0.25541133, 0.28006363, 0.30473082, 0.32418763, 0.33880094, 0.35331442,
                0.36290294, 0.38035896, 0.40230393, 0.42181523, 0.43855241, 0.4481249,
                0.4696107, 0.48147319, 0.4958352, 0.52931651, 0.55098815, 0.56771866,
                0.58238203, 0.60083731, 0.61222939, 0.6262777, 0.64303029, 0.65866165,
            ],
            pointRadius: 4,
            pointBackgroundColor: '#3a96fd',
            fill: true
        }]
    };
    
    const config = {
        type: 'line',
        data: data,
        options: {
            responsive: true,
            scales: {
                y: {
                    grid: {
                        color: 'rgba(128,151,177, 0.3)',
                        borderDash: [3, 3],
                        drawTicks: false,
                        borderColor: '#424b5f',
                    },
                    ticks: {
                        align: "center",
                        padding: 10,
                    },
                },
                x: {
                    grid: {
                        color: 'rgba(128,151,177, 0.3)',
                        borderDash: [3, 5],
                        drawTicks: false,
                        borderColor: '#424b5f'
                    },
                    ticks: {
                        align: "center",
                        padding: 10,
                    }
                }
            },
            plugins: {
                legend: {
                    display: false
                },
                tooltip: {
                    usePointStyle: true,
                },
                zoom: {
                    zoom: {
                        wheel: {
                            enabled: true,
                        },
                        pinch: {
                            enabled: true
                        },
                        drag: {
                            enabled: true
                        },
                        mode: 'x',
                    },
                    pan: {
                        enabled: true,
                        mode: 'x',
                    },
                    limits: {
                        x: {
                            minRange: 3
                        },
                    },
                }
            }
        }
    };
    
    const myChart = new Chart(
        ctx,
        config
    );
    
    function touchHandler(event){
        const touches = event.changedTouches,
            first = touches[0],
            type = event.type.replace(/^touch/, "mouse").replace(/start$/, "down").replace(/end$/, "up");
            // touchstart -> mousedown; touchmove -> mousemove; touchend -> mouseup
    
        const simulatedEvent = new Event(type, {
            bubbles: true, cancelable: true
        })
        simulatedEvent.screenX = first.screenX;
        simulatedEvent.screenY = first.screenY;
        simulatedEvent.clientX = first.clientX;
        simulatedEvent.clientY = first.clientY;
    
        first.target.dispatchEvent(simulatedEvent);
        //event.preventDefault();
    }
    
    myChart.canvas.addEventListener("touchstart", touchHandler, true);
    myChart.canvas.addEventListener("touchmove", touchHandler, true);
    myChart.canvas.addEventListener("touchend", touchHandler, true);
    myChart.canvas.addEventListener("touchcancel", touchHandler, true);
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.1.1/chartjs-plugin-zoom.min.js"></script>
    <div class="container">
        <h3>Chart Example</h3>
        <div class="chart">
            <canvas id="cumulativeChart"></canvas>
        </div>
    </div>