最近挺忙的,几乎没有时间去更新博客,今天正好在学习新东西,正好和大家分享一下。
最近要做一个使用canvas完成图片平移,缩放,添加标注的需求,完成的效果大概如下:
使用canvas内置api完成图片的缩放平移和导出和添加提示
最终的代码如下,这是使用canvas内置api完成的,缩放用到的是scale方法,平移用的是canvas的translate方法,代码中每个地方会有对应的注释,代码都是自己写的,如果又不懂得可以留言。
<!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>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.content {
position: relative;
margin: 50px;
height: 500px;
border: 5px solid red;
overflow: hidden;
}
#canvas {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
border: 5px solid pink;
cursor: grab;
}
.btnx {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: pink;
height: 50px;
cursor: pointer;
}
.btn {
text-align: center;
flex: 1;
}
.btn2 {
display: none;
}
.btn5 {
display: none;
}
</style>
</head>
<body>
<div class="content">
<canvas id="canvas" width="500" height="400"></canvas>
<div class="btnx">
<div class="btn btn1">裁剪</div>
<div class="btn btn2">取消</div>
<div class="btn btn3">放大x0.5</div>
<div class="btn btn4">缩小x0.5</div>
<div class="btn btn5">导出</div>
<div class="btn btn6">显示或隐藏tips</div>
</div>
</div>
<script>
const btn1 = document.querySelector('.btn1')
const btn2 = document.querySelector('.btn2')
const btn3 = document.querySelector('.btn3')
const btn4 = document.querySelector('.btn4')
const btn5 = document.querySelector('.btn5')
const btn6 = document.querySelector('.btn6')
// canvas的dom
const canvas = document.querySelector('#canvas')
// canvas宽度
const canvasWidth = 500
// canvas高度
const canvasHeight = 400
// 定义画布的初始位置
var canvasX = 0;
var canvasY = 0;
// 定义拖动的初始位置
var dragStartX = 0;
var dragStartY = 0;
// 当前是否正在拖拽元素
var isDragging = false;
// 当前是否正在绘制元素
var isDrawing = false;
// 当前的缩放
var scale = 1.0;
// 存储绘制矩形的坐标和大小
var rect = {};
// 新增的canvas的个数
let newCanvasCount = 0
// 是否显示tips
let isShowtips=false
const ctx = canvas.getContext('2d')
canvas.setAttribute('width', canvasWidth)
canvas.setAttribute('height', canvasHeight)
// 获取图片
let img = new Image();
img.src = "./imgs/girl.webp";
img.onload = function () {
drawImageByScale()
}
btn2.addEventListener('click', function () {
newCanvasCount = 0
btn2.style.display = 'none'
btn5.style.display = 'none'
btn1.style.display = 'block'
canvas.style.cursor = 'grab'
// 充值绘制当前图片
drawImageByScale()
// 移除所有新增的canvas
// const allCanvas = document.querySelectorAll('[id^="newcanvas-"]');
// allCanvas.forEach((item, index) => {
// document.body.removeChild(item)
// })
// console.log(allCanvas);
// 移除所有的监听器
clearEventListeners()
// 重新添加让可以移动图标的鼠标监听器
handleCanvasMove()
})
btn3.addEventListener('click', function () {
scale += 0.5
drawImageByScale()
})
btn4.addEventListener('click', function () {
scale = Math.max(0.1, scale - 0.5)
drawImageByScale()
})
btn5.addEventListener('click', exportImage);
btn6.addEventListener('click', function () {
isShowtips=!isShowtips
drawImageByScale()
})
btn1.addEventListener('click', function () {
clearEventListeners()
btn2.style.display = 'block'
btn5.style.display = 'block'
btn1.style.display = 'none'
canvas.style.cursor = 'crosshair'
canvas.addEventListener('mousedown', handleDrawRectMouseDown);
canvas.addEventListener('mousemove', handleDrawRectMouseMove);
canvas.addEventListener('mouseup', handleDrawRectMouseUp);
})
// 让canvas可以移动
handleCanvasMove()
// 让canvas可以缩放
handleCanvsZoom()
// 绘制矩形
function DrawRect() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!isDrawing) return;
// 第一种样式:绘制一个填充矩形
// ============开始==============
// ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
// drawImageByScale();
// ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
// ============结束==============
// 第二种样式:绘制一个边框
// ============开始==============
ctx.lineWidth = 10
ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
drawImageByScale()
ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
// ============结束==============
}
// 获取鼠标在canvas中的x和y坐标
function handleDrawRectMouseDown(e) {
// rect.startX代表鼠标在canvas内的x坐标
// e.clientX用于获取鼠标在浏览器中的横向坐标
// canvas.getBoundingClientRect().left用于获取canvas的左边框相对于浏览器的距离
rect.startX = e.clientX - canvas.getBoundingClientRect().left;
rect.startY = e.clientY - canvas.getBoundingClientRect().top;
isDrawing = true;
}
// 获取绘制的矩形的宽和高
function handleDrawRectMouseMove(e) {
if (!isDrawing) return;
// rect.w和rect.h就代表了矩形的像素大小
rect.w = (e.clientX - canvas.getBoundingClientRect().left) - rect.startX;
rect.h = (e.clientY - canvas.getBoundingClientRect().top) - rect.startY;
DrawRect();
}
// 停止移动
function handleDrawRectMouseUp(e) {
isDrawing = false;
}
// 鼠标按下拖拽图片
function handleDragMouseDown(event) {
isDragging = true;
// 记录鼠标按下时的位置
dragStartX = event.clientX;
dragStartY = event.clientY;
}
// 鼠标按下移动拖拽图片
function handleDragMouseMove(event) {
if (isDragging) {
// 计算鼠标移动的距离
// deltaX的数值是根据鼠标移动的数据来计算的,鼠标速度越快这个数值越大
var deltaX = event.clientX - dragStartX;
var deltaY = event.clientY - dragStartY;
// 更新画布的位置
canvasX += deltaX;
canvasY += deltaY;
// 更新拖动起点位置
dragStartX = event.clientX;
dragStartY = event.clientY;
drawImageByScale()
}
}
// 鼠标松开拖拽图片
function handleDragMouseUp() {
isDragging = false;
}
// 导出绘制好的图片
function exportImage() {
newCanvasCount += 1
// 创建新的 canvas 元素
var croppedCanvas = document.createElement('canvas');
var croppedCtx = croppedCanvas.getContext('2d');
// 设置新的 canvas 尺寸
croppedCanvas.setAttribute('width', rect.w + 'px')
croppedCanvas.setAttribute('height', rect.h + 'px')
croppedCanvas.setAttribute('id', `newcanvas-${newCanvasCount}`)
document.body.appendChild(croppedCanvas)
// 在新的 canvas 上绘制矩形圈起来的部分
croppedCtx.drawImage(canvas, rect.startX, rect.startY, rect.w, rect.h, 0, 0, rect.w, rect.h);
// console.log('rect', rect);
// 将新的 canvas 导出为图片
var imgData = croppedCanvas.toDataURL('image/png');
// console.log('imgData', imgData);
// 创建一个链接并下载图片
var link = document.createElement('a');
link.href = imgData;
link.download = 'cropped_image.png';
link.click();
}
// 处理移动图片
function handleCanvasMove() {
canvas.addEventListener('mousedown', handleDragMouseDown);
canvas.addEventListener('mousemove', handleDragMouseMove);
document.addEventListener('mouseup', handleDragMouseUp);
}
// 处理缩放图片
function handleCanvsZoom() {
function zoom(event) {
event.preventDefault()
// 根据鼠标滚轮方向更新缩放倍数
if (event.deltaY < 0) {
// 向上滚动,放大画布
scale *= 1.1; // 增加10%
} else {
// 向下滚动,缩小画布
scale /= 1.1; // 减小10%
}
drawImageByScale()
}
// Add event listener for mouse wheel
canvas.addEventListener('wheel', zoom);
}
// 清除监控器
function clearEventListeners() {
canvas.removeEventListener('mousedown', handleDrawRectMouseDown)
canvas.removeEventListener('mousedown', handleDragMouseDown)
canvas.removeEventListener('mousemove', handleDrawRectMouseMove)
canvas.removeEventListener('mousemove', handleDragMouseMove)
canvas.removeEventListener('mouseup', handleDrawRectMouseUp)
canvas.removeEventListener('mouseup', handleDragMouseUp)
}
// 通过当前的缩放比和平移的位置来进行绘制图像
function drawImageByScale() {
// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 保存当前的绘图状态
ctx.save();
// 缩放画布
ctx.scale(scale, scale);
// 平移到当前的画布位置
ctx.translate(canvasX, canvasY);
// 重新绘制画布内容
ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight);
if(isShowtips){
addToolTip()
}
// 恢复之前保存的绘图状态,避免影响到其他绘图操作
ctx.restore();
}
// 添加提示
function addToolTip() {
// 绘制选段
ctx.beginPath();
ctx.moveTo(280, 150);
ctx.lineTo(300, 130);
ctx.lineTo(360, 130);
ctx.strokeStyle = 'pink'
ctx.lineWidth = 3
ctx.stroke();
ctx.beginPath();
// 绘制文字
ctx.fillStyle = 'pink'
ctx.font = "15px Verdana";
ctx.fillText('愛你哦!!!', 310, 120)
}
</script>
</body>
</html>