基于Javascript實(shí)現(xiàn)網(wǎng)頁(yè)版的繪圖板
項(xiàng)目簡(jiǎn)介
這是一個(gè)基于HTML5 Canvas和jQuery實(shí)現(xiàn)的簡(jiǎn)單網(wǎng)頁(yè)版繪圖編輯器。提供了基礎(chǔ)的圖片編輯功能,包括畫筆工具、橡皮擦、亮度/對(duì)比度調(diào)節(jié)等功能??梢杂糜诤?jiǎn)單的圖片編輯和繪圖需求。
主要功能
1. 基礎(chǔ)繪圖工具
畫筆工具:支持自定義顏色和大小
橡皮擦工具:支持自定義大小
撤銷/重做:支持多步操作歷史記錄
2. 圖片處理
圖片上傳:支持上傳本地圖片
圖片調(diào)整:支持亮度和對(duì)比度調(diào)節(jié)
圖片保存:可將編輯后的圖片保存到本地
技術(shù)實(shí)現(xiàn)要點(diǎn)
1. 畫布初始化
const canvas = $('#imageCanvas')[0];
const ctx = canvas.getContext('2d');
使用HTML5 Canvas作為繪圖基礎(chǔ),默認(rèn)設(shè)置白色背景。
2. 繪圖實(shí)現(xiàn)
繪圖功能通過(guò)監(jiān)聽(tīng)鼠標(biāo)事件實(shí)現(xiàn):
- mousedown:開(kāi)始繪制
- mousemove:持續(xù)繪制
- mouseup/mouseleave:結(jié)束繪制
關(guān)鍵代碼:
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. 橡皮擦實(shí)現(xiàn)
橡皮擦通過(guò)設(shè)置 Canvas 的 globalCompositeOperation 屬性為 ‘destination-out’ 來(lái)實(shí)現(xiàn)透明擦除效果:
if (currentTool === 'eraser') {
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
}
4. 圖片處理
圖片上傳后會(huì)自動(dòng)調(diào)整大小以適應(yīng)畫布,保持原始比例:
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. 亮度和對(duì)比度調(diào)節(jié)
使用 Canvas 的 filter 屬性實(shí)現(xiàn):
ctx.filter = `brightness(${brightness}%) contrast(${contrast}%)`;
6. 撤銷/重做功能
通過(guò)保存畫布狀態(tài)實(shí)現(xiàn):
function saveState() {
undoStack.push(canvas.toDataURL());
redoStack = [];
updateUndoRedoButtons();
}
使用說(shuō)明
1.上傳圖片:點(diǎn)擊"上傳圖片"按鈕選擇本地圖片
2.基礎(chǔ)編輯:
- 使用畫筆工具進(jìn)行繪制
- 使用橡皮擦工具擦除內(nèi)容
- 調(diào)節(jié)亮度和對(duì)比度滑塊修改圖片效果
3.撤銷/重做:使用對(duì)應(yīng)按鈕進(jìn)行操作歷史管理
4.保存圖片:點(diǎn)擊"保存"按鈕將結(jié)果保存為PNG格式
技術(shù)依賴
HTML5 Canvas
jQuery 3.7.1
現(xiàn)代瀏覽器(支持ES6+)
改進(jìn)方向
1. 功能擴(kuò)展
- 添加圖片旋轉(zhuǎn)/翻轉(zhuǎn)功能
- 實(shí)現(xiàn)圖片裁剪功能
- 添加更多濾鏡效果
- 實(shí)現(xiàn)圖層功能
2. 用戶體驗(yàn)優(yōu)化
- 添加快捷鍵支持
- 優(yōu)化工具切換交互
- 添加操作提示
- 完善錯(cuò)誤處理
3. 性能優(yōu)化
- 優(yōu)化大圖片處理
- 改進(jìn)撤銷/重做機(jī)制
- 添加操作狀態(tài)緩存
- 優(yōu)化Canvas渲染性能
注意事項(xiàng)
圖片上傳大小沒(méi)有限制,但建議不要超過(guò)5MB
保存的圖片格式為PNG,支持透明度
瀏覽器需要支持Canvas的相關(guān)API
完整代碼
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>網(wǎng)頁(yè)版圖片編輯器</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">對(duì)比度:</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">畫筆大?。?lt;/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 = [];
// 初始化畫布,設(shè)置白色背景
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);
}
});
// 圖片調(diào)整
function applyAdjustments() {
if (!originalImage) return;
const brightness = $('#brightnessRange').val();
const contrast = $('#contrastRange').val();
// 計(jì)算保持寬高比的縮放
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();
});
// 設(shè)置初始畫筆屬性
ctx.strokeStyle = $('#brushColor').val();
ctx.lineWidth = $('#brushSize').val();
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
});
</script>
</body>
</html>
到此這篇關(guān)于基于Javascript實(shí)現(xiàn)網(wǎng)頁(yè)版的繪圖板的文章就介紹到這了,更多相關(guān)Javascript繪圖板內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前端實(shí)現(xiàn)一個(gè)高精準(zhǔn)定時(shí)器和延時(shí)器的完整代碼
定時(shí)器和延時(shí)器是非常常用的功能,用于在指定的時(shí)間后執(zhí)行某些操作,這篇文章主要介紹了前端實(shí)現(xiàn)一個(gè)高精準(zhǔn)定時(shí)器和延時(shí)器的完整代碼,需要的朋友可以參考下2026-03-03
JavaScript 上傳文件(psd,壓縮包等),圖片,視頻的實(shí)現(xiàn)方法
本文通過(guò)實(shí)例代碼給大家介紹了JavaScript 上傳文件(psd,壓縮包等),圖片,視頻功能,需要的朋友可以參考下2017-06-06
原生JavaScript實(shí)現(xiàn)五子棋游戲
這篇文章主要為大家詳細(xì)介紹了原生JavaScript實(shí)現(xiàn)五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
教你JS更簡(jiǎn)單的獲取表單中數(shù)據(jù)(formdata)
這篇文章主要介紹了JS更簡(jiǎn)單的獲取表單中數(shù)據(jù)(formdata),本文給大家分享的js獲取表單數(shù)據(jù)更簡(jiǎn)潔,通過(guò)兩種方法結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05
javascript中先加加和后加加區(qū)別 ++a和a++區(qū)別解析
從學(xué)習(xí) javascript 開(kāi)始,就對(duì) 先加加 和 后加加 模糊不清,時(shí)至今日,再來(lái)學(xué)習(xí)一下,這篇文章主要介紹了javascript中先加加和后加加區(qū)別++a和a++區(qū)別解析,需要的朋友可以參考下2023-09-09
ie瀏覽器使用js導(dǎo)出網(wǎng)頁(yè)到excel并打印
簡(jiǎn)單介紹一種可以使用簡(jiǎn)單的JS來(lái)實(shí)現(xiàn)把網(wǎng)頁(yè)中的信息原樣導(dǎo)出到Excel、還可以打印的方法,需要的朋友可以參考下2014-03-03
Parcel.js + Vue 2.x 極速零配置打包體驗(yàn)教程
這篇文章主要介紹了Parcel.js + Vue 2.x 極速零配置打包體驗(yàn) 的相關(guān)資料,需要的朋友可以參考下2017-12-12

