As we all know, canvas is a bitmap. In a bitmap, we can draw various things in it, including pictures, lines, etc. So what should we do if we want to add a click event to a certain picture in the canvas. And js can only monitor canvas events. Obviously this picture does not exist and the pictures in the dom are just drawn in the canvas. Next, I will simply implement event binding for each image inside a canvas.
Let me first talk about the implementation principle: it is actually binding related events to the canvas. By recording the coordinates of the canvas where the picture is located, it is judged which picture the event acts on. In this way, it feels a bit similar to the event agent. However, it is still a little complicated to implement.
ps: I wrote the following code in ts. You can just read it as es6. You can check it if it is slightly different.
Typescript documentation (typescript is really easy to use, I recommend you learn more about it).
1. Establish a connection between the picture and canvas (here I use color blocks instead of pictures)Here we need to establish a certain connection between the color block and the canvas, rather than just rendering. Also record the coordinates, width and height of the color block. Let’s implement it step by step
First write a basic html page to create a canvas:
<!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <meta name=viewport content=width=device-width, initial-scale=1.0> <meta http-equiv=X-UA -Compatible content=ie=edge> <title>canvas event</title> <style> html, body { height: 100%; background: #eee; } canvas { background: #fff; display: block; margin: 0 auto; } </style></head><body> <canvas width=500 height=500 id=canvas></canvas></body>
Next, we need to define a Canvas class. What functions should this class have?
Because the color blocks also have some parameters of their own, in order to facilitate expansion, we also define a class for the color blocks. The functions required for this type are:
Width, height, color, coordinates (x, y), and Canvas instance; let’s decide on these initially.
OK start writing
// Canvas class class Canvas { blockList: Block[] ctx: any canvas: any createBlock (option) { option.Canvas = this this.blockList.push(new Block(option)) this.painting() } rendering (block) { // Render color block function this.ctx.fillStyle = block.color this.ctx.fillRect(block.x, block.y, block.w, block.h) } painting () { // Render all the color blocks in the container to the canvas // Clear the canvas (the old one should be cleared before rendering) this.ctx.fillStyle = '#fff' this.ctx.fillRect(0, 0, this.canvas.width, this .canvas.height) this.blockList.forEach(ele => { this.rendering(ele) }) } constructor (ele) { // Initialization function (the input is canvas) // Set canvas this.canvas = ele this.ctx = this.canvas.getContext('2d') // Color block container this.blockList = [] }}class Block { w: number h: number x: number y: number color : string Canvas: Canvas hierarchy: number constructor ({ w, h, x, y, color, Canvas }) { // Initialize and set the color block related properties this.w = w this.h = h this.x = x this.y = y this.color = color this.Canvas = Canvas }}
Let’s try running a wave below
// Create a Canvas instance and add a blue color block with a width and height of 100px, position (100,100), (300,100) red and blue color blocks var canvas = new Canvas(document.getElementById('canvas')) canvas.createBlock({ // red x: 100, y: 100, w: 100, h: 100, color: '#f00' }) canvas.createBlock({ // blue x: 100, y: 100, w: 300, h: 100, color: '#00f' })
The running results are as follows:
2. Add a click event to the color blockHere you cannot directly add a click event to the color block, so you need to use coordinates to determine which color block is currently clicked.
class Block { // ...Omit part of the code checkBoundary (x, y) { // Determine the boundary method return x > this.x && x < (this.x + this.w) && y > this.y && y < (this.y + this.h) } mousedownEvent () { // Click event console.log(`Clicked on the color block with color ${this.color}`) }}class Canvas { // ...omitted part code constructor (ele) { this.canvas = ele this.ctx = this.canvas.getContext('2d') this.blockList = [] // Event binding (there is one thing to note here. I used the bind method here, which is In order to switch the this pointer in the mousedownEvent method to Canvas) this.canvas.addEventListener('click', this.mousedownEvent.bind(this)) // Click event} mousedownEvent () { // Click event const x = e.offsetX const y = e.offsetY // Here the coordinates of the click are passed to all color blocks, and the boundary judgment method is used to determine whether the click is inside. If yes, execute the event method of the color block. this.blockList.forEach(ele => { if (ele.checkBoundary(x, y)) ele.mousedownEvent(e) }) }}
So far, we have implemented the binding of corresponding click events to different color blocks in different canvases. However, this click event is not perfect, because so far we have not introduced the concept of hierarchy, which means that if two overlapping color blocks are clicked, both will be triggered. So we also need to add hierarchical attributes to the color blocks. If you click on a color block to change the color block, the level of the color block will be raised to the highest level.
class Block { // ...Omit part of the code constructor ({ w, h, x, y, color, Canvas, hierarchy }) { // Initialize and set the properties related to the color block this.w = w this.h = h this. x = x this.y = y this.color = color this.Canvas = Canvas this.hierarchy = 0 }}class Canvas { // ...omit part of the code constructor (ele) { this.canvas = ele this.ctx = this.canvas.getContext('2d') this.blockList = [] // Event binding (there is one thing to note here. I used the bind method here to switch the this pointer in the mousedownEvent method to Canvas) this .canvas.addEventListener('click', this.mousedownEvent.bind(this)) // Click event this.nowBlock = null // Currently selected color block} createBlock (option) { // Create color block function (Block here is the class of color block) option.Canvas = this // The level of creating the latest color block should be the highest option.hierarchy = this.blockList.length this.blockList.push(new Block( option)) this.rendering() } mousedownEvent (e) { // Click event const x = e.offsetX const y = e.offsetY // Get the highest level color block in the point this.nowBlock = (this.blockList.filter(ele => ele.checkBoundary(x, y))).pop() // If there is no captured color block, exit directly if (!this .nowBlock) return // Raise the clicked color block level to the highest this.nowBlock.hierarchy = this.blockList.length // Reorder (from small to large) this.blockList.sort((a, b) => a.hierarchy - b.hierarchy) // Re-allocate the hierarchy from 0 this.blockList.forEach((ele, idx) => ele.hierarchy = idx) // Re-sort in reverse order and then re-render. this.painting() this.nowBlock.mousedownEvent(e) // Only trigger events for the selected color block}}// Here we have to add a third color block that overlaps the red color block canvas.createBlock({ x: 150, y: 150, w: 100, h: 100, color: '#0f0'})
The code in the mousedownEvent method in Canvas is a bit complicated, mainly because it is a bit convoluted.
The effect after running is as follows:
3. Drag and drop different color blocksAbove we have implemented obtaining different color blocks and modifying their levels. Next we need to implement dragging of color blocks, mainly to obtain the changes in position coordinates during the movement of the mouse and when the mouse is initially clicked. This principle is the same as that of ordinary DOM drag and drop implementation.
Get the point where the color block is clicked and the distance (disX, disY) from the left and top of the color block.
When the mouse moves, subtract (disX, disY) from the current distance of the mouse from the left and top of the canvas. This is the x and y coordinates of the color block.
class Block { // ...omit part of the code mousedownEvent (e: MouseEvent) { /* The calculation method of disX and disY here: e.offsetX obtains the distance between the mouse click and the left side of the canvas, this.x is the color block distance The distance to the left of the canvas. e.offsetX-this.x is the distance to the left of the color block. This should be easy to understand */ const disX = e.offsetX - this.x // The distance from the left side of the color block when clicked const disY = e.offsetY - this.y // The distance from the top of the color block when clicked // Bind the mouse sliding event; here mouseEvent.offsetX is also the distance between the mouse and the left side of the canvas, mouseEvent.offsetX - disX is the x coordinate of the color block. In the same way, y is calculated in the same way. Finally, just re-render. document.onmousemove = (mouseEvent) => { this.x = mouseEvent.offsetX - disX this.y = mouseEvent.offsetY - disY this.Canvas.painting() } // Clear all events when the mouse is released document.onmouseup = ( ) => { document.onmousemove = document.onmousedown = null } // console.log(`The color block 22 with the color ${this.color} was clicked`) }}
The effect is as follows:
The complete code is pasted below (the HTML and the calling method will not be included). This example is just a simple implementation of binding events to the content in the canvas. You can implement more complex ones, such as replacing color blocks with pictures. In addition to dragging, You can zoom, rotate, delete, etc. pictures.
class Canvas { blockList: Block[] ctx: any canvas: any nowBlock: Block createBlock (option) { option.hierarchy = this.blockList.length option.Canvas = this this.blockList.push(new Block(option)) this. painting() } rendering (block) { this.ctx.fillStyle = block.color this.ctx.fillRect(block.x, block.y, block.w, block.h) } painting () { // Clear the canvas this.ctx.fillStyle = '#fff' this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height) this.blockList.forEach(ele => { this.rendering(ele) }) } mousedownEvent (e: MouseEvent) { // Click event const x = e.offsetX const y = e.offsetY // Get the highest level color block in the point this.nowBlock = (this.blockList.filter(ele => ele.checkBoundary(x, y))).pop() // If there is no captured color block, exit directly if (!this .nowBlock) return // Raise the clicked color block level to the highest this.nowBlock.hierarchy = this.blockList.length // Reorder (from small to large) this.blockList.sort((a, b) => a.hierarchy - b.hierarchy) // Re-allocate the hierarchy from 0 this.blockList.forEach((ele, idx) => ele.hierarchy = idx) // Re-sort in reverse order and then re-render. this.painting() this.nowBlock.mousedownEvent(e) // this.blockList.forEach(ele => { // if (ele.checkBoundary(x, y)) ele.clickEvent(e) // }) } constructor (ele) { this.canvas = ele this.ctx = this.canvas.getContext('2d') this.blockList = [] // Event binding this.canvas.addEventListener('mousedown', this.mousedownEvent.bind(this)) }}class Block { w: number h: number x: number y: number color: string Canvas: Canvas hierarchy: number constructor ( { w, h, x, y, color, Canvas, hierarchy }) { this.w = w this.h = h this.x = x this.y = y this.color = color this.Canvas = Canvas this.hierarchy = hierarchy } checkBoundary (x, y) { return x > this.x && x < (this.x + this.w) && y > this.y && y < (this.y + this.h) } mousedownEvent (e: MouseEvent) { const disX = e.offsetX - this.x const disY = e.offsetY - this.y document.onmousemove = (mouseEvent) => { this.x = mouseEvent.offsetX - disX this.y = mouseEvent.offsetY - disY this.Canvas.painting() } document.onmouseup = () => { document.onmousemove = document.onmousedown = null } // console.log(`Color block 22 with color ${this.color} was clicked`) }}
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.