Canvas:实现在线画板操作

news2024/9/24 7:21:12

想象一下,用几行代码就能创造出如此逼真的图像和动画,仿佛将艺术与科技完美融合,前端开发的Canvas技术正是这个数字化时代中最具魔力的一环,它不仅仅是网页的一部分,更是一个无限创意的画布,一个让你的想象力自由驰骋的平台。

目录

基础页面搭建

绘画操作

按钮点击事件

最后总结


基础页面搭建

接下来借助canvas实现一个简易的画版操作,首先这里我们设置一下画布的一些基础布局样式:

<body>
    <canvas id="canvas" width="800" height="600" style="border: 1px solid #ccc;"></canvas>
    <hr>
    <button id="boldBtn" type="button">粗线条</button>
    <button id="thinBtn" type="button">细线条</button>
    <button id="saveBtn" type="button">保持签名</button>
    <input type="color" name="" id="color" value="">
    <button class="clearBtn">橡皮擦</button>
    <button id="nullBtn">清空画布</button>
</body>

实现的效果如下所示,一个比较简陋的画布,以及一些基础操作的按钮:

绘画操作

接下来我们开始实现绘画操作的功能,通过监听鼠标不同情况的事件来实现画布上书写内容的操作,首先这里我们先绘制画布以及获取对应的dom元素进行操作:

<script>
    // 获取canvas画布绘制上下文对象以及获取2d画笔对象
    let canvas = document.getElementById("canvas");      
    let ctx = canvas.getContext("2d");
    ctx.lineJoin = 'round'; // 线条连接处为圆角
    ctx.lineCap = 'round'; // 线条端点为圆角

    // 获取DOM元素
    let boldBtn = document.getElementById("boldBtn");
    let thinBtn = document.getElementById("thinBtn");
    let saveBtn = document.getElementById("saveBtn");
    let color = document.getElementById("color");
    let clearBtn = document.getElementsByClassName("clearBtn")[0];
    let nullBtn = document.getElementById("nullBtn");
</script>

接下来开始开始监听鼠标的按下、弹起、移动以及离开画布的四种情况的操作,完整代码如下:

<script>
    // 获取canvas画布绘制上下文对象以及获取2d画笔对象
    let canvas = document.getElementById("canvas");      
    let ctx = canvas.getContext("2d");
    ctx.lineJoin = 'round'; // 线条连接处为圆角
    ctx.lineCap = 'round'; // 线条端点为圆角

    // 获取DOM元素
    let boldBtn = document.getElementById("boldBtn");
    let thinBtn = document.getElementById("thinBtn");
    let saveBtn = document.getElementById("saveBtn");
    let color = document.getElementById("color");
    let clearBtn = document.getElementsByClassName("clearBtn")[0];
    let nullBtn = document.getElementById("nullBtn");

    // 设置是否开始绘画的变量
    let isDraw = false;

    // 鼠标按下去函数
    canvas.onmousedown = function(e) {
        isDraw = true; // 开始绘画
        ctx.beginPath(); // 开始路径
        // 获取鼠标点击的坐标
        let x = e.pageX - canvas.offsetLeft; 
        let y = e.pageY - canvas.offsetTop; 
        ctx.moveTo(x, y); // 移动到当前点
    }
    // 鼠标移动函数
    canvas.onmousemove = function(e) {
        if (isDraw) { // 如果开始绘画
            let x = e.pageX - canvas.offsetLeft; 
            let y = e.pageY - canvas.offsetTop; 
            ctx.lineTo(x, y); // 绘制
            ctx.stroke()
        }
    }
    // 鼠标弹起函数
    canvas.onmouseup = function(e) {
        isDraw = false; // 结束绘画
        ctx.closePath(); // 结束路径
    }
    // 鼠标离开画布函数
    canvas.onmouseleave = function(e) {
        isDraw = false; // 结束绘画
        ctx.closePath(); // 结束路径
    }
</script>

最终呈现的效果如下所示:

按钮点击事件

接下来给画布下方的按钮设置对应的点击事件,让其能够实现对应的功能,如下:

粗线条:这里设置点击事件函数如下,遮盖策略设置在现有画布上下文之上绘制新图形,点击之后宽度增加到20,并添加对应激活类名active,删除其他按钮可能存在的active类名:

// 粗线条函数
boldBtn.onclick = function(e) {
    ctx.globalCompositeOperation = 'source-over'; 
    ctx.lineWidth = 20
    boldBtn.classList.add("active");
    thinBtn.classList.remove("active");
    clearBtn.classList.remove("active");
}

细线条:这里设置点击事件函数如下,遮盖策略设置在现有画布上下文之上绘制新图形,点击之后宽度减少到2,并添加对应激活类名active,删除其他按钮可能存在的active类名:

// 细线条函数
thinBtn.onclick = function(e) {
    ctx.globalCompositeOperation = 'source-over'; 
    ctx.lineWidth = 2
    thinBtn.classList.add("active");
    boldBtn.classList.remove("active");
    clearBtn.classList.remove("active");
}

保存签名:获取图片数据并创建一个a标签进行文件下载:

// 保存签名函数
saveBtn.onclick = function(e) {
    let urlData = canvas.toDataURL(); // 获取图片数据
    // let img = new Image()
    // img.src = urlData; // 图片地址
    // document.body.appendChild(img); // 添加图片到body

    let downloadA = document.createElement("a"); // 创建a标签
    downloadA.setAttribute("download", "签名"); // 设置下载文件名
    downloadA.href = urlData; // 设置a标签的地址
    downloadA.click(); // 点击a标签
}

修改颜色:设置监听函数,动态修改canvas颜色:

// 颜色选择函数
color.onchange = function(e) {
    ctx.strokeStyle = color.value; // 设置线条颜色
}

橡皮擦:这里设置点击事件函数如下,遮盖策略设置现有内容保持在新图形不重叠的地方,点击之后区域呈现空白,并添加对应激活类名active,删除其他按钮可能存在的active类名:

// 橡皮擦函数
clearBtn.onclick = function(e) {
    // 清除画布
    ctx.globalCompositeOperation = 'destination-out';
    ctx.lineWidth = 30
    clearBtn.classList.add("active");
    boldBtn.classList.remove("active");
    thinBtn.classList.remove("active");
}

清空画布:调用canvas函数直接清除整个画布的内容:

// 清空画布函数
nullBtn.onclick = function(e) {
    ctx.clearRect(0, 0, 800, 600);
}

最终呈现的效果如下所示:

最后总结

本次代码实现了一个简单的画板功能,让用户可以在网页上绘制、调整线条粗细、选择颜色、保存签名等操作。以下是代码的实现思路:

1)HTML结构部分

在<body>中包含一个 <canvas> 元素,用于绘制图形。

一组按钮和一个颜色选择器,用于控制画笔的粗细、颜色以及清除和保存绘制的功能。

2)CSS部分

定义了一个按钮的样式 .active,用于表示当前选中状态,例如粗线条、细线条、橡皮擦等按钮会根据点击状态变化背景颜色。

3)JavaScript部分

《1》获取元素:

使用 document.getElementById 和 document.getElementsByClassName 获取 <canvas> 和各种控制按钮。

《2》绘图相关事件:

canvas.onmousedown:鼠标按下时开始绘画,记录起始点。
canvas.onmousemove:鼠标移动时,如果正在绘画(isDraw为真),则根据当前鼠标位置绘制线条。
canvas.onmouseup 和 canvas.onmouseleave:鼠标抬起或离开画布时结束绘画路径。

《3》功能按钮事件:

粗线条 (boldBtn) 和 细线条 (thinBtn):设置线条宽度,并通过 classList 控制按钮的选中状态。
保存签名 (saveBtn):使用 canvas.toDataURL() 获取绘制内容的DataURL,创建一个下载链接,允许用户保存签名。
颜色选择 (color):通过 color.onchange 设置绘制线条的颜色。
橡皮擦 (clearBtn):使用 ctx.globalCompositeOperation = 'destination-out' 实现擦除效果,清除画布上的内容。
清空画布 (nullBtn):使用 ctx.clearRect() 清除整个画布的内容。

《4》绘图上下文:

使用 getContext('2d') 获取 Canvas 2D 渲染上下文对象 ctx,设置线条的样式(圆角连接和端点)。

这段代码整合了 HTML、CSS 和 JavaScript,提供了一个简单而功能丰富的在线绘图工具,用户可以通过点击不同的按钮和操作来绘制、擦除、保存签名等。完整代码如下:

<!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>
        button.active {
            color: #fff;
            background-color: orange;
        }
    </style>
</head>

<body>
    <canvas id="canvas" width="800" height="600" style="border: 1px solid #ccc;"></canvas>
    <hr>
    <button id="boldBtn" type="button">粗线条</button>
    <button id="thinBtn" type="button">细线条</button>
    <button id="saveBtn" type="button">保存签名</button>
    <input type="color" name="" id="color" value="">
    <button class="clearBtn">橡皮擦</button>
    <button id="nullBtn">清空画布</button>
<script>
    // 获取canvas画布绘制上下文对象以及获取2d画笔对象
    let canvas = document.getElementById("canvas");      
    let ctx = canvas.getContext("2d");
    ctx.lineJoin = 'round'; // 线条连接处为圆角
    ctx.lineCap = 'round'; // 线条端点为圆角

    // 获取DOM元素
    let boldBtn = document.getElementById("boldBtn");
    let thinBtn = document.getElementById("thinBtn");
    let saveBtn = document.getElementById("saveBtn");
    let color = document.getElementById("color");
    let clearBtn = document.getElementsByClassName("clearBtn")[0];
    let nullBtn = document.getElementById("nullBtn");

    // 设置是否开始绘画的变量
    let isDraw = false;

    // 鼠标按下去函数
    canvas.onmousedown = function(e) {
        isDraw = true; // 开始绘画
        ctx.beginPath(); // 开始路径
        // 获取鼠标点击的坐标
        let x = e.pageX - canvas.offsetLeft; 
        let y = e.pageY - canvas.offsetTop; 
        ctx.moveTo(x, y); // 移动到当前点
    }
    // 鼠标移动函数
    canvas.onmousemove = function(e) {
        if (isDraw) { // 如果开始绘画
            let x = e.pageX - canvas.offsetLeft; 
            let y = e.pageY - canvas.offsetTop; 
            ctx.lineTo(x, y); // 绘制
            ctx.stroke()
        }
    }
    // 鼠标弹起函数
    canvas.onmouseup = function(e) {
        isDraw = false; // 结束绘画
        ctx.closePath(); // 结束路径
    }
    // 鼠标离开画布函数
    canvas.onmouseleave = function(e) {
        isDraw = false; // 结束绘画
        ctx.closePath(); // 结束路径
    }

    // 粗线条函数
    boldBtn.onclick = function(e) {
        ctx.globalCompositeOperation = 'source-over'; 
        ctx.lineWidth = 20
        boldBtn.classList.add("active");
        thinBtn.classList.remove("active");
        clearBtn.classList.remove("active");
    }
    // 细线条函数
    thinBtn.onclick = function(e) {
        ctx.globalCompositeOperation = 'source-over'; 
        ctx.lineWidth = 2
        thinBtn.classList.add("active");
        boldBtn.classList.remove("active");
        clearBtn.classList.remove("active");
    }
    // 保存签名函数
    saveBtn.onclick = function(e) {
        let urlData = canvas.toDataURL(); // 获取图片数据
        // let img = new Image()
        // img.src = urlData; // 图片地址
        // document.body.appendChild(img); // 添加图片到body

        let downloadA = document.createElement("a"); // 创建a标签
        downloadA.setAttribute("download", "签名"); // 设置下载文件名
        downloadA.href = urlData; // 设置a标签的地址
        downloadA.click(); // 点击a标签
    }
    // 颜色选择函数
    color.onchange = function(e) {
        ctx.strokeStyle = color.value; // 设置线条颜色
    }
    // 橡皮擦函数
    clearBtn.onclick = function(e) {
        // 清除画布
        ctx.globalCompositeOperation = 'destination-out';
        ctx.lineWidth = 30
        clearBtn.classList.add("active");
        boldBtn.classList.remove("active");
        thinBtn.classList.remove("active");
    }
    // 清空画布函数
    nullBtn.onclick = function(e) {
        ctx.clearRect(0, 0, 800, 600);
    }
</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1915295.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前后端如何实现非对称加解密-使用RSA为例讲解!

写在最前面&#xff0c;RSA是一种非对称加密算法&#xff0c;使用不同的公钥和私钥进行加密和解密。 下面是使用RSA进行加密和解密的代码示例&#xff1a; 前端&#xff1a;使用CryptoJS进行RSA加密 在前端JavaScript中&#xff0c;使用jsencrypt库来进行RSA加密&#xff1a…

04OLED简介和调试方法

OLED简介和调试方法 调试方式串口调试显示屏调试其他调试方法总结&#xff1a; OLED简介硬件电路OLED驱动函数 keil调试模式进入方法keil调试界面窗口简单功能说明更加强大的功能 调试方式 电脑想看什么变量可以直接打印到屏幕&#xff0c;但是单片机很多时候由于成本和电路结构…

充电桩运营平台的技术方案 53页

充电桩运营平台的技术方案 53页&#xff0c;全套解决方案 内容太多&#xff0c;复制图片丢失&#xff0c;需要完整的私信我。

图片kb太大了怎么改小?修改图片kb的方法介绍

图片kb太大了怎么改小&#xff1f;将图片的文件大小&#xff08;以KB为单位&#xff09;缩小可以带来多种优点&#xff0c;但也有一些缺点需要注意。缩小图片文件大小可以显著减少它在硬盘或其他存储设备上占用的空间&#xff0c;使你能够存储更多的图片和其他文件。而且&#…

Postman使用教程【项目实战】

目录 引言软件下载及安装项目开发流程1. 创建项目2. 创建集合(理解为&#xff1a;功能模块)3. 设置环境变量&#xff0c;4. 创建请求5. 测试脚本6. 响应分析7. 共享与协作 结语 引言 Postman 是一款功能强大的 API 开发工具&#xff0c;它可以帮助开发者测试、开发和调试 API。…

制造业中SCADA与MES系统承担着怎样的重任?

SCADA&#xff08;Supervisory Control and Data Acquisition&#xff09;是一种广泛应用于工业控制系统中的监控和数据采集系统。它主要用于监控和控制工业过程中的设备和活动&#xff0c;并收集相关数据以供分析和优化。SCADA系统由人机界面&#xff08;HMI&#xff09;、数据…

提醒我每天打卡的“目标打卡提醒软件”

在繁忙的生活中&#xff0c;我们总有许多需要每日坚持的习惯&#xff0c;无论是为了健康而设定的减肥计划&#xff0c;还是为了工作而必须的每日上班打卡&#xff0c;亦或是为了自我提升而规定的每日学习任务。这些日常打卡活动&#xff0c;虽然重要&#xff0c;但在忙碌和疲惫…

罗马仕和西圣充电宝好用吗?西圣、倍思充电宝实测大PK!

随着智能设备在日常生活中的普及&#xff0c;充电宝成为了人们出行必备的数码配件。市场上充电宝品牌琳琅满目&#xff0c;其中罗马仕、西圣和倍思是较为知名的品牌。它们各自有着什么样的特点和优势呢&#xff1f;为了帮助大家更好地选择适合自己的充电宝&#xff0c;我们特意…

“我没钱”最好别用“I have no money“,老外很少这样说!成人学英语到柯桥泓畅学校

说到没钱都有话说&#xff0c;那我们怎么用英语表达&#xff1f;是不是直接说I have no money&#xff0c;老外表示很少这样说&#xff0c;那下面一起来拓展下&#xff1a; 老外如何地道表达“我没钱”&#xff1f; 当我们想表达“我没钱”时&#xff0c;很多人的第一反应就是…

opencv 中如何通过欧式距离估算实际距离(厘米)

1&#xff1a;这个方法个人测试觉得是正确的&#xff0c;误差较小&#xff0c;目前满足我当前的需求&#xff0c;如果方法不对&#xff0c;请大家评论&#xff0c;完善。 2&#xff1a;确保拍摄的参照物是垂直的&#xff0c;如果不垂直&#xff0c;就会有误差&#xff0c;不垂…

心电信号降噪前处理(MATLAB R2018)

心电信号降噪方法分析 &#xff08;1&#xff09;基线漂移的常用滤除法是拟合基线抵消法&#xff0c;此法先估计或提取信号基线&#xff0c;然后进行减法运算去掉信号中漂移成分&#xff0c;达到滤波的目的。抑制基线漂移的另一种方法是采用线性相位滤波器&#xff0c;因为基线…

什么是 GC Roots??一文带你看懂!!

什么是 GC Roots &#xff1f; JVM 是如何判断哪些对象应该回收&#xff0c;哪些应该保留呢&#xff1f;GC Roots 堆是一个巨大的对象集合&#xff0c;其中包含许多对象实例。 这些对象在堆中有不同的引用层次。一些接口会被频繁调用&#xff0c;每秒生成大量对象。这些对象之间…

【网络安全】实验七(ISA防火墙的规则设置)

一、实验目的 二、配置环境 1、打开两台虚拟机&#xff0c;并参照下图&#xff0c;搭建网络拓扑环境&#xff0c;要求两台虚拟机的IP地址要按照图中的标识进行设置&#xff0c;并根据搭建完成情况&#xff0c;勾选对应选项。注&#xff1a;此处的学号本人学号的最后两位数字&a…

MVC之 Controller 》》 ModelState ValidationMessageFor ValidationSummary

ModelState是Controller的一个属性&#xff0c;可以被继承自System.Web.Mvc.Controller的那些类访问。它表示在一次POST提交中被提交到服务器的 键值对集合&#xff0c;每个记录到ModelState内的值都有一个错误信息集。尽管ModelState的名字中含有“Model”&#xff0c;但它只有…

Monorepo仓库管理策略之 Lerna

这里写目录标题 前言&#xff1a;一、简介二、新建项目使用安装生成结构 三、复用现有项目执行命令查看包 四、配置package相互引用导入现有的包 五、发布包确定项目版本发布项目添加项目到到git发布包到NPM包发布出错解决方案 五、实例代码 前言&#xff1a; 将大型代码仓库分…

中国光储充一体化行业:有望成为全球能源转型的重要驱动力

光储充一体化系统&#xff0c;又称微电网解决方案&#xff0c;系一种整合分布式光伏能源、用电负载管理、配电设施以及监控与保护设备的自给型能源供应体系。该系统核心组件包括光伏发电系统、储能装置及充电站&#xff0c;其工作原理为&#xff1a;光伏发电系统捕获太阳能并转…

Floyd判圈算法——寻找重复数(C++)

287. 寻找重复数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。假设 nums 只有 一个重复的整数 &#xff0c;返…

系统分析师-基础知识

基础知识 一、计算机组成与结构1、计算机系统基础知识1.1 计算机硬件组成1.2 中央处理单元&#xff08;CPU&#xff09;1.3 数据表示1.3.1 R进制转十进制&#xff1a;1.3.2 十进制转R进制&#xff1a; 1.4 校验码&#xff08;3种校验码&#xff09;1.4.1 基本知识1.4.2 奇偶校验…

Security认证要点速记

登录校验流程 springSecurity已经为我们默认实现了一个用不着的登录功能&#xff0c;我们需要自己实现个符合我们需求的登录功能&#xff0c;所以我们需要去了解默认登录功能的流程&#xff0c;对其中的部分进行替换 SpringSecurity底层就是过滤器链&#xff0c;包含实现了各种…

滑动变阻器在实际应用中需要注意哪些安全事项?

滑动变阻器在实际应用中&#xff0c;为了确保其正常运作及保护电路安全&#xff0c;需要注意以下安全事项&#xff1a; 一、了解并遵守规格参数 最大电阻值和允许通过的最大电流值&#xff1a;使用前&#xff0c;必须清楚滑动变阻器的最大电阻值和允许通过的最大电流值&#x…