Cut to the chase
After a request, the image processing new Mosaic, can undo the previous step with undo
Item address can be pulled down and used directly
Technical requirements
JS, canvas,
Design ideas
- Canvas Draw a picture with the same proportion and no blank space on the canvas
- Gets the mouse position as the mouse moves over the canvas
- Gets the pixel average of mouse positioning range size(size per Mosaic)
- Set the RGBA of all pixels in this region to the average of what you just calculated
- Cycle steps 2-4. Before drawing, judge whether the current drawing point is too close to the last drawing point
- Export the url of the full size Mosaic picture
- Undo: Save the picture data after each Mosaic
- Recovery: Cache undo data
Code implementation
Define a function that accepts the container ID and canvasID
//html <div id="content"><canvas id="myCanvas" width="1200" height="0"></canvas></div> //js function CreateMosaic(contentId, canvasId){// All code inside this function}Copy the code
Define an initialization function that returns the required variables
// Initialize parameters
const init = () = >{
const content = document.getElementById(contentId)
const canvas = document.getElementById(canvasId);
const painting = canvas.getContext('2d');
const size = 20 // Each time the Mosaic ranges, pixels
let lastP = [0.0] // Draw the center point one last time
let isPrint = false // Start drawing, true means drawable, false means not drawable
let brushFollow = false // The brush starts
let brush = null / / brush
let handleData = [] // Each operation step
let cacheDeleteData = [] // Cache undo data for recovery
let imgWH = [] // Original width and height of the image
return { content, canvas, painting, size, lastP, isPrint, brushFollow, brush, handleData, cacheDeleteData, imgWH }
}
let { content, canvas, painting, size, lastP, isPrint, brushFollow, brush, handleData, cacheDeleteData, imgWH } = init()
Copy the code
The picture is scaled in equal proportion to the canvas width, and the last height of the canvas is set to be the same as the height of the picture after scaling
// Calculate the width and height of the image scale
const calculate = (cw, pw, ph) = > {
ph = ph * ( cw / pw )
pw = cw
return {pw, ph}
}
const img = new Image();
img.src = 'Picture address'
img.crossOrigin = ' ';/ / across domains
img.onload = () = > {
const {pw, ph} = calculate(canvas.width, img.width, img.height)
imgWH = [img.width, img.height]
painting.drawImage(img, 0.0, pw, ph);
canvas.height = ph
handleData.push(painting.getImageData(0.0, canvas.width, canvas.height))
}
Copy the code
Define the pixel average value of mouse positioning range size(size per Mosaic)
// Get the mean value of pixels in the range
const getPxAVG = (imgDate) = > {
const data = imgDate.data
let r = 0, g = 0, b = 0, a = 0
const sumPx = data.length / 4
for (let i = 0; i < sumPx; i ++){
r = r + data[i * 4]
g = g + data[i * 4 + 1]
b = b + data[i * 4 + 2]
a = a + data[i * 4 + 3]}return [r / sumPx, g / sumPx, b / sumPx, a / sumPx]
}
Copy the code
Sets the RGBA value for all pixels in an area
// Set the pixel values in the range
const setPxColor = (imgDate, color) = > {
for (let i = 0; i < imgDate.data.length; i ++){
imgDate.data[i] = color[i % 4]}return imgDate
}
Copy the code
Defines a function to draw a Mosaic
// Create a Mosaic
const printMosaic = (x, y) = > {
// Get the pixel data of mouse location range size
const imgData = painting.getImageData(x, y, size, size)
// Get the average value of range pixel data
const pxAVG = getPxAVG(imgData)
// Create a Mosaic image data for the region
let mosaic = setPxColor(painting.createImageData(size, size), pxAVG)
// Redraw
painting.putImageData(mosaic, x, y)
}
Copy the code
Define the onMousedown, onMousemove, onMouseup functions
// Determine if the range of the last Mosaic is exceeded
const judgeD = (currentP, lastP, r) = > {
const [x1, y1] = currentP, [x2, y2] = lastP
return Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2)) <= r
}
canvas.onmousedown = e= > {
if(! brushFollow)return false
isPrint = true
}
canvas.onmousemove = e= > {
const { offsetX, offsetY } = e
if(! brushFollow)return false
brush.style.transform = `translate(${offsetX - size / 2}px, ${offsetY - size / 2}px)`
if(! isPrint)return false
if(! judgeD([offsetX, offsetY], lastP, size /2)){
lastP = [offsetX, offsetY]
printMosaic(offsetX - (size / 2), offsetY - (size / 2))
}
}
canvas.onmouseup = e= > {
isPrint = false
handleData.push(painting.getImageData(0.0, canvas.width, canvas.height))
cacheDeleteData = []
}
Copy the code
Defines the brush creation and startup functions
// Create a brush
const createBrush = () = > {
const brush = document.createElement('div')
brush.style.position = 'absolute'
brush.style.zIndex = '99'
brush.style.width = size + 'px'
brush.style.height = size + 'px'
brush.style.background = 'rgba (233233233,0.5)'
brush.style.pointerEvents = 'none'
brush.style.left = '0'
brush.style.right = '0'
brush.style.transform = `translate(-${size}px, -${size}px)`
content.appendChild(brush)
return brush
}
/ / start
const start = () = > {
!brush && (brush = createBrush())
brushFollow = true
}
/ / stop
const stop = () = > {
brush && brush.remove()
brushFollow = false
brush = null
}
canvas.onmouseenter = start
canvas.onmouseleave = stop
Copy the code
Undo and Restore
/ / cancel
const revocation = () = > {
if (handleData.length <= 1) return false
cacheDeleteData.push(handleData.pop())
painting.putImageData(handleData[handleData.length - 1].0.0)}/ / recovery
const recover = () = > {
if (cacheDeleteData.length === 0) return false
handleData.push(cacheDeleteData.pop())
painting.putImageData(handleData[handleData.length - 1].0.0)}// Createmaic exposes the two functions
return {
revocation,
recover,
}
Copy the code
Returns the original size Mosaic picture
// Do not write the actual code
// Create a canvas with the width and height of the imgWH cache. Display: None, put into the DOM.
// Assign the result of canvas.todataURL ('image/ PNG ') to a new new image ()
// Then in the new canvas.getContext('2d'). DrawImage (0, 0,... [imgWH])
// The new canvas.toDataURL('image/ PNG ') is the result we want
Copy the code