项目简介
这是一个基于HTML5 Canvas和jQuery实现的简单网页版绘图编辑器。提供了基础的图片编辑功能,包括画笔工具、橡皮擦、亮度/对比度调节等功能。可以用于简单的图片编辑和绘图需求。
主要功能
1. 基础绘图工具
- 画笔工具:支持自定义颜色和大小
- 橡皮擦工具:支持自定义大小
- 撤销/重做:支持多步操作历史记录
2. 图片处理
- 图片上传:支持上传本地图片
- 图片调整:支持亮度和对比度调节
- 图片保存:可将编辑后的图片保存到本地
技术实现要点
1. 画布初始化
const canvas = $('#imageCanvas')[0];
const ctx = canvas.getContext('2d');
使用HTML5 Canvas作为绘图基础,默认设置白色背景。
2. 绘图实现
绘图功能通过监听鼠标事件实现:
mousedown
:开始绘制mousemove
:持续绘制mouseup/mouseleave
:结束绘制
关键代码:
function startDrawing(e) {isDrawing = true;ctx.beginPath();if (currentTool === 'eraser') {ctx.save();ctx.globalCompositeOperation = 'destination-out';} else {ctx.globalCompositeOperation = 'source-over';}ctx.moveTo(e.offsetX, e.offsetY);
}
3. 橡皮擦实现
橡皮擦通过设置 Canvas 的 globalCompositeOperation
属性为 ‘destination-out’ 来实现透明擦除效果:
if (currentTool === 'eraser') {ctx.save();ctx.globalCompositeOperation = 'destination-out';
}
4. 图片处理
图片上传后会自动调整大小以适应画布,保持原始比例:
const scale = Math.min(canvas.width / img.width, canvas.height / img.height);
const x = (canvas.width - img.width * scale) / 2;
const y = (canvas.height - img.height * scale) / 2;
5. 亮度和对比度调节
使用 Canvas 的 filter 属性实现:
ctx.filter = `brightness(${brightness}%) contrast(${contrast}%)`;
6. 撤销/重做功能
通过保存画布状态实现:
function saveState() {undoStack.push(canvas.toDataURL());redoStack = [];updateUndoRedoButtons();
}
使用说明
- 上传图片:点击"上传图片"按钮选择本地图片
- 基础编辑:
- 使用画笔工具进行绘制
- 使用橡皮擦工具擦除内容
- 调节亮度和对比度滑块修改图片效果
- 撤销/重做:使用对应按钮进行操作历史管理
- 保存图片:点击"保存"按钮将结果保存为PNG格式
技术依赖
- HTML5 Canvas
- jQuery 3.7.1
- 现代浏览器(支持ES6+)
改进方向
1. 功能扩展
- 添加图片旋转/翻转功能
- 实现图片裁剪功能
- 添加更多滤镜效果
- 实现图层功能
2. 用户体验优化
- 添加快捷键支持
- 优化工具切换交互
- 添加操作提示
- 完善错误处理
3. 性能优化
- 优化大图片处理
- 改进撤销/重做机制
- 添加操作状态缓存
- 优化Canvas渲染性能
注意事项
- 图片上传大小没有限制,但建议不要超过5MB
- 保存的图片格式为PNG,支持透明度
- 浏览器需要支持Canvas的相关API
完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>网页版图片编辑器</title><!-- 引入 jQuery --><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script><style>body {font-family: Arial, sans-serif;margin: 20px;background-color: #f0f0f0;}.container {max-width: 1200px;margin: 0 auto;}.toolbar {background: white;padding: 15px;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);margin-bottom: 20px;}.toolbar button {padding: 8px 15px;margin: 0 5px;border: 1px solid #ddd;border-radius: 4px;background: #fff;cursor: pointer;transition: all 0.3s ease;}.toolbar button:hover {background: #f5f5f5;}.toolbar button.active {background: #e0e0e0;}.canvas-container {position: relative;margin: 20px 0;background: white;padding: 20px;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);}#imageCanvas {border: 1px solid #ddd;margin: 0 auto;display: block;}.controls {background: white;padding: 15px;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);margin-top: 20px;}.control-group {margin: 10px 0;}.control-group label {display: block;margin-bottom: 5px;}input[type="range"] {width: 200px;}input[type="color"] {width: 50px;height: 30px;padding: 0;border: none;}.hidden {display: none;}</style>
</head>
<body><div class="container"><div class="toolbar"><input type="file" id="imageInput" class="hidden" accept="image/*"><button id="uploadBtn">上传图片</button><button id="undoBtn" disabled>撤销</button><button id="redoBtn" disabled>重做</button><button id="saveBtn" disabled>保存</button><button id="brushBtn">画笔</button><button id="eraserBtn">橡皮擦</button></div><div class="canvas-container"><canvas id="imageCanvas" width="800" height="600"></canvas></div><div class="controls"><div class="control-group"><label for="brightnessRange">亮度:</label><input type="range" id="brightnessRange" min="0" max="200" value="100"><span id="brightnessValue">100%</span></div><div class="control-group"><label for="contrastRange">对比度:</label><input type="range" id="contrastRange" min="0" max="200" value="100"><span id="contrastValue">100%</span></div><div class="control-group"><label for="brushSize">画笔大小:</label><input type="range" id="brushSize" min="1" max="50" value="5"><span id="brushSizeValue">5px</span></div><div class="control-group"><label for="brushColor">画笔颜色:</label><input type="color" id="brushColor" value="#000000"></div></div></div><script>$(document).ready(function() {const canvas = $('#imageCanvas')[0];const ctx = canvas.getContext('2d');let isDrawing = false;let currentTool = 'brush';let originalImage = null;let undoStack = [];let redoStack = [];// 初始化画布,设置白色背景ctx.fillStyle = 'white';ctx.fillRect(0, 0, canvas.width, canvas.height);saveState();// 工具选择$('#brushBtn').click(function() {currentTool = 'brush';$(this).addClass('active');$('#eraserBtn').removeClass('active');});$('#eraserBtn').click(function() {currentTool = 'eraser';$(this).addClass('active');$('#brushBtn').removeClass('active');});// 绘图功能function startDrawing(e) {isDrawing = true;ctx.beginPath();if (currentTool === 'eraser') {ctx.save();ctx.globalCompositeOperation = 'destination-out';} else {ctx.globalCompositeOperation = 'source-over';}ctx.moveTo(e.offsetX, e.offsetY);}function draw(e) {if (!isDrawing) return;if (currentTool === 'eraser') {ctx.save();ctx.globalCompositeOperation = 'destination-out';} else {ctx.globalCompositeOperation = 'source-over';}ctx.lineTo(e.offsetX, e.offsetY);ctx.stroke();if (currentTool === 'eraser') {ctx.restore();}}function stopDrawing() {if (isDrawing) {isDrawing = false;ctx.closePath();if (currentTool === 'eraser') {ctx.restore();}saveState();}}$('#imageCanvas').mousedown(startDrawing).mousemove(draw).mouseup(stopDrawing).mouseleave(stopDrawing);// 画笔控制$('#brushColor').change(function() {ctx.strokeStyle = $(this).val();});$('#brushSize').on('input', function() {const size = $(this).val();ctx.lineWidth = size;$('#brushSizeValue').text(size + 'px');});// 图片上传处理$('#uploadBtn').click(function() {$('#imageInput').click();});$('#imageInput').change(function(e) {const file = e.target.files[0];if (file) {const reader = new FileReader();reader.onload = function(event) {const img = new Image();img.onload = function() {// 保持图片比例const scale = Math.min(canvas.width / img.width, canvas.height / img.height);const x = (canvas.width - img.width * scale) / 2;const y = (canvas.height - img.height * scale) / 2;ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.drawImage(img, x, y, img.width * scale, img.height * scale);originalImage = img;saveState();$('#saveBtn').prop('disabled', false);};img.src = event.target.result;};reader.readAsDataURL(file);}});// 图片调整function applyAdjustments() {if (!originalImage) return;const brightness = $('#brightnessRange').val();const contrast = $('#contrastRange').val();// 计算保持宽高比的缩放const scale = Math.min(canvas.width / originalImage.width, canvas.height / originalImage.height);const x = (canvas.width - originalImage.width * scale) / 2;const y = (canvas.height - originalImage.height * scale) / 2;ctx.filter = `brightness(${brightness}%) contrast(${contrast}%)`;ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.drawImage(originalImage, x, y, originalImage.width * scale, originalImage.height * scale);ctx.filter = 'none';saveState();}$('#brightnessRange, #contrastRange').on('input', function() {const value = $(this).val();const id = $(this).attr('id');$(`#${id}Value`).text(value + '%');applyAdjustments();});// 撤销/重做功能function saveState() {undoStack.push(canvas.toDataURL());redoStack = [];updateUndoRedoButtons();}function updateUndoRedoButtons() {$('#undoBtn').prop('disabled', undoStack.length <= 1);$('#redoBtn').prop('disabled', redoStack.length === 0);}$('#undoBtn').click(function() {if (undoStack.length <= 1) return;redoStack.push(undoStack.pop());const lastState = undoStack[undoStack.length - 1];loadState(lastState);updateUndoRedoButtons();});$('#redoBtn').click(function() {if (redoStack.length === 0) return;const nextState = redoStack.pop();undoStack.push(nextState);loadState(nextState);updateUndoRedoButtons();});function loadState(state) {const img = new Image();img.onload = function() {ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.drawImage(img, 0, 0);};img.src = state;}// 保存功能$('#saveBtn').click(function() {const link = document.createElement('a');link.download = '编辑后的图片.png';link.href = canvas.toDataURL();link.click();});// 设置初始画笔属性ctx.strokeStyle = $('#brushColor').val();ctx.lineWidth = $('#brushSize').val();ctx.lineCap = 'round';ctx.lineJoin = 'round';});</script>
</body>
</html>
本项目仅用于学习和参考。