基于Javascript實(shí)現(xiàn)網(wǎng)頁版的繪圖板
項(xiàng)目簡介
這是一個(gè)基于HTML5 Canvas和jQuery實(shí)現(xiàn)的簡單網(wǎng)頁版繪圖編輯器。提供了基礎(chǔ)的圖片編輯功能,包括畫筆工具、橡皮擦、亮度/對(duì)比度調(diào)節(jié)等功能??梢杂糜诤唵蔚膱D片編輯和繪圖需求。
主要功能
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)
繪圖功能通過監(jiān)聽鼠標(biāo)事件實(shí)現(xiàn):
- mousedown:開始繪制
- 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)
橡皮擦通過設(shè)置 Canvas 的 globalCompositeOperation 屬性為 ‘destination-out’ 來實(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. 撤銷/重做功能
通過保存畫布狀態(tài)實(shí)現(xiàn):
function saveState() {
undoStack.push(canvas.toDataURL());
redoStack = [];
updateUndoRedoButtons();
}
使用說明
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)
圖片上傳大小沒有限制,但建議不要超過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)頁版圖片編輯器</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)頁版的繪圖板的文章就介紹到這了,更多相關(guān)Javascript繪圖板內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
layui checkbox默認(rèn)選中,獲取選中值,清空所有選中項(xiàng)的例子
今天小編就為大家分享一篇layui checkbox默認(rèn)選中,獲取選中值,清空所有選中項(xiàng)的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09
微信小程序?qū)崿F(xiàn)獲取準(zhǔn)確的騰訊定位地址功能示例
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)獲取準(zhǔn)確的騰訊定位地址功能,結(jié)合實(shí)例形式詳細(xì)分析了微信小程序使用騰訊地理位置接口的相關(guān)注冊(cè)、操作步驟及接口使用技巧,需要的朋友可以參考下2019-03-03
IE6/7/8中Option元素未設(shè)value時(shí)Select將獲取空字符串
可以看到當(dāng)忘記寫option的value時(shí)這些現(xiàn)代瀏覽器都會(huì)盡量返回正確的(客戶端程序員想要的)結(jié)果value,其容錯(cuò)性比IE6/7/8做的更好。2011-04-04
JavaScript中的property和attribute介紹
JavaScript中的property和attribute介紹,需要的朋友可以參考下。2011-12-12
uniapp開發(fā)小程序的開發(fā)規(guī)范總結(jié)
uni-app 是一個(gè)使用 vue.js 開發(fā)跨平臺(tái)應(yīng)用的前端框架,下面這篇文章主要給大家介紹了關(guān)于uniapp開發(fā)小程序的開發(fā)規(guī)范,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
Bootstrap3 datetimepicker控件使用實(shí)例
這篇文章主要為大家詳細(xì)介紹了Bootstrap3 datetimepicker控件使用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
JavaScript結(jié)合HTML DOM實(shí)現(xiàn)聯(lián)動(dòng)菜單
這篇文章主要為大家詳細(xì)介紹了JavaScript結(jié)合HTML DOM實(shí)現(xiàn)聯(lián)動(dòng)菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
JavaScript字符串操作的四個(gè)實(shí)用技巧
在制作前端頁面的過程中,經(jīng)常需要用到JavaScript進(jìn)行邏輯處理,很多時(shí)候都需要對(duì)字符串進(jìn)行操作,這篇文章主要給大家介紹了關(guān)于JavaScript字符串操作的四個(gè)實(shí)用技巧,需要的朋友可以參考下2021-07-07
ASP中進(jìn)行HTML數(shù)據(jù)及JS數(shù)據(jù)編碼函數(shù)
在有些時(shí)候我們無法控制亂碼的出現(xiàn), 比如發(fā)送郵件的時(shí)候有些郵件顯示亂碼, 比如Ajax返回?cái)?shù)據(jù)總是亂碼. 怎么辦?2009-11-11

