I wrote an article before recorded the history of optimization of time to upload pictures, a history of vant image upload component optimization Or the last project, now has a new demand, is to embed the watermark to upload pictures, watermarking image is the content of the descendants of time + + system Logo + name of the system This requirement is difficult to handle, Adding watermark has always been back-end processing before, now I hope to try front-end processing, and now the project uses the OSS direct upload method, in order to reduce the pressure of the server, the upload operation is not through their own server transfer, since there is no way to be transferred to back-end processing, so let’s do it. It’s exciting to think about trying something you’ve never done before
Let’s start with the first version, which uses canvas to fill text
/** * Add watermark *@param {file} Uploaded image file */
async function addWaterMarker(file) {
// Convert the file to an IMG tag
let img = await blobToImg(file)
return new Promise((resolve, reject) = > {
// Create canvas canvas
let canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0.0)
// Set the fill size, font and style. Here, set the font size to scale in proportion to the width of the canvas to prevent the problem of small watermark generated by large pictures
ctx.font = `${canvas.width * 0.05}Px tahoma `
ctx.fillStyle = "red"
// Set the right alignment
ctx.textAlign = 'right'
// Draw text at the specified position
ctx.fillText('I'm Watermark 1', canvas.width - 100, canvas.height - 100)
ctx.fillText('I am Watermark 2', canvas.width - 100, canvas.height - 50)
// Return canvas as blob file
canvas.toBlob(blob= > resolve(blob))
})
}
/** * blob to img tag */
function blobToImg(blob) {
return new Promise((resolve, reject) = > {
let reader = new FileReader()
reader.addEventListener('load'.() = > {
let img = new Image()
img.src = reader.result
img.addEventListener('load'.() = > resolve(img))
})
reader.readAsDataURL(blob)
})
}
Copy the code
The watermark is indeed added, if the watermark is just a simple layout of text, this can also be used, but after thinking about it always feel not elegant, add watermark content is more complex, and pictures, rely on this way to achieve it is still enough
After much effort, a second version was produced, in which the watermark content was converted from HTML to images, which were then combined into uploaded images
/** * Add watermark *@param {file} Uploaded picture file *@param {el} Watermark content HTML */
async function addWaterMarker(file, el = '#markImg') {
// Convert the file to an IMG tag
let img = await blobToImg(file)
return new Promise(async (resolve, reject) => {
try {
// Create canvas canvas
let canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0.0)
// Create a watermark image canvas
let markCanvas = document.createElement('canvas')
// Create a watermark image canvas
const markImg = await createMarkImg(document.querySelector(el))
// Make the watermark scale equally according to the image size, default width is set to 1000
let zoom = canvas.width / 1000
let markCtx = markCanvas.getContext('2d')
// Scale the watermark image canvas
markCtx.scale(zoom, zoom)
// Set the width and height of the watermark canvas to the width and height of the scaled watermark canvas
markCanvas.width = markImg.width
markCanvas.height = markImg.height
// Fill the watermark canvas with the watermark canvas
markCtx.drawImage(markImg, 0.0)
// Fill the watermark canvas canvas to canvas canvas
ctx.drawImage(markCanvas, canvas.width - markCanvas.width, canvas.height - markCanvas.height, markCanvas.width, markCanvas.height)
canvas.toBlob(blob= > resolve(blob))
} catch (error) {
reject(error)
}
})
}
/** * blob to img tag */
function blobToImg(blob) {
return new Promise((resolve, reject) = > {
let reader = new FileReader()
reader.addEventListener('load'.() = > {
let img = new Image()
img.src = reader.result
img.addEventListener('load'.() = > resolve(img))
})
reader.readAsDataURL(blob)
})
}
/** * To create a watermark canvas, install html2Canvas.js plugin */
function createMarkImg(el) {
return new Promise(async (resolve, reject) => {
try {
const markImg = await html2canvas(el, {
allowTaint: false.// Allow pollution
useCORS: true.backgroundColor: null//'transparent' // background color
})
resolve(markImg)
} catch (error) {
reject(error)
}
})
}
Copy the code
Although the writing is a little complicated, but it is very easy to use, the watermark content using HTML + CSS first draw, and then directly synthesized to the designated position of the picture, save a lot of worry, but people sometimes love to torture, the first day to finish the code the next day is very awkward, or feel not elegant, self-summary at least the following two problems
- The watermark part uses two canvas layers to achieve scaling, which has certain performance consumption and is inappropriate
- The volume of the uploaded image is two or three times larger after watermarking, which is under the premise of compression before uploading the image, which will undoubtedly greatly increase the speed of uploading the image
Again, the tentative final version is finally out, and the code is as follows
/** * add watermark */
export async function addWaterMarker(file, el = '#markImg') {
// Convert file blobs to images
let img = await blobToImg(file)
return new Promise(async (resolve, reject) => {
try {
// Create canvas canvas
let canvas = document.createElement('canvas')
// Adjust the canvas width and height proportionally to reduce the image size
let imgRatio = img.naturalWidth / img.naturalHeight // Image scale
canvas.width = 750 // The default setting is 750
canvas.height = canvas.width / imgRatio
let ctx = canvas.getContext('2d')
// Fill in the uploaded image
ctx.drawImage(img, 0.0, canvas.width, canvas.height)
// Generate a watermark image
const markWidth = document.querySelector(el).clientWidth
let zoom = canvas.width * 0.3 / markWidth
let markEle = document.querySelector(el)
// Scale the watermarked HTML before converting it to an image
markEle.style.transform = `scale(${zoom}) `
const markImg = await htmlToCanvas(markEle)
// Fill the watermark
ctx.drawImage(markImg, canvas.width - markImg.width - 15 * zoom, canvas.height - markImg.height - 15 * zoom, markImg.width, markImg.height)
// Convert canvas to blob
canvas.toBlob(blob= > resolve(blob))
} catch (error) {
reject(error)
}
})
}
/** * blob to img tag */
function blobToImg(blob) {
return new Promise((resolve, reject) = > {
let reader = new FileReader()
reader.addEventListener('load'.() = > {
let img = new Image()
img.src = reader.result
img.addEventListener('load'.() = > resolve(img))
})
reader.readAsDataURL(blob)
})
}
/** * To convert HTML to canvas, html2Canvas.js plug-in */ is required
export function htmlToCanvas(el, backgroundColor = 'rgba(0,0,0,.1)') {
return new Promise(async (resolve, reject) => {
try {
const markImg = await html2canvas(el, {
allowTaint: false.// Allow pollution
useCORS: true,
backgroundColor //'transparent' // background color
})
resolve(markImg)
} catch (error) {
reject(error)
}
})
}
Copy the code
So far, the above two problems can be solved perfectly. If the image volume is still too large, you can adjust the width of canvas appropriately. Now let’s look at the effect of the image with watermark