获取图片并上传

min截图, 利用canva在浏览器内部实现的截图

获取图片并上传

思路:
初始化: 监听input获取图片的点击事件 -> 初始化挂载监听事件
操作
从文件夹获取本地图片 -> 触发监听事件 -> 获取本地上床文件的 File/Blob 对象(FIleReader) -> 通过readAsDataURL 读取成 base64 -> 在图片加载的时候进行复制操作 -> 画图

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
html,
body {
margin: 0;
}
.canvas-container {
display: none;
border: 1px solid #ddd;
}
img {
width: 300px;
}
</style>
</head>
<body>
<div>
<label for="oImageFile">选择图片</label>
<input type="file" id="oImageFile" accept="image/*" />
</div>

<div class="canvas-container">
<canvas id="can"></canvas>
</div>
<img :src="newfile" />

<script>
const img = document.querySelector('img')

const oImageFile = document.getElementById('oImageFile')

const oContainer = document.querySelector('.canvas-container')
const oCan = document.getElementById('can')
const ctx = oCan.getContext('2d')

const init = () => {
bindEvent()
}
function handleFileChange(e) {
const file = e.target.files[0]
const reader = new FileReader();

reader.readAsDataURL(file)
reader.onload = function(e) {
console.log(e.target.result)
img.src = e.target.result

img.onload = function(e) {
const {width, height} = e.target
generateCanvas(oContainer, oCan, width, height)
ctx.drawImage(img, 0, 0, width, height)
}
}

}

function generateCanvas(container, canvas, width, height) {
container.style.wodth = width + 'px'
container.style.width = height + 'px'
canvas.width = width
canvas.height = height
container.style.display = 'block'
}

function bindEvent() {
oImageFile.addEventListener('change', handleFileChange, false)
}
init()
</script>
</body>
</html>

加蒙层

利用canvas方法, 两行解决

1
2
3
4
	function drawImageMask(x, y, width, height, opacity) {
ctx.fillStyle = `rgba(0, 0, 0, ${opacity})`
ctx.fillRect(x, y, width, height)
}

在绘制完图片后调用蒙层函数

1
2
3
4
5
6
 img.onload = function (e) {
const { width, height } = e.target
generateCanvas(oContainer, oCan, width, height)
ctx.drawImage(img, 0, 0, width, height)
drawImageMask(0, 0, width, height, 0.5)
}

[[前端了解/截图功能/_resources/小练习/e1aa9ed1f45116737a4f5702041f7596_MD5.jpeg|Open: Pasted image 20251207184443.png]]
![[前端了解/截图功能/_resources/小练习/e1aa9ed1f45116737a4f5702041f7596_MD5.jpeg]]
就变成这样啦

监听鼠标事件

在监听完文件上传事件后, 新增鼠标点击事件
oCan.addEventListener('mousedown', handleCanvasMouseDown, false)

监听鼠标移动和抬起

1
2
3
4
5
6
7
function handleCanvasMouseDown(e) {
//更新鼠标位置
initPos = [e.offsetX, e.offsetY]

oCan.addEventListener('mousemove', handleCanvasMouseMove, false)
oCan.addEventListener('mouseup', handleCanvasMouseUp, false)
}

鼠标移动时计算矩形的宽高

1
2
3
4
5
6
7
function handleCanvasMouseMove(e) {
const endX = e.offsetX
const endY = e.offsetY
const [startX, startY] = initPos
const rectWidth = endX - startX
const rectHeight = endY - startY
}

抬起时移除对鼠标移动和抬起的监听

1
2
3
4
function handleCanvasMouseUp(e) {
oCan.removeEventListener('mousemove', handleCanvasMouseMove, false)
oCan.removeEventListener('mouseup', handleCanvasMouseUp, false)
}

选取图片

[[前端了解/截图功能/_resources/小练习/49f324fd65e2068c471ee05484c8decb_MD5.jpeg|Open: Pasted image 20251207201835.png]]
![[前端了解/截图功能/_resources/小练习/49f324fd65e2068c471ee05484c8decb_MD5.jpeg]]

逻辑链
监听鼠标按下 -> 鼠标按下 -> 更新鼠标位置 -> 监听鼠标移动和抬起 -> 鼠标移动 -> 计算鼠标移动的距离, 算出矩形宽高 -> 清除画布(保证拖动过程中不留下残影) -> 绘制蒙层 -> 绘制截图区域 -> 把所选矩形挖空(‘destination-out’) -> 在蒙版上挖出一个透明的区域(fillRect(initPos[0], initPos[1], rectWidth, rectHeight)) -> 新的内容只在原有内容之后绘制’destination-over’ -> 将原始图片绘制一遍(会出现在透明区域的下面)

1
2
3
4
5
6
7
8
9
10
11
12
13
function handleCanvasMouseMove(e) {
const endX = e.offsetX
const endY = e.offsetY
const [startX, startY] = initPos
const rectWidth = endX - startX
const rectHeight = endY - startY

const { width, height } = oCan
//清空画布
ctx.clearRect(0, 0, width, height)
drawImageMask(0, 0, width, height, maskOpacity) //蒙层
drawScreenShot(width, height, rectWidth, rectHeight) //绘制选取
}
1
2
3
4
5
6
7
8
function drawScreenShot(canwidth, canheight, rectWidth, rectHeight) {
ctx.globalCompositeOperation = 'destination-out'//挖空
ctx.fillStyle = `#000`//透明
ctx.fillRect(initPos[0], initPos[1], rectWidth, rectHeight)

ctx.globalCompositeOperation = 'destination-over' //新图形绘制在下面
ctx.drawImage(img, 0, 0, canwidth, canheight, 0, 0, canwidth, canheight)
}

获取所截图片

在获取截图的位置后通过 getImageData, 初始化新的canvas坐标, putImageData得到所选区域图片
在鼠标抬起时调用drawScreenShotImage (位置信息在鼠标移动时更新)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
html,
body {
margin: 0;
}
.canvas-container,
.canvas-container2 {
display: none;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<div>
<label for="oImageFile">选择图片</label>
<input type="file" id="oImageFile" accept="image/*" />
</div>

<div class="canvas-container">
<canvas id="can"></canvas>
</div>
<img :src="newfile" />
<div class="canvas2-container">
<canvas id="can2"></canvas>
</div>

<script>
const maskOpacity = 0.5

const img = document.querySelector('img')

const oImageFile = document.getElementById('oImageFile')

const oContainer = document.querySelector('.canvas-container')
const oContainer2 = document.querySelector('.canvas2-container')

const oCan = document.getElementById('can')
const oCan2 = document.getElementById('can2')

const ctx = oCan.getContext('2d')
const ctx2 = oCan2.getContext('2d')

let initPos = [] // 记录鼠标的初始位置
let screenShotData = [] //存放所选图片的位置信息

const init = () => {
bindEvent()
}

function handleFileChange(e) {
const file = e.target.files[0]
const reader = new FileReader()

reader.readAsDataURL(file)
reader.onload = function (e) {
img.src = e.target.result

img.onload = function (e) {
const { width, height } = e.target
generateCanvas(oContainer, oCan, width, height)
ctx.drawImage(img, 0, 0, width, height)
drawImageMask(0, 0, width, height, maskOpacity)
}
}
}

function generateCanvas(container, canvas, width, height) {
container.style.wodth = width + 'px'
container.style.width = height + 'px'
canvas.width = width
canvas.height = height
container.style.display = 'block'
}

function bindEvent() {
oImageFile.addEventListener('change', handleFileChange, false)
//添加鼠标监听
oCan.addEventListener('mousedown', handleCanvasMouseDown, false)
}

function handleCanvasMouseDown(e) {
//更新鼠标位置
initPos = [e.offsetX, e.offsetY]

oCan.addEventListener('mousemove', handleCanvasMouseMove, false)
oCan.addEventListener('mouseup', handleCanvasMouseUp, false)
}

function handleCanvasMouseMove(e) {
const endX = e.offsetX
const endY = e.offsetY
const [startX, startY] = initPos
const rectWidth = endX - startX
const rectHeight = endY - startY

const { width, height } = oCan

screenShotData = [startX, startY, rectWidth, rectHeight]

ctx.clearRect(0, 0, width, height)
drawImageMask(0, 0, width, height, maskOpacity)
drawScreenShot(width, height, rectWidth, rectHeight)
}

function handleCanvasMouseUp(e) {
oCan.removeEventListener('mousemove', handleCanvasMouseMove, false)
oCan.removeEventListener('mouseup', handleCanvasMouseUp, false)
drawScreenShotImage(screenShotData)
}

function drawImageMask(x, y, width, height, opacity) {
ctx.fillStyle = `rgba(0, 0, 0, ${opacity})`
ctx.fillRect(x, y, width, height)
}

function drawScreenShot(canwidth, canheight, rectWidth, rectHeight) {
ctx.globalCompositeOperation = 'destination-out'
ctx.fillStyle = `#000`
ctx.fillRect(initPos[0], initPos[1], rectWidth, rectHeight)

ctx.globalCompositeOperation = 'destination-over'
ctx.drawImage(img, 0, 0, canwidth, canheight, 0, 0, canwidth, canheight)
}

function drawScreenShotImage(screenData) {
const data = ctx.getImageData(...screenData)
generateCanvas(oContainer2, oCan2, screenData[2], screenData[3])
ctx2.clearRect(...screenData)
ctx2.putImageData(data, 0, 0)
}

init()
</script>
</body>
</html>