最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 制作一个画板来学习canvas

    正文概述 掘金(三只萌新)   2021-01-22   678

    制作一个画板来学习canvas

    效果展示

    制作一个画板来学习canvas

    功能

    github 代码地址
    制作一个画板来学习canvas
    项目地址中针对画板中的功能都单独做了抽离。如果想了解某个功能的话可以直接打开对应的文件查看效果。

    如何给 canvas 设置背景

    首先我们会想到

    • 直接给 canvas 元素设置 css 背景颜色。
    • 使用 fill / fillRect 给画板填充颜色。

    canvas 设置 css 背景

    这种方式有一些局限性

    1. 当我们使用在画板上使用 fillRect 之后,是无法再在通过 css 改变颜色。
      • 相当于 canvas 本身是透明的可以通过 css 来设置底色。当 canvas 上加了一层有色图层,那么就无法看到原先的底色了。
    2. 我们 css 无法局部使用印花的效果。类似下图效果
    制作一个画板来学习canvas

    canvas 使用 fillRect

    使用 css 有这些局限,我们就使用 canvas 来绘制一个真正的背景层。

    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'green';
    ctx.fillRect(0, 0, 150, 100);
    

    上面代码确实可以在 canvas 填充成对应的颜色。

    问题1

    但是如果画板上要是本身就已经绘制了内容,如果你使用 fillRect 会将之前绘制的内容覆盖。
    这是由于 CanvasRenderingContext2D.globalCompositeOperation 默认是 source-over

    • source-over 解释为: setting and draws new shapes on top of the existing canvas content
    • destination-over 解释为:New shapes are drawn behind the existing canvas content.

    我们绘制的背景应该在已有图层的后面。所以我们可以将设置为 ctx.globalCompositeOperation = 'destination-over'

    两种背景的更新如下图。source-over 是覆盖在已经绘制的内容之上。 destination-over 是绘制在之后。
    制作一个画板来学习canvas

    问题2

    使用上面的办法确实是可以在现有内容后绘制一层背景层。但是当我们想要再次改变背景颜色时候怎么办?
    我们再次更新背景时候还是绘制在已有内容之后,所以是看不到我们绘制的内容的。 制作一个画板来学习canvas

    解决方案

    首先我们了解一个 api

    • 这里的 image 包括 HTMLImageElement 、HTMLCanvasElement 、SVGImageElement 、HTMLVideoElement 等元素。

    这样我们使用两个 canvas 来完成更换背景的操作。他们各自分工,一个作为内容层、一个作为背景层。背景层使用 CanvasRenderingContext2D.drawImage() 将内容层渲染到背景层上。 制作一个画板来学习canvas

    画板导入图片进行编辑

    如何将本地图片导入绘制到画板上

    首先来介绍 FileRender api,通过这个 FileReader 来读取本地的图片文件。

    1. 使用 FileReader 读取 blob 文件转为 base64的地址,
    2. 在 load 事件中拿到地址赋值给 img 元素,得到一个 img 对象。(这里可以定义图片的宽高等操作)
    3. 得到图片后我们可以使用 drawImage 将图片绘制到 canvas 上。
    /**
     * 读取 input 元素选择的文件转化成 Base64 的地址,并根据该地址创建 img 元素
     * @param blob File 对象
     * @param width 指定图片的宽度
     */
    export function blobToImg (blob:Blob) {
      return new Promise((resolve, reject) => {
        let reader = new FileReader()
        reader.addEventListener('load', () => {
          let img = new Image()
          img.src = reader.result as string
          img.addEventListener('load', () => {
              resolve(img)
            }
          )
        })
        reader.readAsDataURL(blob)
      })
    }
    

    对图片进行拖拽、拉伸

    制作一个画板来学习canvas

    操作框

    操作框是用于判断当前要进行什么操作的。

    • 当落在八个不同方向的小矩形中的时候进行各个方向的拉伸。
    • 当落在图片上进行移动操作

    使用 new Path2D() 创建控制框路径(矩形框和边角的控制框),并且将这些路径存储起来,之后通过 ctx.isPointInPath(x,y) 来判断当前落点是否在对于的框内。

    移动图片

    首先我们知道一开始图片是通过 ctx.drawImage(img,x,y,w,h) 来将本地图片绘制到 canvas 上的。 当我们移动图片的时候我们需要考虑的只有 x,y 坐标位置。 w,h是不需要改变的,所以我们移动时候 mousemove 事件中监听坐标位置变换值,然后将图片重新绘制到新的坐标即可。

    拉伸图片

    八个方向的拉伸考虑的会有一些不一样。例如

    • 右上角拉伸的时候,我们需要考虑的只有 w,h 的变换。因为起点位置是不变的。
    • 左上角的拉伸的时候,x,y,w,h 都会有变换。
    • ......

    总的来说拉伸图片也是去改变 x,y,w,h的值,然后根据这些值重新去渲染图片。

    文字输入效果

    模拟光标

    模拟光标就是在鼠标点击点位置绘制一个类似光标的矩形,然后让这个矩形消失 -> 出现 -> 消失的过程。来看两个api。

    1. 通过 getImageData 来将当前画板上的像素都存储起来。(相当于我们保存当前 canvas 的快照)。
    2. 绘制光标矩形
    3. 使用 putImageData 将之前保存的快照应用到 canvas 上。(相当于返回到第一步的画面)
    4. 重复 2,3步骤。实现光标闪烁效果。

    文字自动换行

    CanvasRenderingContext2D.fillText(text, x, y [, maxWidth]);
    

    fillText 可以将文字绘制指定的位置,但是不会自动换行。如果想要实现自动换行需要了解另外一个知识点。

    SVG forginObject元素与文本自动换行

    在 svg 中使用 foreignObject 标签可以直接在SVG内部嵌入XHTML元素。当超过设定当宽度后,XHML 元素本身是自带换行的。

    <svg xmlns="http://www.w3.org/2000/svg">
      <foreignObject width="120" height="50">
          <body xmlns="http://www.w3.org/1999/xhtml">
            <p style="font-size:12px;margin:0;">一段需要word wrap的文字。</p>
          </body>
        </foreignObject>
    </svg>
    

    SVG forginObject元素生成图片

    1. 将html标签拼接到body之中:<svg xmlns="http://www.w3.org/2000/svg"><body><p style="width:100px"></p></body><foreignObject><body></body></foreignObject></svg> 格式中。
    2. 创建 img 元素将步骤1的地址赋值给 img 元素。
    3. 使用 drawImage 将图片位置到 canvas 上
    4. 如果你要保存图片可以使用 canvas.toDataURL()canvas.toBlob() 将图片重新导出。

    canvas输入文字自动换行。

    export async function autoWrapText(width:number,height:number,value:string,font="16px sans-serif"):Promise<HTMLImageElement>{
      const { fontColor } = store.state
     //  这里用的color不能使用 hex 如:#000,无效
      const path = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="'+ width +'" height="'+ height +'"><body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;font:'+ font +';word-break: break-word;color:'+fontColor+'">'+value+'</body></foreignObject></svg>';
      return  await loadImgSize(path,width,height)
    }
    
    function loadImgSize(path:string,width:number,height:number):Promise<HTMLImageElement> {
      return new Promise(res=>{
        const img = new Image()
        img.width = width 
        img.height = height
        img.onload = function () {
          res(img)
        }
        img.src = path
      })
    }
    
    1. 监听键盘事件,判断当前输入内容或者功能键(如果要做特殊功能的话)。
    2. 在 mousedown 中记录当前鼠标的位置,计算出距离 canvas 画板右侧的距离记为 remain 变量。将 svg foreignObject 标签的宽度设置为 remain。
    3. 创建 img 标签。将地址拼接成 data:image/svg+xml... 格式
    4. 使用 drawImage 将img绘制到 canvas上。

    前端图片上传前压缩

    前端压缩的好处

    • 由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅,同时大大降低了网络异常导致上传失败风险。
    • 最最重要的体验改进点:省略了图片的再加工成本。

    流程

    图片(本地选取或路径) -> canvas 压缩(使用 ctx.drawImage(img,0,0,w,h)来实现) -> 图片(使用 canvas.toDataURL() 、 canvas.toBlob())方法重新导出图片。

    使用 ctx.drawImage 来指定 w,h 按比例重新渲染到 canvas,然后导出图片实现压缩。

    更多功能查看 github 代码

    参考

    SVG 简介与截图等应用
    mdn


    起源地下载网 » 制作一个画板来学习canvas

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元