Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Web技术】774- 基于canvas完成图片裁剪工具

【Web技术】774- 基于canvas完成图片裁剪工具

作者头像
pingan8787
发布于 2020-11-19 07:46:19
发布于 2020-11-19 07:46:19
1.4K10
代码可运行
举报
文章被收录于专栏:前端自习课前端自习课
运行总次数:0
代码可运行

前言

本文是基于canvas去实现图片裁剪工具。因为canvas代码还是比较长的,尽量写思路,完整代码已放在github上。

canvas模糊问题

这个是写canvas必定接触的问题,网上关于这个的答案也到处都是,就不详细介绍了。

因为canvas不是矢量图,在Retina屏下,浏览器用多个像素点去渲染一个像素,导致canvas最后呈现出模糊问题。

解决方案:

  • 获取window.devicePixelRatio设备的物理像素分辨率与CSS像素分辨率的比值。
  • canvas context有个属性backingStorePixelRatio表示渲染canvas之前会用几个像素来存储画布信息。不过这个只在某些浏览器上有,例如safari
  • 通过设置canvas.width/heightcanvas.style.width/heightcanvas进行缩放处理,比例为devicePixelRatio/backingStorePixelRatio(ratio)。(canvas.width/height表示画布实际大小,而canvas.style.width/height表示在浏览器上渲染结果大小)
  • 最后再通过context.scale(ratio, ratio)canvas进行处理,修复他的呈现效果

如果用typescript的话,会报backingStorePixelRatio不存在错误,加上一个类型定义文件解决。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export const getPixelRatio = (context: CanvasRenderingContext2D) => {
  const backingStore =
    context.backingStorePixelRatio ||
    context.webkitBackingStorePixelRatio ||
    context.mozBackingStorePixelRatio ||
    context.msBackingStorePixelRatio ||
    context.oBackingStorePixelRatio || 1;
  return (window.devicePixelRatio || 1) / backingStore;
};
const calcCanvasSize = () => {
    //...dosth.
    canvasRef.current.style.width = `${canvasWidth}px`;
    canvasRef.current.style.height = `${canvasHeight}px`;
    canvasRef.current.width = canvasWidth * ratio;
    canvasRef.current.height = canvasHeight * ratio;
    ctx.scale(ratio, ratio);
};

给canvas画上img

这个其实就是,通过input获取到本地图片文件,通过window.URL.createObjectURL获取到DOMString,将其作为imgsrc。通过ctx.drawImage将图片绘画到canvas上。

因为对于图片裁剪工具而言,img是应该绘画在最底层,所以需要通过globalCompositeOperation,将其绘画在底层。(globalCompositeOperation表示如何将一个源(新的)图像绘制到目标(已有)的图像上。)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const handleChoiseImg = () => {
    if (createURL) {
      window.URL.revokeObjectURL(createURL);
    };

    createURL = window.URL.createObjectURL(inputRef.current!.files![0]);
    img = new Image();
    img.onload = () => {
      //initImageCanvas(img); 这个函数我是去获取img应该缩小比例和缩小宽高
      // calcCanvasSize(); 这个我是去获取canvas应该呈现的size
      drawImage();  //绘画img
    };
    img.src = createURL;
};

const drawImage = () => {
    // todo sth.
    ctx.save();
    ctx.globalCompositeOperation = 'destination-over';
    // ctx.translate(canvasWidth / 2, canvasHeight / 2);
    // ctx.rotate(Math.PI / 180 * rotate);
    // if (rotate % 180 !== 0) {
    //   [canvasWidth, canvasHeight] = [canvasHeight, canvasWidth];
    // };
    // ctx.translate(-canvasWidth / 2, - canvasHeight / 2);
    ctx.drawImage(
      img,
      (canvasWidth - scaleImgWidth) / 2, (canvasHeight - scaleImgHeight) / 2,
      scaleImgWidth, scaleImgHeight
    );
    // canvasWidth/Height表示canvas的宽高(style),scaleImgWidth/Height表示图片缩放后的宽高
    ctx.restore();
};

蒙层&选中框

蒙层绘制

还是利用globalCompositeOperation将其绘画在已有图像的上方。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const drawCover = () => {
    ctx.save();
    ctx.fillStyle = 'rgba(0,0,0,0.5)';
    ctx.fillRect(0, 0, canvasSize.width, canvasSize.height);
    ctx.globalCompositeOperation = 'source-atop';
    ctx.restore();
};

选中框绘制

其实选中框,就是通过clearRect清除某个区域的蒙层,然后绘画自己的框框style,最后将img绘画在底层。

canvas的动画都是一帧一帧绘画出来的,选中框的拖动过程,其实就是不断去clearRect整个canvas,然后重新走上面的流程,即重新绘画的过程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const drawSelect = (x: number, y: number, w: number, h: number) => {
    ctx.clearRect(0, 0, canvasSize.width, canvasSize.height);
    //清空整个canvas
    drawCover();
    //绘画蒙层
    ctx.save();
    ctx.clearRect(x, y, w, h);
    //清空选中区域
    ctx.strokeStyle = '#5696f8';
    ctx.strokeRect(x, y, w, h);
    // 画选中框
    // todo sth. 给选中框加一些style
    ctx.restore();
    drawImage();
    // 绘画图片
};

选中框拖拽拉伸&边界处理

选中框拖拽拉伸就是,对mouse事件的处理,在mouseDown的时候,给其一个标志符,在mouseMove进行选中框不断刷新绘制,在mouseUp取消标志符(这个事件可以给外面容器)。

边界处理,就是对mouseMove处理过的选中框位置进行处理判断,若超出边界,则修复他。就是对offsetXoffsetY进行处理,然后在不同方向上去判断如何修改选中框,由于代码量比较大,完整可去github上看。

效果图:

图片旋转处理

canvas旋转中心是以左上角为中心,如果直接调用rotate,那么结果肯定不是我们想要的结果。那么就利用到了translate去移动canvas到中心点,然后再调用rotate旋转,旋转结束后再利用translatecanvas移回他的位置。

唯一的问题就是,弄清rotate后,你再translate平移canvas这个时候的x、y的值。

我这边对于图片裁剪工具的处理是,旋转后,去修改canvaswidth/height&style width/height。这个时候,canvas是旋转了,但是image重新绘画的时候,也要绘画旋转后的图,那么就利用上方讲的方法去旋转绘画。

还有就是别忘记通过save & restore去保存和恢复绘图状态。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const drawImage = () => {
    // todo sth.
    ctx.save();
    ctx.globalCompositeOperation = 'destination-over';
    ctx.translate(canvasWidth / 2, canvasHeight / 2);
    ctx.rotate(Math.PI / 180 * rotate);
    if (rotate % 180 !== 0) {
      [canvasWidth, canvasHeight] = [canvasHeight, canvasWidth];
    };
    ctx.translate(-canvasWidth / 2, - canvasHeight / 2);
    ctx.drawImage(
      img,
      (canvasWidth - scaleImgWidth) / 2, (canvasHeight - scaleImgHeight) / 2,
      scaleImgWidth, scaleImgHeight
    );
    ctx.restore();
};
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
效果图:

图片缩放处理

scale也是以左上角为缩放中心,然后如果缩放的话也需要save & restore,不然会对后续操作进行影响。

不过,我这里没有采用scale,而是手动修改图片缩放比例,然后重新得到scaleImgWidthscaleImgHeight,在去调用drawImage。因为代码上是将其显示在中心,所以就可以直接修改后调用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 修改 scaleImg 得到scaleImgWidth & scaleImgHeight
ctx.drawImage(
  img,
  (canvasWidth - scaleImgWidth) / 2, (canvasHeight - scaleImgHeight) / 2,
  scaleImgWidth, scaleImgHeight
);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
效果图:

图片灰度处理

灰度处理就是通过getImageData获取canvasImageData即像素数据,可以对像素数据进行处理。然后再将这个处理后的像素数据,重新通过putImageData放回到canvas上。

像素数据,对于每个像素都有四个方面的信息,分别是RedGreenBlueAlpha

灰度处理公式还是挺多的,我这边就采用(R + 2G + B) >> 2

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const imgData = ctx.getImageData(0, 0, canvasSize.width * ratio, canvasSize.height * ratio);
getGrayscaleData(imgData);
ctx.putImageData(imgData, 0, 0);

除此之外,还可以做很多类似的处理,比如,对比色处理,颜色选择器等等。

效果图:

实时显示截选的图片

如果仅仅是去截选canvas目前显示的部分,是不太友好的。应该是对应到原始图片的相应位置,去截选这个位置的图片才是比较友好的。

处理思路:

  • 新创建一个canvas,将img完整绘画在上面,并且完成旋转问题
  • 通过选中框的x y w h的值,还有img width/heightcanvas width/height的值,得到对应原始图片的截选部分的x y
  • 通过getImageData得到ImageData,并判断是否需要灰度处理
  • 然后重新修改上面创建的canvaswidth/height为选中图片部分的putW putH
  • ImageData通过putImageData放入canvas中 通过toBlob获取到blob
  • 最后通过window.URL.createObjectURL获取到DOMString
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export const getPhotoData = () => {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
    // todo canvas处理

    ctx.drawImage(img, 0, 0, imgWidth, imgHeight);
    // 处理获得putX putY putW putH
    const imgData = ctx.getImageData(putX, putY, putW, putH);
    if (grayscale) {    //灰度处理
        getGrayscaleData(imgData);
    };
    canvas.width = putW;
    canvas.height = putH;

    ctx.putImageData(imgData, 0, 0);
    return new Promise(res => {
        canvas.toBlob(e => res(e));
    });
};

const cancelChangeSelect = async () => {
    // todo sth.
    dataUrl && (window.URL.revokeObjectURL(dataUrl));
    const blob = await getPhotoData() as Blob;
    const newDataUrl = window.URL.createObjectURL(blob);
    setDataUrl(newDataUrl);
    // todo sth.
};
// 省去不关键代码

效果图:

下载截选图片

这个其实上面已经写的差不多了,获取到了dataUrl后,将其作为a标签的href,下载就完事儿了。(当然还有很多其他下载方式,就不一一列举了)

源自:https://segmentfault.com/a/1190000015288700

声明:文章著作权归作者所有,如有侵权,请联系小编删除。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端自习课 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
你好,你知道怎么申请TF签吗?
你好,你知道怎么申请TF签吗?
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
Day 3 学习Canvas这一篇文章就够了
一、canvas简介 ​ <canvas> 是 HTML5 新增的,一个可以使用脚本(通常为JavaScript)在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。 ​ 它最初由苹果内部使用自己MacOS X WebKit推出,供应用程序使用像仪表盘的构件和 Safari 浏览器使用。 后来,有人通过Gecko内核的浏览器 (尤其是Mozilla和Firefox),Opera和Chrome和超文本网络应用技术工作组建议为下一代的网络技术使用该元素。 ​ Canvas是由HTML代码配合高度和宽度属性而定义出的可绘制区域。JavaScript代码可以访问该区域,类似于其他通用的二维API,通过一套完整的绘图函数来动态生成图形。 ​ Mozilla 程序从 Gecko 1.8 (Firefox 1.5)开始支持 <canvas>, Internet Explorer 从IE9开始<canvas> 。Chrome和Opera 9+ 也支持 <canvas>。 二、Canvas基本使用 2.1 <canvas>元素
IT人一直在路上
2019/09/16
1.1K0
Day 3 学习Canvas这一篇文章就够了
Canvas绘制飞机飞行
运用Canvas绘制一个飞机飞行动画。 动画效果如下: 实现代码如下: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF
越陌度阡
2020/11/26
7600
Canvas绘制飞机飞行
从一个画板demo学习canvas
初学canvas,做了一个画板应用,地址点这里 。本篇为canvas的一些基础思想和注意事项,不是基础api。主要是在于touch事件上的实践经验
Ganother
2019/02/26
6770
探究 canvas 绘图中撤销(undo)功能的实现方式
最近在做网页版图片处理相关的项目,也算是初入了 canvas 的坑。项目需求中有一个给图片添加水印的功能。我们知道,在浏览器端实现图片添加水印功能,通常的做法就是使用 canvas 的 drawImage 方法。对于普通的合成(比如一张底图和一张 PNG 水印图片合成)来说,其大致实现原理如下:
逆葵
2019/04/25
2.2K0
探究 canvas 绘图中撤销(undo)功能的实现方式
前端|利用<canvas>画布制作地球轨道
<canvas> 画布标签常用于绘制图像,但是,<canvas> 元素本身并没有绘制能力,它仅仅是图形的容器,要想通过<canvas>画布标签来绘制图像,还需要调用js方法。其中最常见得方法是getContext()方法,它可返回一个对象,该对象提供了用于在画布上绘图的方法和属性,可在画布上绘制文本、线条、矩形、圆形等等。今天就利用<canvas> 画布来绘制一下地球轨道的效果。
算法与编程之美
2020/04/17
2.1K0
Canvas基本动画-太阳系的动画 原
1、清空canvas 除非接下来要画的内容会完全充满canvas(例如背景图),否则你需要清空所有,最简单的方法是使用clearRect方法 2、保存canvas状态 如果你要改变一些会改变canvas状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下 3、绘制动画图形 4、恢复canvas状态 如果已经保存了canvas的状态,可以先恢复它,然后重绘下一帧。
tianyawhl
2019/04/04
9040
Canvas 从进阶到退学
接着 《Canvas 从入门到劝朋友放弃(图解版)》 ,本文继续补充 canvas 基础知识点。
德育处主任
2022/12/13
2.1K0
Canvas 从进阶到退学
HTML5(六)——Canvas 高级操作
angle 旋转弧度,如果想使用角度,可以把角度转成弧度,公式为:deg * Path.PI/180。
呆呆
2021/09/29
1.4K0
如何在canvas中模拟css的背景图片样式
笔者开源了一个Web思维导图mind-map,最近在优化背景图片效果的时候遇到了一个问题,页面上展示时背景图片是通过css使用background-image渲染的,而导出的时候实际上是绘制到canvas上导出的,那么就会有个问题,css的背景图片支持比较丰富的效果,比如通过background-size设置大小,通过background-position设置位置,通过background-repeat设置重复,但是canvas笔者只找到一个createPattern()方法,且只支持设置重复效果,那么如何在canvas里模拟一定的css背景效果呢,不要走开,接下来一起来试试。
街角小林
2023/03/01
7.4K0
如何在canvas中模拟css的背景图片样式
Canvas 10款基础滤镜(原理篇)
—\ntheme: smartblue\n—\n# 本文简介\n\n点赞 + 关注 + 收藏 = 学会了\n\n \n\n在学习 Canvas 的路上大概率会了解到 滤镜 ,这是个很有趣的东西。\n\n用 Canvas 开发滤镜需要对几何数学、颜色的理解等领域都有一定基础。\n\n但先别关掉文章,没基础也没关系,不是还可以复制粘贴吗?(像极了正则表达式)\n\n \n\n我在学习的过程中也尝试过写一些简单的滤镜,也收集过很多滤镜。但由于历史有点久远了,我找不到收集回来的滤镜出处了。如果有冒犯到原作者,请联系我进行整改~\n\n \n\n如果你对 Canvas 感兴趣,可以关注《一看就懂的 Canvas 专栏》\n\n \n\n本文使用到的猫咪图片素材来自 The Cat API。\n\n如果想要更多猫猫狗狗的API,可以查看 《前端需要的免费在线api接口》 。\n\n \n\n \n\n# 开发环境\n\n本文所列出的例子都是在 .html 文件里编写的,可以直接看看代码仓库。\n\n需要注意的是,本例使用到的方法虽然是 Canvas 原生操作像素的方法,但必须有个服务器才能运行起来,不然不会生效。\n\n \n\n可以搭建本地服务器运行本文案例,方法有很多种。\n\n比如你使用 Vue 或者 React 的脚手架搭建的项目,运行后就能跑起本文所有案例。\n\n又或者使用 http-server 启动本地服务。\n\n \n\n本文使用一个更简单的工具。我用 VS Code 开发,安装了 Live Server 插件,它可以启动一个本地服务,同时自带热更新功能。\n\n \n\n开发环境方面就讲这么多,不是本文重点。如果是在不懂怎么搭建环境可以在留言区提问~\n\n \n\n \n\n# 滤镜原理\n\n众所周知,位图是由像素组成,像素是位图最小的信息单元。 你可以把日常看到大多数图片理解成由一个个点组成的图像。\n\n滤镜的作用是通过具体规则,将图像中的像素点按照计算得出的公式修改一遍再重新渲染出来。\n\n\n
德育处主任
2022/09/08
4890
Canvas 10款基础滤镜(原理篇)
canvas相关API简介及思考
canvas对于大部分前端开发人员来说,可以用一个词来形容--既熟悉又陌生。为什么这样说,因为大部分前端开发人员在写业务代码的时候用到canvas的概率很小,就算用到了,也只是类似drawImage这个API,并且,对drawImage这个API的了解也并不深刻,只知道它可以将图片绘制到画布上,其他的功能大部分人应该都不知道。
terrence386
2022/07/15
8230
canvas相关API简介及思考
canvas学习笔记(八)—- 基本动画
1.用window.setInterVal()、window.setTimeOut()和window.requestAnimationFrame()来定期执行一个指定函数
Java架构师必看
2021/08/19
6930
canvas学习和滤镜实现
最近学习了 HTML5 中的重头戏–canvas。利用 canvas,前端人员可以很轻松地、进行图像处理。其 API 繁多,这次主要学习常用的 API,并且完成以下两个代码:
心谭博客
2020/04/20
7060
canvas绘制闹钟-方法2 原
 drawBackground()里面的ctx.save() 为什么不能放到上面的原因:一般ctx.save() 与ctx.restore()是成对出现的。ctx.save() 的作用是保存状态,ctx.restore()指弹出保存的状态来使用,如果此函数中没有ctx.save(),则在draw()中执行ctx.restore(); 就没有意义,因为没有已经保存的状态,导致绘图有问题。
tianyawhl
2019/04/04
6080
简单三步做出微信h5表情包
在聊天中,表情占据了越来越重要的地位。它能够承载一些难以言说的情感,也让聊天显得更加生动活泼 。 项目实现: 选取mp4文件或摄像头录制生成微信GIF表情,可添加文字和特效(制作中)等功能。 流程图:
腾讯NEXT学位
2018/07/05
2.7K0
【基础系列】Canvas专题
        为了在canvas上绘制,你必须先得到一个画布上下文对象的引用,用本方法即可完成这一操作,格式如下:
江中散人_Jun
2023/10/16
4380
【基础系列】Canvas专题
实现Web端自定义截屏
当客户在使用我们的产品过程中,遇到问题需要向我们反馈时,如果用纯文字的形式描述,我们很难懂客户的意思,要是能配上问题截图,这样我们就能很清楚的知道客户的问题了。
神奇的程序员
2021/02/04
2.5K0
H5和微信小游戏 Canvas API 整理前言
这段时间闲下来,系统学习了微信小程序和微信小游戏,发现还是挺有意思的。现在微信小游戏的开发都离不开游戏引擎,用原生小游戏开发工具开发的很少很少。但是毕竟我不是专业游戏开发,所有游戏引擎就不搞了,我们就单纯来看原生微信小游戏开发。
大公爵
2018/10/10
3K0
H5和微信小游戏 Canvas API 整理前言
CSS3魔法堂:CSS3滤镜及Canvas、SVG和IE滤镜替代方案详解
一、前言                                  IE特有的滤镜常常作为CSS3各种新特性的降级处理补充,而Adobe转向HTML5后与Chrome合作推出CSS3的Filter特性,因此当前仅Webkit内核的浏览器支持CSS3 Filter,而FF和IE10+则需要使用SVG滤镜(svg effects for html)或Canvas作为替代方案处理了,而IE5.5~9则使用IE滤镜、JS+DIV或VML处理!本篇为先占个坑,以后慢慢填^_^!!!   CSS3 Filter
^_^肥仔John
2018/01/18
2K0
CSS3魔法堂:CSS3滤镜及Canvas、SVG和IE滤镜替代方案详解
Canvas初实现拍照小游戏
由于实现的比较简单,且在部分机型上会出现点小问题,此处仅作为js代码的记录,暂不打算写相关教程。
WindCoder
2018/09/20
1.1K0
相关推荐
Day 3 学习Canvas这一篇文章就够了
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验