preface
Sometimes we need to use an image to get the color of a specific pixel. The powerful Canvas provides us with a ready-made interface. This feature is not difficult, but we need to understand Canvas properly and learn how to use its API. If you are anxious to see the results, you can visit directly
Demonstrate to
The source address
I won’t write down every step in detail, but you can read along with this tutorial by referring to the source code.
Draw a picture (-)
First, we need to draw the Canvas based on the image. steps
- Create a canvas that is the same width and height as the image
- Get the canvas context
- Draw the image to canvas
We show it in the minimodel in React and we get the image instance in the React DidMount. Of course, you can also create an image object directly.
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
export class TestPicker extends PureComponent {
static propTypes = {
src: PropTypes.string.isRequired,
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
}
static defaultProps = {
width: 1300,
height: 769,
src: '/sec3.png'} constructor (props) {super(props) this.imagecanvasRef = ref => this.imagecanvas = ref This.image = new image () this.image.src = props. SRC} // Please note that you must start drawing Canvas after the image is fully loadedcomponentDidMount () {
this.image.onload = () => this.renderImageCanvas()
}
renderImageCanvas = () => {
const { width, height } = this.props
this.imageCtx = this.imageCanvas.getContext('2d')
this.imageCtx.drawImage(this.image, 0, 0, width, height)
}
render () {
const { width, height, src } = this.props
return <div>
<canvas
width={width}
height={height}
style={{ width, height }}
ref={this.imageCanvasRef}>
</canvas>
</div>
}
}
Copy the code
As long as you mount it under the appropriate node, you can see that you have a Canvas the same size as the image and the image is drawn.
However, we need to pay attention to the fact that the picture should be homologous. If it is not homologous, an error will be reported when Canvas draws the picture. For details, see Using images
Canvas And actual width and height
In essence, canvas width and height setting contains two levels, one is the size of canvas and the other is the width and height occupied by canvas in the document object. The default Canvas size is (width: 300px, height: 150px), for example, when you use CSS (width: 3000px; Height: 1500px), the size of the inner drawing area will be forced to be consistent with the overall width and height, that is, the inner drawing area will be magnified ten times. Pixel-level magnification causes the actual rendering to become more blurry. Because be aware that sometimes your drawing area gets scaled.
Realizing magnifying glass displacement (2)
We need to place the magnifying glass in the center of the mouse and follow the mouse as it moves. The implementation is also relatively simple: get the current clientX and clientY via onMousemove and subtract the left and top occupied by the current Canvas window.
First, we add an initialized state to the constructor to represent the current mouse shift. Change state when onMousemove is triggered when the mouse moves, change state to trigger re-render, and change left and top.
constructor() { this.glassCanvasRef = ref => this.glassCanvas = ref this.state = { left: 0, top: 0}} handleMouseMove = (e) = > {/ / calculate the current mouse position relative to the canvas enclosing calculateCenterPoint ({clientX: e.c. with our fabrication: lientX, clientY: e.clientY }) const { centerX, centerY } = this.centerPoint this.setState({ left: centerX, top: centerY }) } calculateCenterPoint = ({ clientX, clientY }) => { const { left, top } = this.imageCanvas.getBoundingClientRect() this.centerPoint = { centerX: Math.floor(clientX - left), centerY: Math.floor(clientY - top) } }render () {
const { width, height, src } = this.props
const { left, top } = this.state
return <div style={{ position: 'relative' }}>
<canvas
width={width}
height={height}
style={{ width, height }}
onMouseMove={this.handleMouseMove}
ref={this.imageCanvasRef}>
</canvas>
<canvas
ref={this.glassCanvasRef}
className="glass"
style={{ left: left - glassWidth/2, top: top - glassHeight/2, width: glassWidth, height: glassHeight }}>
</canvas>
</div>
}
const glassWidth = 100
const glassHeight = 100
Copy the code
Draw and enlarge the contents of the region (3)
Well, actually we’re almost halfway there. The next step is to place the image of the enlarged area into our magnifying glass. Before drawing, let’s clear the canvas
handleMouseMove = (e) => {
this.glassCtx.clearRect(0, 0, glassWidth, glassWidth)
}
Copy the code
We wanted to enlarge the elements in the magnifying glass section, and I chose 10x by default. In this case, the styling is friendly, and if you need to enlarge the element further, you just need to modify the scale.
const INIT_NUMBER = 10
const finallyScale = INIT_NUMBER * (scale < 1 ? 1 : scale)
Copy the code
Next we use a complex version of the drawImage provided by canvas to capture part of the image. CanvasRenderingContext2D. DrawImage () according to the demonstration of the MDN pictures, we know
- Sx and SY are the left and top distances from the original image to the magnifying glass we need to draw
- SWidth and sHeight are the ones we want to zoom in on
- Dx and dy are the offsets of the current drawing contents in the magnifying glass
- DWidth and dHeight are the magnifying glass sizes
drawImageSmoothingEnable(this.glassCtx, false)
this.glassCtx.drawImage(this.image,
Math.floor(centerX - (glassWidth / 2) / finallyScale), Math.floor(centerY - (glassHeight / 2) / finallyScale),
Math.floor(glassWidth / finallyScale), Math.floor(glassHeight / finallyScale),
-INIT_NUMBER, -INIT_NUMBER,
glassWidth, glassHeight
)
const drawImageSmoothingEnable = (context, enable) => {
context.mozImageSmoothingEnabled = enable
context.webkitImageSmoothingEnabled = enable
context.msImageSmoothingEnabled = enable
context.imageSmoothingEnabled = enable
}
Copy the code
We need to calculate the amplification factor. In addition, due to the calculation of the current position of the mouse, it may be 1 pixel off, but is magnified by 10 times. So I increased the offset by 10 pixels. You can determine the offset based on the actual situation.
Use drawImageSmoothingEnable to render the final image with a jagged effect. This will give you a true pixel style.
Drawing grid Lines (4)
Again, refer to the documentation on MDN for drawing grid lines.
const GRID_COLOR = 'lightgray'drawGrid(this.glassCtx, GRID_COLOR, INIT_NUMBER, INIT_NUMBER) const drawGrid = (context, color, stepx, Stepy) => {context.strokestyle = color context.linewidth = 0.5for (letI = stepx + 0.5; i < context.canvas.width; i += stepx) { context.beginPath() context.moveTo(i, 0) context.lineTo(i, context.canvas.height) context.stroke() }for (letI = stepy + 0.5; i < context.canvas.height; i += stepy) { context.beginPath() context.moveTo(0, i) context.lineTo(context.canvas.width, i) context.stroke() } }Copy the code
Realization of color selection (5)
We use getImageData to get the specific pixel data, but we need to convert it to usable data.
getColor = () => {
const { centerX, centerY } = this.centerPoint
const { data } = this.imageCtx.getImageData(centerX, centerY, 1, 1)
const color = transform2rgba(data)
}
const transform2rgba = (arr) => {
arr[3] = parseFloat(arr[3] / 255)
return `rgba(${arr.join(', ')})`
}
Copy the code
conclusion
Originally I implemented a drawing of a magnifying glass in the Canvas to enlarge the image. However, the problem is that the magnifying glass can only work inside the Canvas, so adding styles and so on needs to be drawn through the Canvas, thus losing the ability of CSS. This separation now supports custom CSS styles and reduces the complexity of continuing to draw Canvas magnifications in the Canvas.
Of course, this is just an enlightening demo, and there are still a lot of rough edges. Hope it works for you