Canvas Graphic Interaction

Based on the line chart drawn in the last article “Canvas Tour Series —-(II)” (if you have not read the previous article, you can simply understand that this chapter does not introduce the drawing of the folding line chart), the effect to be realized is that when the mouse moves to a point, the corresponding interactive effect will appear. Gitee source address, Github source address

Hover effect

To achieve the effect as shown in the figure above, the first problem to be solved is node selection. How to judge that the current mouse point has a node? The method adopted this time is to read the position of the mouse relative to the current position relative to the canvas, and then scan the position of the drawing point to determine whether the current position is near a certain point.

/** * Initializes the graph *@param {string} Id Canvas ID */
function initChart(id) {
 // Omit other code......
  this._drawedImg = []
  /** * hover effect */
  let self = this
  function bindHover() {
    self.canvas.onmousemove = function(e) {
    	// Mouse position relative to canvas
      let offsetX = e.clientX - self.canvas.clientLeft
      let offsetY = e.clientY - self.canvas.clientTop
      let points = self._getPoints().slice()
      let flag = true
      for (let i in points) {
        // Determine whether the mouse is near the point
        if (Math.abs(points[i].x - offsetX) < 10 && Math.abs(points[i].y - offsetY) < 10) {
        	flag = false
         	// Mouse over the data point......}}if (flag) {
       	// The mouse position is not on the data point......
      }
    }
  }
  bindHover()
}
Copy the code

Since adding the hover effect is performed only once and does not want to be accessed externally, it is defined as an internal function call.

Draw hover effect

We can now determine whether the mouse is currently near the data point and then draw the corresponding effect.

// Draw horizontal guides
self.context.beginPath()
self.context.moveTo(self.chartZone[0], points[i].y)
self.context.lineTo(points[i].x, points[i].y)
self.context.setLineDash([8.8])
self.context.strokeWidth = 4
self.context.strokeStyle = '#1abc9c'
self.context.stroke()
self.context.setLineDash([])
// Draw the hover effect for the point
self.context.beginPath()
self.context.moveTo(points[i].x + 6, points[i].y)
self.context.arc(points[i].x, points[i].y, 6.0.2 * Math.PI, false)
self.context.fillStyle = '#ffffff'
self.context.fill()
self.context.strokeStyle = '#1abc9c'
self.context.strokeWidth = 4
self.context.stroke()
// Draw the prompt text
let texts = [`x:${self.xAxisLable[i]}`.`y:${self.data[i]}`]
let textWidth = self.context.measureText(texts[0]).width
self.context.fillStyle = 'rgba (0, 0, 0, 0.6)'
self.context.fillRect(
  points[i].x + 10,
  points[i].y - 20,
  textWidth + 30.44
  ) // Create a translucent background for the text
self.context.font = 'italic 16px blod'
self.context.fillStyle = '#ffffff'
self.context.textAlign = 'left'
texts.forEach(function(text, idx) {
  self.context.fillText(text,
    points[i].x + 15,
    points[i].y + idx * 16)})Copy the code

After drawing, we can get the effect of the mouse moving over the data point

Off-screen Canvas technology

After the hover effect is drawn, you may encounter a situation where the hover effect appears every time the mouse moves over the data point, and the hover effect is not cleared.

So every time the mouse leaves the data point, we need to restore the image to its original appearance. We can choose to empty the canvas and paint the image again. Or use off-screen canvas technology to restore the image.

In fact, the off-screen canvas is very simple, which is to save the image of the canvas and draw the saved image on the canvas again when needed.

Therefore, we need to use the toDataURL method to store the canvas image, and use the drawImage method to draw the image again when the mouse leaves near the data point.

function bindHover() {
  self.canvas.onmousemove = function(e) {
    let offsetX = e.clientX - self.canvas.clientLeft
    let offsetY = e.clientY - self.canvas.clientTop
    let points = self._getPoints().slice()
    let flag = true
    for (let i in points) {
      // Determine whether the mouse is near the point
      if (Math.abs(points[i].x - offsetX) < 10 && Math.abs(points[i].y - offsetY) < 10) {
        flag = false
        // If there is no saved image, save the current image and then draw the hover effect
        if (self._drawedImg.length === 0) {
          // Save the image
          self._drawedImg.push(self.canvas.toDataURL("image/png"))
          // Draw and over effects......
        }
        break}}// If the mouse is not near the data point, take out the stored image and draw it
    if (flag && self._drawedImg.length > 0) {
      let _imgURL = self._drawedImg.pop()
      let img = new Image()
      img.src = _imgURL
      img.onload = function() {
        self._clear()
        self.context.drawImage(img, 0.0)}}}}Copy the code

!!!!!!!!! Note: When redrawing an Image, since the new Image() takes time to load, you need to place the drawing in the onLoad event, otherwise an empty Image will be drawn.

At this point, the hover effect we want is almost complete.

conclusion

An important point in making hover effect is to determine the selection of elements. We cannot select elements in canvas like DOM nodes, so we need to adopt other ways to select objects. For example, when the object color is unique on the canvas, we can use the color of the pixel point of the mouse to determine the selection of objects. Object selection can also be determined by comparing the position of mouse point with the position of element. When the amount of data is large, these methods are very time-consuming. If you are interested, you can further study the selection of objects in the canvas image.

The off-screen canvas technology sounds impressive, but in fact it is a simple storage and reading operation. Therefore, when facing a new technology, you should not be frightened by its name, but understand its essence in detail.