JS+Canvas實現上傳圖片截圖功能
1. 前言
在我們平時開發(fā)圖片上傳時,有時需要實現圖片的裁剪功能。通常我們使用Vue-cropper來實現圖片的裁剪功能。
如何實現呢?本文介紹下圖片裁剪的功能的實現過程。
2. 實現思路
- 使用
input元素讓用戶選擇圖片文件,然后使用FileReaderAPI 讀取圖片文件的內容,并將其設置為img元素的src屬性。 - 創(chuàng)建一個
canvas元素并獲取其 2D 渲染上下文,然后使用drawImage方法將圖片繪制到 canvas 上。 - 監(jiān)聽鼠標的
mousedown、mousemove和mouseup事件,用于創(chuàng)建和調整裁剪框。當用戶點擊鼠標并移動時,記錄鼠標的位置并在 canvas 上繪制一個矩形作為裁剪框。 - 當用戶釋放鼠標按鈕時,使用
getImageData方法獲取裁剪框內的像素數據,然后創(chuàng)建一個新的 canvas 并使用putImageData方法將這些像素數據放入新的 canvas 中。這樣就得到了裁剪后的圖片。 - 最后,使用
toDataURL方法將裁剪后的圖片轉換為 Base64 格式的字符串,或者使用toBlob方法將其轉換為 Blob 對象,然后可以將其下載到用戶的計算機上,或者上傳到服務器。
3. 實現步驟
使用原生js進行實現,使用兩個canvas,一個是原圖展示,一個是截圖后的。
HTML部分:
<!-- 文件上傳 -->
<input type="file" id="inputFile" accept="image/*" />
<!-- 展示讀取的圖片的canvas -->
<div class="clipContainer">
<canvas id="clip"></canvas>
</div>
<!-- 截圖后的圖片的canvas -->
<div class="drawContainer">
<canvas id="draw"></canvas>
</div>
JS公共部分:
const inputNode = document.querySelector("#inputFile");
const clipCanvas = document.querySelector("#clip");
const drawCanvas = document.querySelector("#draw");
const clipContainer = document.querySelector(
".clipContainer"
);
const drawContainer = document.querySelector(
".drawContainer"
);
const clipCanvasCtx = clipCanvas.getContext("2d");
const drawCanvasCtx = drawCanvas.getContext("2d");
const imageNode = document.createElement('img');
const drawImageNode = document.createElement('img');//用于最終展示圖片
let resultFile = null;//最終生成的File對象圖片文件
3.1 選擇文件進行預覽
使用FileReader讀取文件的url和寬高,進行cavas繪制。
// 1. 選擇圖片 讀取圖片
inputNode.addEventListener(
"change",
function (e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function (e) {
imageNode.src = e.target.result;
imageNode.onload = function () {
// 獲取真實的圖片寬高 canvas繪制圖片
generateCanvas(clipContainer, clipCanvas, this.width, this.height);
clipCanvasCtx.drawImage(imageNode, 0, 0, this.width, this.height);
};
};
file && reader.readAsDataURL(file);
},
false
);
// 2. canvas繪制圖片
function generateCanvas(canvasContainer, canvas, width, height) {
// 2.1 外部容器的寬高
canvasContainer.style.width = width + 'px';
canvasContainer.style.height = height + 'px';
// 2.2 canvas的寬高
canvas.width = width;
canvas.height = height;
}
結果如下:

3.2 監(jiān)聽mousedown事件
監(jiān)聽mousedown 記錄起點坐標,使用startPositon進行記錄起始位置。
// 記錄點擊開始結束位置
let startPosition = []
clipCanvas.addEventListener('mousedown', function (e) {
// 3.1 記錄開始截圖的位置
startPosition = [e.offsetX, e.offsetY]
// 3.2 開始監(jiān)聽 mousemove 和 mouseup 事件
clipCanvas.addEventListener('mousemove', mousemoveHandler, false)
clipCanvas.addEventListener('mouseup', mouseupHandler, false)
})
3.3 監(jiān)聽mousemove事件
記錄鼠標移動坐標,計算出截圖大小,繪制截圖蒙層區(qū)域
// 記錄截圖的偏移x,y和截圖的寬高w,h
let screenShotData = []
function mousemoveHandler(e) {
// 1. 計算裁剪區(qū)域的寬度和高度
const { offsetX, offsetY } = e
const [startX, startY] = startPosition
// 偏移位置-起始位置 = 真實寬高
const [rectWidth, rectHeight] = [offsetX - startX, offsetY - startY]
// 2. 保存裁剪區(qū)域的相關信息
screenShotData = [startX, startY, rectWidth, rectHeight]
// 3. 重新繪制
fill(clipCanvasCtx, clipCanvas, screenShotData)
}
function fill(ctx, canvas, sizeData) {
const { width, height } = canvas
const [x, y, w, h] = sizeData
// 1. 再次繪制前,清理canvas1畫布上的內容
ctx.clearRect(0, 0, width, height)
// 2. 畫布上繪制蒙層
ctx.fillStyle = `rgba(0,0,0,0.6)`
ctx.fillRect(0, 0, width, height)
// 3. 繪制裁剪區(qū)域
// 在源圖像外繪制新圖像,只有源圖像外的目標圖像部分會被顯示,源圖像是透明的
ctx.globalCompositeOperation = 'destination-out'
ctx.fillStyle = '#2c2c2c'
ctx.fillRect(...sizeData)
// 設置在現有畫布上繪制新的圖形
ctx.globalCompositeOperation = 'destination-over'
// 剪切圖像,并在畫布上定位被剪切的部分
ctx.drawImage(imageNode, 0, 0, width, height, 0, 0, width, height);
}
結果如下:

3.4 監(jiān)聽mouseup事件
注銷事件監(jiān)聽,將裁剪區(qū)域放入另一個canvas中進行處理,并讀取File對象
function mouseupHandler(e) {
// 1. 注銷監(jiān)聽 mousedown 和 mousemove 事件
clipCanvas.removeEventListener('mousemove', mousemoveHandler, false)
clipCanvas.removeEventListener('mouseup', mouseupHandler, false)
// 2. 開始繪制截圖區(qū)域圖片
const data = clipCanvasCtx.getImageData(...screenShotData)
// 3. 使用新畫布
const [x, y, w, h] = screenShotData
generateCanvas(drawCanvas, drawContainer, w, h)
// 每次繪制前,都先進行清除操作
drawCanvasCtx.clearRect(...screenShotData);
// 將 clipCanvas 裁剪區(qū)域的數據放入 drawCanvas 中
drawCanvasCtx.putImageData(data, 0, 0);
drawCanvas.style.display = 'none'
// 4. 展示圖片
const imageUrl = drawCanvas.toDataURL('image/png')
drawImageNode.src = imageUrl;
document.body.appendChild(drawImageNode)
// 5. 生成File對象
drawCanvas.toBlob(blob => {
resultFile = new File([blob], 'test.png', { type: 'image/png' })
}, 'image.png')
}

4. 總結
最后總結一下如圖:

到此這篇關于JS+Canvas實現上傳圖片截圖功能的文章就介紹到這了,更多相關JS Canvas圖片截圖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
用JavaScript實現用一個DIV來包裝文本元素節(jié)點
當我試圖將文本(可能也包含HTML元素)用一個DIV元素包起來時,可以使用下面的方法,需要的朋友可以參考下2014-09-09

