canvas学习(四):简单画板

前言

canvas实现画板功能,有画笔、矩形、橡皮擦、圆形、线段粗细、画笔颜色、下载图片等功能
是对canvas的综合学习回顾

步骤

  1. 基本样式
<style type="text/css">
    *{
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body{
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
    }
    .caidan{
      height: 100px;
      width: 100vw;
      display: flex;
      border-bottom: 3px solid #ccc;
      justify-content: space-around;
      align-items: center;
    }
    #canvas{
      flex: 1;
      width: 100vw;
    }
    .btn{
      width: 150px;
      height: 50px;
      border: 1px solid #ccc;
      border-radius: 20px;
      text-align: center;
      line-height: 50px;
      color: #999;
    }
    .active{
      box-shadow: 0 0 20px deepskyblue;
      background-color: #f2f2f2;
      border: 1px solid deepskyblue;
    }
  </style>
  <div class="caidan">
    <div class="btn" id="huabi">画笔</div>
    <div class="btn" id="rect">矩形</div>
    <div class="btn">圆形</div>
    <div class="btn">线段粗细</div>
    <div class="btn">画笔颜色</div>
  </div>
  <canvas id="canvas">
    你的浏览器不支持canvas
  </canvas>
  1. 画笔和矩形
思路解析:
	1、点击菜单项渲染阴影,触发相应的函数
	2、鼠标按下、弹起事件控制画板可画
  <script>
    // 需求
    // 画笔功能:能够拖动鼠标在页面内绘图  能够设置画笔粗细 设置画笔颜色
    // 能够在任意位置绘制圆形: 拖动鼠标可以在任意位置绘制圆形并可以定制大小
    // 能够在任意位置绘制矩形: 拖动鼠标可以在任意位置绘制矩形并可以定制大小

    // 1、找到画布对象
    /** @type {HTMLCanvasElement} */ 
    var canvas= document.getElementById('canvas')
    console.log([canvas])
    canvas.setAttribute("width",canvas.offsetWidth) // 设置宽度为页面全屏
    canvas.setAttribute("height",canvas.offsetHeight)
    // 2、上下文对象,画笔
    var ctx=canvas.getContext('2d') // 用于2d对象绘制 webgl是3d引擎

    // 设置一个对象 保存画笔当前的状态
    var huaban = {
      type:null,
      isDrow:false, // 是否处于可画状态
      beginX:0, // 矩形开始时起点的位置
      beginY:0, // 矩形开始时起点的位置
      imageData:null,
      huabiFn:(e)=>{ // 画笔执行的内容
        var x = e.pageX - canvas.offsetLeft; // 找到当前鼠标位置
        var y = e.pageY - canvas.offsetTop;
        ctx.beginPath()
        ctx.arc(x,y,3,0,2*Math.PI) // 以当前鼠标触发的位置为中心 进行画小圆,缺点是画的快无法连贯
        ctx.closePath()
        ctx.fill()
      },
      rectFn:(e)=>{ // 矩形执行的函数
        var x = e.pageX - canvas.offsetLeft; // 找到当前鼠标位置
        var y = e.pageY - canvas.offsetTop;
        ctx.clearRect(0,0,canvas.offsetWidth,canvas.offsetHeight) // 画矩形前清除画板
        if(huaban.imageData){ // 导入之前保存的画板图像
          ctx.putImageData(huaban.imageData,0,0,0,0,canvas.offsetWidth,canvas.offsetHeight)
        }
        ctx.beginPath()
        ctx.rect(huaban.beginX,huaban.beginY,x-huaban.beginX,y-huaban.beginY) // 以当前鼠标触发的位置为中心
        ctx.stroke()
        ctx.closePath()
      }
    }
    function btnClick(){ // 全局清除样式
     document.querySelectorAll('.btn').forEach((item)=>{
       item.classList.remove("active")
     }) 
    }
    // 画笔按钮点击事件
    var huabiBtn = document.querySelector('#huabi')
    huabiBtn.onclick=()=>{
      btnClick()
      huabiBtn.classList.add("active")
      huaban.type='huabi'
    }
	// 矩形点击事件
    var rectBtn = document.querySelector('#rect')
    rectBtn.onclick=()=>{
      btnClick()
      rectBtn.classList.add("active")
      huaban.type='rect'
    }
    // 监听鼠标按下事件
    canvas.onmousedown=(e)=>{
      huaban.isDraw = true
      if(huaban.type=="rect"){
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
        huaban.beginX=x
        huaban.beginY=y
      }
    }
    // 监听鼠标弹起事件
    canvas.onmouseup=()=>{
      // 将当前的数据对象保留
      huaban.imageData = ctx.getImageData(0,0,canvas.offsetWidth,canvas.offsetHeight)
      huaban.isDraw = false
    }

     // 鼠标移动时绘制圆形 将源图像的目标内容清除掉
     canvas.onmousemove=(e)=>{
      if(huaban.isDraw&&huaban.type){
        huaban[huaban.type+'Fn'](e) // 调用当前所点的按钮
      }
    }

在这里插入图片描述

  1. 画笔优化(连贯)
上述情况下,画笔是用填充小圆的方式实现,出现问题在画的速度过快的情况下无法连贯
优化:以绘制线段的方式实现
思路:移动过程中时刻改变起点位置
// 监听鼠标移动,如果当前是画笔状态,时刻更新起点位置
     canvas.onmousemove=(e)=>{
      if(huaban.isDraw&&huaban.type){
        huaban[huaban.type+'Fn'](e) // 调用当前所点的按钮
      }
      if(huaban.type=="huabi"){
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
        huaban.beginX=x
        huaban.beginY=y
      }
    }
lineWidth:6,
 huabiFn:(e)=>{ // 画笔执行的内容
        ctx.beginPath()
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
++        ctx.moveTo(huaban.beginX,huaban.beginY)
++        ctx.lineTo(x,y)
--        // ctx.beginPath()
++		 ctx.lineWidth=huaban.lineWidth
++        ctx.lineCap="round"
++        ctx.lineJoin="round"
--        // ctx.moveTo(huaban.beginX,huaban.beginY)
++        ctx.stroke()
--        // ctx.arc(x,y,3,0,2*Math.PI) // 以当前鼠标触发的位置为中心
--        // ctx.fill()
        ctx.closePath()
      },

效果图
在这里插入图片描述

  1. 圆形
和矩形书写方式类似
// huaban对象下添加
yuanFn:(e)=>{
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
        ctx.clearRect(0,0,canvas.offsetWidth,canvas.offsetHeight)
        if(huaban.imageData){
          ctx.putImageData(huaban.imageData,0,0,0,0,canvas.offsetWidth,canvas.offsetHeight)
        }
        ctx.beginPath()
        ctx.arc(x,y,Math.abs(x-huaban.beginX),0,2*Math.PI) // 以当前鼠标触发的位置为中心 绝对值半径
        ctx.stroke()
        ctx.closePath()
      },
// 圆形
    var yuanBtn =  document.querySelector('#yuan')
    yuanBtn.onclick=()=>{
      btnClick()
      yuanBtn.classList.add("active")
      huaban.type='yuan'
    }
     // 监听鼠标按下事件
    canvas.onmousedown=(e)=>{
      huaban.isDraw = true
++      if(huaban.type=="rect"||huaban.type=="yuan"){
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
        huaban.beginX=x
        huaban.beginY=y
      }
    }

在这里插入图片描述

  1. 线段粗细
思路:设置一个字段用来控制线段粗细
<div class="caidan">
   <div class="btn" id="huabi">画笔</div>
   <div class="btn" id="rect">矩形</div>
   <div class="btn" id="yuan">圆形</div>
++   <div class="btn1 line xi">线段细</div>
 ++  <div class="btn1 line normal">线段</div>
++   <div class="btn1 line cu">线段粗</div>
++   <div class="btn1">画笔颜色</div>
 </div>

 .btn1{
     width: 150px;
     height: 50px;
     border: 1px solid #ccc;
     border-radius: 20px;
     text-align: center;
     line-height: 50px;
     color: #999;
   }
   .line{
     display: flex;
     justify-content: center;
     align-items: center;
   }
   .xi::after{
     content: "";
     background-color: #333;
     width: 6px;
     height: 6px;
     display: block;
     border-radius: 3px;
   }
   .normal::after{
     content: "";
     background-color: #333;
     width: 10px;
     height: 10px;
     display: block;
     border-radius: 5px;
   }
   .cu::after{
     content: "";
     background-color: #333;
     width: 16px;
     height: 16px;
     display: block;
     border-radius: 8px;
   }
// 选中样式,清除其他 回到最开始线段粗细
function btn1Click(){
     huaban.lineWidth=6
    document.querySelectorAll('.line').forEach((item)=>{
      item.classList.remove("active")
    }) 
   }
// 设置粗细的按钮
   var lineDivs=document.querySelectorAll(".line")
   lineDivs.forEach((item,i)=>{
     item.onclick=()=>{
       btn1Click() // 清除样式
       item.classList.add('active')
       if(i==0){
         huaban.lineWidth=6
       }else if(i==1){
         huaban.lineWidth=16
       }else{
         huaban.lineWidth=32
       }
     }
   })

在这里插入图片描述

  1. 字体颜色
和设置线段粗细类似
   <div class="btn1 line cu">线段粗</div>
 --   <div class="btn1">画笔颜色</div>
 ++   <div class="btn1" id="btnColor"><input type="color" id="color"></div>
// 添加color属性保存我们选中的颜色
var huaban = {
      type:null,
      isDrow:false,
      beginX:0,
      beginY:0,
      lineWidth:6,
      imageData:null,
  ++    color:'#000',
      ~~
    }
// 点击颜色组件
    var colorBtn =  document.querySelector('#btnColor')
    colorBtn.onclick=()=>{
      colorBtn.classList.add("active")
    }
    // 监听颜色改变事件
    var colorInput = document.querySelector("#color")
    colorInput.onchange = (e)=>{
      huaban.color = colorInput.value
    }
 huabiFn:(e)=>{ // 画笔执行的内容
        ctx.beginPath()
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
++        ctx.lineWidth=huaban.lineWidth
++        ctx.strokeStyle=huaban.color
        ctx.lineCap="round"
        ctx.lineJoin="round"
        ctx.moveTo(huaban.beginX,huaban.beginY)
        ctx.lineTo(x,y)
        ctx.stroke()
        ctx.closePath()
      },
      rectFn:(e)=>{
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
        ctx.clearRect(0,0,canvas.offsetWidth,canvas.offsetHeight)
        if(huaban.imageData){
          ctx.putImageData(huaban.imageData,0,0,0,0,canvas.offsetWidth,canvas.offsetHeight)
        }
++        ctx.lineWidth=huaban.lineWidth
++        ctx.strokeStyle=huaban.color
        ctx.beginPath()
        ctx.rect(huaban.beginX,huaban.beginY,x-huaban.beginX,y-huaban.beginY) // 以当前鼠标触发的位置为中心
        ctx.stroke()
        ctx.closePath()
      },
      yuanFn:(e)=>{
        var x = e.pageX - canvas.offsetLeft; // 找到当前位置
        var y = e.pageY - canvas.offsetTop;
        ctx.clearRect(0,0,canvas.offsetWidth,canvas.offsetHeight)
        if(huaban.imageData){
          ctx.putImageData(huaban.imageData,0,0,0,0,canvas.offsetWidth,canvas.offsetHeight)
        }
++        ctx.lineWidth=huaban.lineWidth
++        ctx.strokeStyle=huaban.color
        ctx.beginPath()
        ctx.arc(x,y,Math.abs(x-huaban.beginX),0,2*Math.PI) // 以当前鼠标触发的位置为中心
        ctx.stroke()
        ctx.closePath()
      },

在这里插入图片描述

  1. 下载图片
实现画布的下载保存功能
canvas.toDataURL() // 会把画布转化成base64格式的图片
调用a标签的下载事件实现
<div class="btn1" id="download">下载图片</div>
// 点击下载按钮
    var download = document.querySelector("#download")
    download.onclick=()=>{
      var url =  canvas.toDataURL()
      // console.log(url) // 打印是base64格式图片地址
      var img = new Image()
      img.src = url
      let eleLink = document.createElement('a')
      eleLink.download = "画布图片"
      eleLink.style.display = 'none';
      eleLink.href = url
      eleLink.click()
      eleLink.remove()
    }

效果图
在这里插入图片描述

  1. 橡皮擦
本质上就是:颜色为画布背景色的画笔
<div class="btn" id="xpc">橡皮擦</div>

var xpcBtn = document.querySelector('#xpc')
   xpcBtn.onclick=()=>{
     btnClick()
     xpcBtn.classList.add("active")
     huaban.type='xpc'
   }
// 对象中添加橡皮擦函数
xpcFn:(e)=>{
       ctx.beginPath()
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       ctx.lineWidth=huaban.lineWidth
       ctx.strokeStyle="#fff"
       ctx.lineCap="round"
       ctx.lineJoin="round"
       ctx.moveTo(huaban.beginX,huaban.beginY)
       ctx.lineTo(x,y)
       ctx.stroke()
       ctx.closePath()
     },
// 鼠标移动时绘制圆形 将源图像的目标内容清除掉
    canvas.onmousemove=(e)=>{
     if(huaban.isDraw&&huaban.type){
       huaban[huaban.type+'Fn'](e) // 调用当前所点的按钮
     }
++      if(huaban.type=="huabi"||huaban.type=="xpc"){
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       huaban.beginX=x
       huaban.beginY=y
     }
   }
  1. 全部代码
<!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>Document</title>
 <style type="text/css">
   *{
     margin: 0;
     padding: 0;
     box-sizing: border-box;
   }
   body{
     width: 100vw;
     height: 100vh;
     display: flex;
     flex-direction: column;
     justify-content: flex-start;
   }
   .caidan{
     height: 100px;
     width: 100vw;
     display: flex;
     border-bottom: 3px solid #ccc;
     justify-content: space-around;
     align-items: center;
   }
   #canvas{
     flex: 1;
     width: 100vw;
   }
   .btn{
     width: 150px;
     height: 50px;
     border: 1px solid #ccc;
     border-radius: 20px;
     text-align: center;
     line-height: 50px;
     color: #999;
   }
   .active{
     box-shadow: 0 0 20px deepskyblue;
     background-color: #f2f2f2;
     border: 1px solid deepskyblue;
   }
   .btn1{
     width: 150px;
     height: 50px;
     border: 1px solid #ccc;
     border-radius: 20px;
     text-align: center;
     line-height: 50px;
     color: #999;
   }
   .line{
     display: flex;
     justify-content: center;
     align-items: center;
   }
   .xi::after{
     content: "";
     background-color: #333;
     width: 6px;
     height: 6px;
     display: block;
     border-radius: 3px;
   }
   .normal::after{
     content: "";
     background-color: #333;
     width: 10px;
     height: 10px;
     display: block;
     border-radius: 5px;
   }
   .cu::after{
     content: "";
     background-color: #333;
     width: 16px;
     height: 16px;
     display: block;
     border-radius: 8px;
   }
 </style>
</head>
<body>
 <div class="caidan">
   <div class="btn" id="huabi">画笔</div>
   <div class="btn" id="rect">矩形</div>
   <div class="btn" id="yuan">圆形</div>
   <div class="btn" id="xpc">橡皮擦</div>
   <div class="btn1" id="download">下载图片</div>
   <div class="btn1 line xi active">线段细</div>
   <div class="btn1 line normal">线段</div>
   <div class="btn1 line cu">线段粗</div>
   <div class="btn1" id="btnColor"><input type="color" id="color"></div>
 </div>
 <canvas id="canvas">
   你的浏览器不支持canvas
 </canvas>
 <script>
   // 需求
   // 画笔功能:能够拖动鼠标在页面内绘图  能够设置画笔粗细 设置画笔颜色
   // 能够在任意位置绘制圆形: 拖动鼠标可以在任意位置绘制圆形并可以定制大小
   // 能够在任意位置绘制矩形: 拖动鼠标可以在任意位置绘制矩形并可以定制大小

   // 1、找到画布对象
   /** @type {HTMLCanvasElement} */ 
   var canvas= document.getElementById('canvas')
   console.log([canvas])
   canvas.setAttribute("width",canvas.offsetWidth)
   canvas.setAttribute("height",canvas.offsetHeight)
   // 2、上下文对象,画笔
   var ctx=canvas.getContext('2d') // 用于2d对象绘制 webgl是3d引擎

   // 设置一个对象 保存画笔当前的状态
   var huaban = {
     type:null,
     isDrow:false,
     beginX:0,
     beginY:0,
     lineWidth:6,
     imageData:null,
     color:'#000',
     huabiFn:(e)=>{ // 画笔执行的内容
       ctx.beginPath()
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       ctx.lineWidth=huaban.lineWidth
       ctx.strokeStyle=huaban.color
       ctx.lineCap="round"
       ctx.lineJoin="round"
       ctx.moveTo(huaban.beginX,huaban.beginY)
       ctx.lineTo(x,y)
       // ctx.beginPath()
       // ctx.moveTo(huaban.beginX,huaban.beginY)
       ctx.stroke()
       // ctx.arc(x,y,3,0,2*Math.PI) // 以当前鼠标触发的位置为中心
       // ctx.fill()
       ctx.closePath()
     },
     xpcFn:(e)=>{
       ctx.beginPath()
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       ctx.lineWidth=huaban.lineWidth
       ctx.strokeStyle="#fff"
       ctx.lineCap="round"
       ctx.lineJoin="round"
       ctx.moveTo(huaban.beginX,huaban.beginY)
       ctx.lineTo(x,y)
       ctx.stroke()
       ctx.closePath()
     },
     rectFn:(e)=>{
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       ctx.clearRect(0,0,canvas.offsetWidth,canvas.offsetHeight)
       if(huaban.imageData){
         ctx.putImageData(huaban.imageData,0,0,0,0,canvas.offsetWidth,canvas.offsetHeight)
       }
       ctx.lineWidth=huaban.lineWidth
       ctx.strokeStyle=huaban.color
       ctx.beginPath()
       ctx.rect(huaban.beginX,huaban.beginY,x-huaban.beginX,y-huaban.beginY) // 以当前鼠标触发的位置为中心
       ctx.stroke()
       ctx.closePath()
     },
     yuanFn:(e)=>{
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       ctx.clearRect(0,0,canvas.offsetWidth,canvas.offsetHeight)
       if(huaban.imageData){
         ctx.putImageData(huaban.imageData,0,0,0,0,canvas.offsetWidth,canvas.offsetHeight)
       }
       ctx.lineWidth=huaban.lineWidth
       ctx.strokeStyle=huaban.color
       ctx.beginPath()
       ctx.arc(x,y,Math.abs(x-huaban.beginX),0,2*Math.PI) // 以当前鼠标触发的位置为中心
       ctx.stroke()
       ctx.closePath()
     },
   }
   function btnClick(){
    document.querySelectorAll('.btn').forEach((item)=>{
      item.classList.remove("active")
    }) 
   }
   function btn1Click(){
     huaban.lineWidth=6
    document.querySelectorAll('.line').forEach((item)=>{
      item.classList.remove("active")
    }) 
   }
   // 画笔按钮点击事件
   var huabiBtn = document.querySelector('#huabi')
   huabiBtn.onclick=()=>{
     btnClick()
     huabiBtn.classList.add("active")
     huaban.type='huabi'
   }

   var rectBtn = document.querySelector('#rect')
   rectBtn.onclick=()=>{
     btnClick()
     rectBtn.classList.add("active")
     huaban.type='rect'
   }

   // 圆形
   var yuanBtn =  document.querySelector('#yuan')
   yuanBtn.onclick=()=>{
     btnClick()
     yuanBtn.classList.add("active")
     huaban.type='yuan'
   }
   // 橡皮擦
   var xpcBtn = document.querySelector('#xpc')
   xpcBtn.onclick=()=>{
     btnClick()
     xpcBtn.classList.add("active")
     huaban.type='xpc'
   }

   // 设置粗细的按钮
   var lineDivs=document.querySelectorAll(".line")
   lineDivs.forEach((item,i)=>{
     item.onclick=()=>{
       btn1Click() // 清除样式
       item.classList.add('active')
       if(i==0){
         huaban.lineWidth=6
       }else if(i==1){
         huaban.lineWidth=16
       }else{
         huaban.lineWidth=32
       }
     }
   })

   // 点击颜色组件
   var colorBtn =  document.querySelector('#btnColor')
   colorBtn.onclick=()=>{
     colorBtn.classList.add("active")
   }
   // 监听颜色改变事件
   var colorInput = document.querySelector("#color")
   colorInput.onchange = (e)=>{
     huaban.color = colorInput.value
   }

   // 点击下载按钮
   var download = document.querySelector("#download")
   download.onclick=()=>{
     var url =  canvas.toDataURL()
     // console.log(url) // 打印是base64格式图片地址
     var img = new Image()
     img.src = url
     let eleLink = document.createElement('a')
     eleLink.download = "画布图片"
     eleLink.style.display = 'none';
     eleLink.href = url
     eleLink.click()
     eleLink.remove()
   }

   // 监听鼠标按下事件
   canvas.onmousedown=(e)=>{
     huaban.isDraw = true
     if(huaban.type=="rect"||huaban.type=="yuan"){
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       huaban.beginX=x
       huaban.beginY=y
     }
   }

   // 监听鼠标弹起事件
   canvas.onmouseup=()=>{
     // 将当前的数据对象保留
     huaban.imageData = ctx.getImageData(0,0,canvas.offsetWidth,canvas.offsetHeight)
     huaban.isDraw = false
   }

    // 鼠标移动时绘制圆形 将源图像的目标内容清除掉
    canvas.onmousemove=(e)=>{
     if(huaban.isDraw&&huaban.type){
       huaban[huaban.type+'Fn'](e) // 调用当前所点的按钮
     }
     if(huaban.type=="huabi"||huaban.type=="xpc"){
       var x = e.pageX - canvas.offsetLeft; // 找到当前位置
       var y = e.pageY - canvas.offsetTop;
       huaban.beginX=x
       huaban.beginY=y
     }
   }


   
 </script>
</body>
</html>
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值