canvas实现简易画板

news2024/11/22 6:59:35
效果图如下:

在这里插入图片描述

实现功能:
1、改变画笔粗细
2、保存签名实现下载功能
3、使用橡皮擦功能
4、清空画布
5、改变画笔颜色

实现代码如下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>canvas实现画板</title>
    <style>
     
        .btn{
            width: 600px;
            display: flex;
            align-items: center;
            justify-content: space-around;
            
        }
        .btn span{
            padding: 5px 10px;
            border: 2px solid pink;
            cursor: pointer;
        }
        .btn span:not(:last-child){
            margin-right: 10px;
        }
        .btn input[type=color]{
            height: 36px;
            margin-right: 10px;
            cursor: pointer;
        }
        canvas{
            border: 2px solid #000;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="btn">
        <span class="boldBtn">粗线条</span>
        <span class="sinBtn">细线条</span>
        <span class="saveBtn">保存签名</span>
        <span class="clearBtn">橡皮擦</span>
        <input class="colorBtn" type="color">
        <span class="clearCanvasBtn">清空画布</span>
    </div>
    <script>
        //创建画布
        let canvas = document.createElement('canvas');
        canvas.width = 600;
        canvas.height = 400;
        document.body.appendChild(canvas)
        let ctx = canvas.getContext('2d');
        ctx.lineCap = 'round';
        ctx.lineJoin = 'round'
        ctx.fillStyle = "#fff"

        //获取按钮
        let boldBtn = document.querySelector('.boldBtn');
        let sinBtn = document.querySelector('.sinBtn');
        let saveBtn = document.querySelector('.saveBtn');
        let clearBtn = document.querySelector('.clearBtn');
        let clearCanvasBtn = document.querySelector('.clearCanvasBtn');
        let colorBtn = document.querySelector('.colorBtn');

        let isDraw = false;

        //鼠标按下
        canvas.onmousedown = function(e){
            isDraw = true;
            let x = e.pageX;
            let y = e.pageY;
            // console.log(x,y);
            // 获取鼠标在画布内的坐标
            x = x - canvas.offsetLeft;
            y = y - canvas.offsetTop;
            //画线起点坐标
            ctx.beginPath()
            ctx.moveTo(x,y)
        }
        //鼠标弹起
        canvas.onmouseup = function(){
            //恢复为默认
            ctx.globalCompositeOperation ="source-over";
            isDraw = false;
            //结束路径
            ctx.closePath()
        }
        //鼠标离开
        canvas.onmouseleave = function(){
            ctx.globalCompositeOperation ="source-over";
            isDraw = false;
            ctx.closePath()
        }

        //鼠标移动
        canvas.onmousemove = function(e){
            if (!isDraw) return;
            let x = e.pageX;
            let y = e.pageY;
            // console.log(x,y);
            // 获取鼠标在画布内的坐标
            x = x - canvas.offsetLeft;
            y = y - canvas.offsetTop;
            ctx.lineTo(x,y)
            ctx.stroke()
            
        }

        //粗线条
        boldBtn.onclick = function(){
            ctx.lineWidth = 20
        }

        //细线条
        sinBtn.onclick = function(){
            console.log(ctx);
            ctx.lineWidth = 1
        }

        //改变颜色
        colorBtn.onchange = function(){
            console.log(colorBtn.value);
            //获取颜色改变值
            ctx.strokeStyle = colorBtn.value
        }

        //  清空画布
        clearCanvasBtn.onclick = function(){
            //恢复为默认
            ctx.clearRect(0,0,600,400)
        }
        // 橡皮擦
        clearBtn.onclick = function(){
            ctx.globalCompositeOperation ="destination-out";
            ctx.lineWidth = 30;
        }
        //保存签名
        saveBtn.onclick = function(){
            let url = canvas.toDataURL()
            console.log(url);
            //下载签名
            let a = document.createElement('a');
            a.href = url;
            a.download = '签名'
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a)
        }
        
    </script>
</body>
</html>

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

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

相关文章

ChatGPT新功能曝光:可记住用户信息、上传文件和工作区

&#x1f989; AI新闻 &#x1f680; ChatGPT新功能曝光&#xff1a;可记住用户信息、上传文件和工作区 摘要&#xff1a;一张神秘截图曝光了ChatGPT新功能&#xff0c;包括可记住用户信息的"My profile"、上传和管理文件的"My files"以及可以让AI使用不…

从Wi-Fi,蓝牙,到4G,5G,到卫星网络,频谱共享已无处不在

在智能手机像牙刷一样普及的今天&#xff0c;频谱共享&#xff0c;成为了近些年通信界的一个热词儿。频谱因为通信的重要而变得越发重要&#xff0c;又因为频谱是一种稀缺资产&#xff0c;而使用需求又在日益飞速地增长&#xff0c;所以成为重中之重。智能手机、物联网、军事和…

【QT】枚举常用宏到底有什么作用?(Q_ENUM,Q_FLAG,Q_DECLARE_FLAGS,Q_DECLARE_OPERATORS_FOR_FLAGS)

目录 1. Q_ENUM宏 与 QMetaEnum类1.1 Q_ENUM宏的作用1.2 使用Q_ENUM注意的问题1.3 在写有关枚举的代码时&#xff0c;我们可能遇到这种情况&#xff1a;需要用到枚举的字符串&#xff0c;该怎么办&#xff1f;1.4 下面通过一段简单的代码来说明Q_ENUM的作用 2. Q_FLAG宏2.1 Q_F…

satellite: 利用TLE动态计算并实时显示多颗卫星的位置及轨迹

本示例的目的是介绍演示如何在vue+satellite项目中利用两行根数动态地计算,并显示多个卫星的位置及轨迹。每秒钟更新一下卫星的位置和角度,加载当前时间到固定时间(如720分钟后)的一段轨迹。 直接复制下面的 vue+openlayers源示例代码,操作2分钟即可运行实现效果 文章目…

DDD领域驱动设计基本理解

DDD是一种软件设计思想和方法论&#xff0c;以领域为核心构建软件设计体系&#xff0c;将业务模型抽象成领域模型进行拆解和封装。本文简要介绍DDD的基本概念和常用的分层设计架构&#xff0c;并结合业务场景进行领域驱动设计的实战分析&#xff0c;以加深理解。 1、DDD领域驱动…

opencv通过轮廓去除虚线

思路&#xff1a; 将虚线膨胀为实线&#xff0c;通过高度和宽度找到轮廓&#xff0c;再将轮廓内的面积涂白色 img cv2.imread(imagePath) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray_test gray.copy() binary_test cv2.adaptiveThreshold(clean_gray(gray_test),25…

做项目,最难搞定的不是甲方爸爸...

早上好&#xff0c;我是老原。 前几天和一个老朋友吃饭的时候&#xff0c;他和我吐槽他上个月做的一个项目&#xff0c;实在太累了&#xff0c;几乎是没日没夜地赶进度&#xff0c;身体都快垮了。 我问他既然时间来不及&#xff0c;为什么不前期就和客户沟通好。 他说其实客…

uni-app 的使用体验总结

框架简介 uni-app 是一个使用 Vue.js (opens new window)开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝&#xff09;…

【自然语言处理】COLD:中文攻击性言论检测数据集

COLD&#xff1a;A Benchmark for Chinese Offensive Language Detection 文章目录 COLD&#xff1a;A Benchmark for Chinese Offensive Language Detection1 论文出处2 背景2.1 背景介绍2.2 针对问题2.3 创新点 3 数据集构建3.1 数据源3.2 效率改进3.3 数据集分析 4 实验设计…

驱动开发:内核物理内存寻址读写

在某些时候我们需要读写的进程可能存在虚拟内存保护机制&#xff0c;在该机制下用户的CR3以及MDL读写将直接失效&#xff0c;从而导致无法读取到正确的数据&#xff0c;本章我们将继续研究如何实现物理级别的寻址读写。 首先&#xff0c;驱动中的物理页读写是指在驱动中直接读…

LiveGBS流媒体平台GB/T28181功能-海康大华宇视华为NVR等4G摄像头自带物联网卡注册国标平台后看不到设备的时候如何排查及抓包

LiveGBS流媒体平台GB/T28181功能-海康大华宇视华为NVR等4G摄像头自带物联网卡注册国标平台后看不到设备的时候如何排查及抓包 1、设备注册后查看不到1.1、是否是4G|5G摄像头1.2、关闭萤石云1.3、防火墙排查1.4、端口排查1.5、IP地址排查1.6、设备TCP/IP配置排查1.7、设备多网卡…

【Nexus】Maven从Nexus中下载jar包

目录 一、前言二、配置Apache Maven1、在Maven的settings.xml中添加一个镜像配置&#xff0c;并覆盖中央仓库的默认配置 二、创建Maven项目&#xff0c;配置pom文件拉取Nexus中的jar包1、确定配置的Maven的settings.xml是否是上一步修改的settings.xml文件&#xff0c;以及repo…

规划地类、用途分区、空间管制区代码对应表

规划地类、用途分区、空间管制区代码对应表 —the—end—

UE5 与 C++ 入门教程·第一课:角色与 Enhanced Input

本文主要围绕 UE5 新的输入系统&#xff0c;手把手从 0 搭建 Unreal 项目&#xff0c;实现角色的基础移动。 重要提示&#xff1a;众所周知&#xff0c;C 属于编译型语言&#xff0c;因此动态灵活性不足&#xff0c;不过执行效率高&#xff0c;而蓝图简单灵活&#xff0c;却执行…

探索TCC:释放高可用性和弹性事务的潜力

1、TCC简介 分布式事务是指在分布式系统中&#xff0c;多个服务之间需要保证数据的一致性和完整性的场景。传统的单机事务无法满足分布式系统的需求&#xff0c;因此需要引入一种新的事务模型来解决分布式事务问题。 TCC&#xff08;Try-Confirm-Cancel&#xff09;是一种基于…

MySQL的分库分表

分必要不要分库分表&#xff08;通过优化之后还明显影响业务再分&#xff0c;可以通过监控慢查询确定&#xff09; 分库分表的一般条件:单表数据量超过1000w&#xff08;阿里应该是说5000w&#xff09;或者单表数据文件(.ibd)超过20GB&#xff0c;这个很重要&#xff0c;&…

点云配准综述一篇综述《A comprehensive survey on point cloud registration》(翻译)

参照了 2021最新关于点云配准的全面综述 - 知乎&#xff0c;并且加了些自己翻译&#xff0c;全篇的内容可能稍有删减。主要作为个人笔记&#xff0c;阅读了几篇综述&#xff0c;发现这篇是质量较好的&#xff0c;值得花时间细读。 文章分类 文章将配准方法分为了同源配准和不…

JMeter三大重要组件——线程组、取样器、查看结果数(3)

JMeter三大重要组件 一、JMeter三大重要组件——线程组1、作用&#xff1a;JMeter主要通过线程组来运行用户脚本2、在取样器错误后要执行的动作&#xff1a;3、线程属性3、调度器4、setUp线程组和tearDown线程组 二、JMeter三大重要组件——取样器1、基本a、自动重定向和跟随重…

Obsidian多端同步插件LiveSync

网友 Leo 和 Paco反馈&#xff0c;群晖升级到 DSM7.2 &#xff0c;注册表可以搜索镜像&#xff0c;根据 Leo 贴的 /var/packages/Docker/etc/dockerd.json 的内容&#xff0c;DSM7.2 应该是使用了 https://docker.nju.edu.cn 作为注册表镜像&#xff0c;但老苏测试过下面几种情…

易基因:易基因近期染色质免疫共沉淀测序(ChIP-seq)研究成果|项目集锦

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 在生物学研究中&#xff0c;DNA与蛋白质之间的互作&#xff08;DNA-Protein Interactions&#xff0c;DPIs&#xff09;是至关重要的&#xff0c;参与基因的表达、调控、复制、重组和修复…