首页 > 编程语言 >Javascript实现的网页版绘图板

Javascript实现的网页版绘图板

时间:2024-10-30 17:20:18浏览次数:6  
标签:function canvas 网页 img Javascript ctx width const 绘图板

项目简介

这是一个基于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();
}

使用说明

  1. 上传图片:点击"上传图片"按钮选择本地图片
  2. 基础编辑
    • 使用画笔工具进行绘制
    • 使用橡皮擦工具擦除内容
    • 调节亮度和对比度滑块修改图片效果
  3. 撤销/重做:使用对应按钮进行操作历史管理
  4. 保存图片:点击"保存"按钮将结果保存为PNG格式

技术依赖

  • HTML5 Canvas
  • jQuery 3.7.1
  • 现代浏览器(支持ES6+)

改进方向

1. 功能扩展

  • 添加图片旋转/翻转功能
  • 实现图片裁剪功能
  • 添加更多滤镜效果
  • 实现图层功能

2. 用户体验优化

  • 添加快捷键支持
  • 优化工具切换交互
  • 添加操作提示
  • 完善错误处理

3. 性能优化

  • 优化大图片处理
  • 改进撤销/重做机制
  • 添加操作状态缓存
  • 优化Canvas渲染性能

注意事项

  1. 图片上传大小没有限制,但建议不要超过5MB
  2. 保存的图片格式为PNG,支持透明度
  3. 浏览器需要支持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>

本项目仅用于学习和参考。

标签:function,canvas,网页,img,Javascript,ctx,width,const,绘图板
From: https://blog.csdn.net/exlink2012/article/details/143370478

相关文章

  • 【JavaScript】之浏览器对象模型(BOM)详解
    浏览器对象模型(BOM:BrowserObjectModel)是JavaScript的一部分,它允许你与浏览器窗口进行交互。不同于DOM(文档对象模型)主要处理网页内容,BOM关注的是浏览器窗口本身及其各种特性,例如导航、窗口大小、浏览器历史记录等等。就是JavaScript将浏览器的各个组成部分封装成了对......
  • JavaScript 实现对 JSON 对象数组数据进行分页处理
    JavaScript实现对JSON对象数组数据进行分页处理在前端JavaScript中对JSON对象数组进行分页,可以通过以下方式实现:分页函数示例代码假设有一组JSON对象数据,比如一组用户信息:constdata=[{id:1,name:"Alice"},{id:2,name:"Bob"},{id:3,name:"......
  • 虚拟小玩具!推荐一个基于网页技术的3D魔方,摸鱼党快玩(带私活源码)
     楔子魔方,这个词汇对于大家来说应该并不陌生。在儿时的记忆中,我们曾经可以一整天都在玩魔方。然而,随着时间的流逝,我们步入了程序员的行业,每天与电脑的鼠标和键盘为伍。在这个过程中,魔方也与时俱进,从实体玩具转变为装载在电脑中的虚拟小玩具。对制作网页魔方原理感兴趣的......
  • JavaScript基础知识——黑马JavaWeb学习笔记
    JavaScript基础JavaScript:跨平台、面向对象的脚本语言(脚本语言:不需要编译,浏览器解释完直接运行)作用:控制网页行为,使网页可交互ps:JavaScript与Java是两门完全不同的语言本文为学习黑马程序员JavaWeb开发教程中JS部分学习笔记文章目录JavaScript基础一、JS引入方式1.......
  • 国标GB28181视频平台EasyGBS国标GB28181软件实现无需插件的视频监控对讲和网页直播
    在当今社会,视频监控已经成为公共安全、企业管理、智能城市建设等领域不可或缺的一部分。然而,由于不同厂家和平台之间的兼容性问题,视频监控系统的联网和整合面临巨大挑战。为了解决这个问题,国家制定了《公共安全视频监控联网系统信息传输、交换、控制技术要求》(GB/T28181—2016......
  • selenium抓取动态网页数据
    1.selenium抓取动态网页数据基础介绍1.1什么是AJAXAJAX(AsynchronouseJavaScriptAndXML:异步JavaScript和XML)通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新,这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行局部更新。传统的网页(不使用Aj......
  • 前端JavaScript的异步编程:从回调到Promise再到Async/Await
    写在前面在前端开发中,异步编程是一个非常重要的概念。随着JavaScript语言和前端技术的发展,异步编程的模式也在不断演进。本文将带你了解从最初的回调函数(Callback)到Promise,再到现代的Async/Await,这些异步编程模式的演变过程。回调函数(Callback)回调函数是最早期的异步编程......
  • (web查看三维CAD图纸)在三维网页CAD中绘制一个窗户模型
    前言本文使用mxcad3d在网页中创建一个简单的三维窗户模型,mxcad3d提供了丰富的三维建模功能和便捷的API,使得创建各种三维模型变得简单方便,最终效果如下图: 环境搭建和入门首先学习mxcad的基本使用方法,可通过官方的入门教程来搭建一个最基本的项目模板,依次查看教程:安装`Node.js......
  • javascript 数组 filter
    javascript数组filter在JavaScript中,filter方法被用于创建一个新数组,该数组包含通过提供的函数实现的测试的所有元素。解法1:基本使用方法letnumbers=[4,9,16,25,29];letnewNumbers=numbers.filter(num=>num>10);console.log(newNumbers);//......
  • javascript-Web APLs (三)
     事件流指的是事件完整执行过程中的流动路径 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段 简单来说:捕获阶段是从父到子冒泡阶段是从子到父 实际工作都是使用事件冒泡为主事件捕获DOM.addEventListener(事件类型,事件处......