canvas实现简单画板

canvas实现简单画板

前言

canvas 可以实现鼠标或者手指作画。具体用到了 mouse 事件和 touch 事件。下面就实现了一个简单的画板,实际效果如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210704063642713.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hhcm1zd29ydGgyMDE2,size_16,color_FFFFFF,t_70

解析

在页面中,有文字、按钮,有包裹进度条的块。
有如下需求:

  1. 线条能在按钮上显示,并且按钮能点击
  2. 进度条所在 div 块上能作画,并且进度条块能点击

目前已经实现了第一个需求,您可以点击这里:简单画板-进度条不能画线

代码

<!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>简单画板-进度条不能画线</title>
  <style>
    * {
      margin:0;
      padding: 0;
      user-select: none;
    }
    canvas{
      position: fixed;
    }
    p {text-indent: 2em;}
    .big-buttom{
      padding: 10px;
    }
    button{
      cursor: pointer;
    }
    .pz{
      position: relative;
      /* z-index: 1; */
    }
    .btn_wrap{
      height: 100px;
      padding: 20px;
      box-sizing: border-box;
      border: 1px solid #FE8227;
      display: flex;
      margin-bottom: 10px;
    }
    .btn_wrap.process{
      position: relative;
      cursor: pointer;
    }
    .div_btn{
      border: 2px solid #0ac013;
      width: 155px;
      height: 40px;
      line-height: 40px;
      cursor: pointer;
      text-align: center;
      color: #0ac013;
      border-radius: 88px;
      border: 2px solid #e8e8e8;
      padding: 0 20px;
      box-sizing: border-box;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <div>
    <p>
      故乡的春天是我今生的起点,故乡的月亮又是故乡春天的起点,正因为如此,我此生是以故乡月亮行走的方式行走于世界之中,从故乡的泥土上走出来的人们,就算身处异乡,举头望月,那月亮始终是故乡春天那轮月亮的颜色,无法改变!那样的云彩,陪伴着那样皎洁的月色,生长着一点点忧伤,荡漾着一缕缕思念……
    </p>
    <p>
      在缺少蝈蝈叫声的春天夜里,天边那一弯月牙呢?你是谁呀?是陌生又熟悉的你吗,怎么春天总在你清澈的眼神中行走?当你灿烂的笑容递过来的时刻,燕子的剪影在麻雀的叫声里飘舞,似那一缕轻梦,总能轻轻唤醒春风。当我懂得人世间最初感情的时刻,你就挂在春天的树梢上,直到今朝,依然挂在我寂寞的窗口。妻子的月亮,受不了春天潇洒的风寒,躲在家的怀里不肯出门,而你却伸出那只忘情的小手,推窗而入,悄悄走进我的心田。在这样的春天,许多故事入梦了,但是关于你的记忆在漫天飞翔。那是多么撩人的画面,我深陷得不能自己,绵绵恙笛老是陪伴缕缕月色定格于窗户的画框里,无需要春风给力,我能够很快发现了你,我的思念与牵挂一次次验证了我对你的虔诚。虽然,直到今天,你还是你,我还是我,在亲密的间隙里夹着少许陌生,但是依旧改不了那缱绻的味道。不要怀疑我是否是青铜器,古董就古董,在春风门前我算不得高雅,但是也不会作贱自己,否则就没有勇气面对八面来风的春天和春天玲珑剔透的明月了。
    </p>
    <p>
      春天的月亮的是有心思的,深得让人可以跋涉一辈子,月亮的心思只有月亮知道,但是人的心思月亮一定知道,否则她怎么能那样善解人意,跟着人的感觉,走进人的灵魂深处?你看,你看啦,故乡春天的月亮行走着行走着,一不小心就出轨了,掉进故乡清澈的小河里。我不知道,你是不是以出轨的样儿走进我的空间的?成为我忐忑的期盼,但是,毋容置疑你已经活在我的天空里了,不知我是否也能走进你的家园里?我一直坚定的认为:一个人能活在另一个人的心坎里这是多么多么幸福的事情。
    </p>
    <p>
      妻子是土生土长的故乡人,如春天那一轮满月,总是勤勤恳恳挂在家的中央,穿着花围裙,老在厨房奏响锅碗瓢盆交响曲,在洗衣机旁唱响洗衣歌,用纯朴燃烧自己,默默地用爱呵护、坚守、保卫自己的地盘。随着时间的荏苒,我很少端详她认真的模样,只是,每天清早,梳洗打扮一番,真的特靓,堪称精品女人,但是每天下班回家却是灰头土脸。我戏曰:“老婆,你每天打扮得那靓是给别人看的,每天交给我的是一个次品女人。”“那是为你撑面子呗,我的坏蛋蛋!”
      老婆有意无意笑嘻嘻的说。我知道,那里头藏着的东西叫生活,叫日子。一个女人敢把最丑的一面展现在男人面前,那她早就把灵魂交付给了那男人保管了。
      在妻子的劳动中翻开了家的崭新的一页,而妻子面对春天报以憨厚一笑,就如春天那一轮清澈满月,但是,幸福就是在那一刻简单开始了……
    </p>
    <div class="btn_wrap">
      <div>
        <button class="pz big-buttom">大按钮</button>
      </div>
      <div>
        <button class="pz">默认按钮</button>
      </div>
      <div class="div_btn pz">div按钮</div>
    </div>
    <div class="btn_wrap process">
      这里是进度条
      <progress value="22" max="100"></progress>
    </div>
  </div>
  <script>
    const canvasEl = document.querySelector('#canvas')
    // 画板全屏
    const pageWidth = document.documentElement.clientWidth;
    const pageHeight = document.documentElement.clientHeight;
    canvasEl.width = pageWidth;
    canvasEl.height = pageHeight;
    const ctx = canvasEl.getContext('2d')
    ctx.lineWidth = 4
    ctx.lineJoin = 'round'
    ctx.lineCap = 'round'
    ctx.strokeStyle = '#F44336'
    ctx.strokeRect(100, 100, 80, 80);
    const handleCopy = (t) => {
      return {
        id: t.identifier,
        x: t.clientX,
        y: t.clientY
      }
    }

    // 触屏多点操作
    let touchArr = []
    // 当前绘制点
    let activeMouse = {}

    const handleFn = (m, cb) => {
      m.preventDefault();
      [...m.changedTouches].forEach(item => {
        const current = touchArr.find(v => v.id === item.identifier)
        if (current) {
          ctx.beginPath()
          ctx.moveTo(current.x, current.y)
          ctx.lineTo(item.clientX, item.clientY)
          ctx.stroke()
          cb && cb(current, item)
        }
      })
    }

    const touchStart = (m) => {
      m.preventDefault();
      [...m.changedTouches].forEach(item => {
        touchArr.push(handleCopy(item))
      })
    }

    const touchMove = (m) => {
      m.preventDefault()
      const fn = (current, item) => touchArr.splice(touchArr.findIndex(v => v.id === current.id), 1, handleCopy(item))
      handleFn(m, fn)
    }

    const touchEnd = (m) => {
      m.preventDefault()
      const fn = (current) => touchArr.splice(touchArr.findIndex(v => v.id === current.id), 1)
      handleFn(m, fn)
    }

    const mouseStart = (e) => {
      e.preventDefault()
      activeMouse = { x: e.clientX, y: e.clientY }
    }

    const mouseMove = (e) => {
      e.preventDefault()
      if (!activeMouse.x || !activeMouse.y) return
      ctx.beginPath()
      ctx.moveTo(activeMouse.x, activeMouse.y)
      ctx.lineTo(e.clientX, e.clientY)
      ctx.stroke()
      activeMouse = { x: e.clientX, y: e.clientY }
    }

    const mouseEnd = (e) => {
      e.preventDefault()
      activeMouse = {}
    }

    canvasEl.addEventListener('touchstart', touchStart)
    canvasEl.addEventListener('touchmove', touchMove)
    canvasEl.addEventListener('touchend', touchEnd)

    canvasEl.addEventListener('mousedown', mouseStart)
    canvasEl.addEventListener('mousemove', mouseMove)
    canvasEl.addEventListener('mouseup', mouseEnd)

  </script>
</body>
</html>

要想在画板中能点击按钮和块,那么可点击的内容一定要比画板的层级高,按钮这种占据面积小的元素,对于画笔的影响不大,可轻松穿过。而且线条能在div 组成的按钮上显示,能够满足第一个需求的按钮,就是 div 按钮了。
此时,进度条块的层级比画板层级高,画笔无法在进度条块上作画,只能在进度条块的两端横穿而过,画笔自动补全中间缺失的部分,操作如下:
在这里插入图片描述
现在,既想在进度条块上作画,又想能点击进度条块,如果解决呢?
分析如下(针对PC端):
画笔在画板上通过 mousedownmousemovemouseup这三种事件,当画笔出现在层级较画板高的进度条块上时,这三种事件无法触发,线条就画不出来了。那么,在进度条块上添加这3种事件就可以无缝连接作画了。
您可以点击这里:简单画板-进度条可以画线-文字图片
在这里插入图片描述

代码

<!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>简单画板-进度条可以画线-文字图片</title>
  <style>
    * {
      margin:0;
      padding: 0;
      user-select: none;
    }
    canvas{
      position: fixed;
    }
    p {text-indent: 2em;}
    .big-buttom{
      padding: 10px;
    }
    button{
      cursor: pointer;
    }
    .pz{
      position: relative;
      /* z-index: 1; */
    }
    .btn-wrap{
      height: 100px;
      padding: 20px;
      box-sizing: border-box;
      border: 1px solid #FE8227;
      display: flex;
      margin-bottom: 10px;
    }
    .btn-wrap.progress{
      cursor: pointer;
    }
    .div-btn{
      border: 2px solid #0ac013;
      width: 155px;
      height: 40px;
      line-height: 40px;
      cursor: pointer;
      text-align: center;
      color: #0ac013;
      border-radius: 88px;
      border: 2px solid #e8e8e8;
      padding: 0 20px;
      box-sizing: border-box;
    }
    .img-wrap{
      text-align: center;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <div>
    <p>
      故乡的春天是我今生的起点,故乡的月亮又是故乡春天的起点,正因为如此,我此生是以故乡月亮行走的方式行走于世界之中,从故乡的泥土上走出来的人们,就算身处异乡,举头望月,那月亮始终是故乡春天那轮月亮的颜色,无法改变!那样的云彩,陪伴着那样皎洁的月色,生长着一点点忧伤,荡漾着一缕缕思念……
    </p>
    <p>
      春天的月亮的是有心思的,深得让人可以跋涉一辈子,月亮的心思只有月亮知道,但是人的心思月亮一定知道,否则她怎么能那样善解人意,跟着人的感觉,走进人的灵魂深处?你看,你看啦,故乡春天的月亮行走着行走着,一不小心就出轨了,掉进故乡清澈的小河里。我不知道,你是不是以出轨的样儿走进我的空间的?成为我忐忑的期盼,但是,毋容置疑你已经活在我的天空里了,不知我是否也能走进你的家园里?我一直坚定的认为:一个人能活在另一个人的心坎里这是多么多么幸福的事情。
    </p>
    <p>
      妻子是土生土长的故乡人,如春天那一轮满月,总是勤勤恳恳挂在家的中央,穿着花围裙,老在厨房奏响锅碗瓢盆交响曲,在洗衣机旁唱响洗衣歌,用纯朴燃烧自己,默默地用爱呵护、坚守、保卫自己的地盘。随着时间的荏苒,我很少端详她认真的模样,只是,每天清早,梳洗打扮一番,真的特靓,堪称精品女人,但是每天下班回家却是灰头土脸。我戏曰:“老婆,你每天打扮得那靓是给别人看的,每天交给我的是一个次品女人。”“那是为你撑面子呗,我的坏蛋蛋!”
      老婆有意无意笑嘻嘻的说。我知道,那里头藏着的东西叫生活,叫日子。一个女人敢把最丑的一面展现在男人面前,那她早就把灵魂交付给了那男人保管了。
      在妻子的劳动中翻开了家的崭新的一页,而妻子面对春天报以憨厚一笑,就如春天那一轮清澈满月,但是,幸福就是在那一刻简单开始了……
    </p>
    <div class="btn-wrap">
      <div>
        <button class="pz big-buttom" id="b-buttom">大按钮</button>
      </div>
      <div>
        <button class="pz" id="s-buttom">默认按钮</button>
      </div>
      <div class="div-btn pz" id="d-buttom">div按钮</div>
    </div>
    <div class="btn-wrap progress pz" id="progress-wrap">
      这里是进度条
      <progress value="22" max="100"></progress>
    </div>
    <div class="img-wrap">
      <img src="data:image/jpeg;base64,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" alt="">
    </div>
  </div>
  <script>
    const canvasEl = document.querySelector('#canvas')
    const progressWrapEl = document.querySelector('#progress-wrap')
    const bButtomEl = document.querySelector('#b-buttom')
    const sButtomEl = document.querySelector('#s-buttom')
    const dButtomEl = document.querySelector('#d-buttom')
    // 画板全屏
    const pageWidth = document.documentElement.clientWidth;
    const pageHeight = document.documentElement.clientHeight;
    canvasEl.width = pageWidth;
    canvasEl.height = pageHeight;
    const ctx = canvasEl.getContext('2d')
    ctx.lineWidth = 4
    ctx.lineJoin = 'round'
    ctx.lineCap = 'round'
    ctx.strokeStyle = '#F44336'
    ctx.strokeRect(100, 100, 80, 80);

    const handleCopy = (t) => {
      return {
        id: t.identifier,
        x: t.clientX,
        y: t.clientY
      }
    }

    // 触屏多点操作
    let touchArr = []
    // 当前绘制点
    let activeMouse = {}

    const handleFn = (m, cb) => {
      m.preventDefault();
      [...m.changedTouches].forEach(item => {
        const current = touchArr.find(v => v.id === item.identifier)
        if (current) {
          ctx.beginPath()
          ctx.moveTo(current.x, current.y)
          ctx.lineTo(item.clientX, item.clientY)
          ctx.stroke()
          cb && cb(current, item)
        }
      })
    }

    const touchStart = (m) => {
      m.preventDefault();
      [...m.changedTouches].forEach(item => {
        touchArr.push(handleCopy(item))
      })
    }

    const touchMove = (m) => {
      m.preventDefault()
      const fn = (current, item) => touchArr.splice(touchArr.findIndex(v => v.id === current.id), 1, handleCopy(item))
      handleFn(m, fn)
    }

    const touchEnd = (m) => {
      m.preventDefault()
      const fn = (current) => touchArr.splice(touchArr.findIndex(v => v.id === current.id), 1)
      handleFn(m, fn)
    }

    const mouseStart = (e) => {
      e.preventDefault()
      activeMouse = { x: e.clientX, y: e.clientY }
    }

    const mouseMove = (e) => {
      e.preventDefault()
      if (!activeMouse.x || !activeMouse.y) return
      ctx.beginPath()
      ctx.moveTo(activeMouse.x, activeMouse.y)
      ctx.lineTo(e.clientX, e.clientY)
      ctx.stroke()
      activeMouse = { x: e.clientX, y: e.clientY }
    }

    const mouseEnd = (e) => {
      e.preventDefault()
      activeMouse = {}
    }

    canvasEl.addEventListener('touchstart', touchStart)
    canvasEl.addEventListener('touchmove', touchMove)
    canvasEl.addEventListener('touchend', touchEnd)

    canvasEl.addEventListener('mousedown', mouseStart)
    canvasEl.addEventListener('mousemove', mouseMove)
    canvasEl.addEventListener('mouseup', mouseEnd)

    progressWrapEl.addEventListener('touchstart', touchStart)
    progressWrapEl.addEventListener('touchmove', touchMove)
    progressWrapEl.addEventListener('touchend', touchEnd)

    progressWrapEl.addEventListener('mousedown', mouseStart)
    progressWrapEl.addEventListener('mousemove', mouseMove)
    progressWrapEl.addEventListener('mouseup', mouseEnd)

    progressWrapEl.addEventListener('click', () => {
      alert('点击了进度条块')
    })
    bButtomEl.addEventListener('click', () => {
      alert('点击了大按钮')
    })
    sButtomEl.addEventListener('click', () => {
      alert('点击了小按钮')
    })
    dButtomEl.addEventListener('click', () => {
      alert('点击了div按钮')
    })

  </script>
</body>
</html>
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
以下是一个简单的Python画板实现示例: ```python import tkinter as tk class PaintApp: def __init__(self, master): self.master = master self.master.title("简单画板") self.master.geometry("400x400") self.master.resizable(False, False) self.color = "black" self.canvas = tk.Canvas(self.master, bg="white", width=300, height=300) self.canvas.pack() self.canvas.bind("<B1-Motion>", self.draw) self.color_btns = [] colors = ["black", "red", "green", "blue", "purple"] for i, color in enumerate(colors): btn = tk.Button(self.master, bg=color, width=2, command=lambda c=color: self.change_color(c)) btn.pack(side="left", padx=5) self.color_btns.append(btn) clear_btn = tk.Button(self.master, text="清空", command=self.clear_canvas) clear_btn.pack(side="bottom", pady=10) def draw(self, event): x, y = event.x, event.y r = 5 self.canvas.create_oval(x-r, y-r, x+r, y+r, fill=self.color, outline=self.color) def change_color(self, color): self.color = color def clear_canvas(self): self.canvas.delete("all") if __name__ == "__main__": root = tk.Tk() app = PaintApp(root) root.mainloop() ``` 在这个画板应用中,我们使用了Tkinter库来创建GUI界面。我们创建了一个PaintApp类来管理画板应用的各种行为。在构造函数中,我们创建了一个Canvas对象来显示用户的绘画内容,并绑定了鼠标拖拽事件,当用户在画布上拖拽鼠标时,我们会调用draw()方法来绘制一个圆形,颜色由用户选择的颜色决定。我们还创建了一个按钮列表,让用户可以选择不同的颜色。最后,我们还创建了一个清空按钮,让用户可以清空画布。 这只是一个非常简单画板实现示例,但是它可以为初学者提供一个基本的思路,如何使用Tkinter库来创建GUI界面,并实现简单的绘画功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值