写点什么

[JS 入门到进阶] 手写裁剪图片的工具,并部署。一键裁剪掘金文章封面

作者:HullQin
  • 2022 年 8 月 30 日
    广东
  • 本文字数:2886 字

    阅读完需:约 9 分钟

[JS入门到进阶] 手写裁剪图片的工具,并部署。一键裁剪掘金文章封面

我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

背景

裁剪图片,一个超级常用的功能!


我个人非常喜欢 Windows 的「画图」工具。这款工具我小学二年级时非常喜欢玩。长大后,也对它爱不释手。因为「画图」占用内存非常小,可以放大 8 倍,方便像素级别调整细节,裁剪、移动都非常方便!


但是工作后一直在用 Mac,裁剪图片没那么方便了。Mac 自带的预览工具似乎有裁剪能力,但是自己总是用不惯,还是喜欢 Windows「画图」的交互。


最近,每次更新掘金文章、新建掘金专栏,总是需要配个图。掘金配图是需要比例的,尺寸不成比例时,掘金会帮你裁剪中间的部分。但如果我们能提前按照比例要求准备好素材,提前查看效果,那是令强迫症最舒服的!

解决方案

我不喜欢用各种 APP,还需要下载,还需要学交互,而且很多时候我希望自动化实现一些功能,APP 的实现成本是很高的!


网上也有很多在线裁剪的。但是也存在问题:


  1. 很多网站要求图片先上传,修改完再下载,如果图片包含隐私,就会有隐私泄漏风险。

  2. 那些网站往往有很多广告,速度还比较慢,体验很差!

  3. 无法自动化,依然需要手动操作。


作为前端开发者,我打算自己写一个「图片裁剪」工具!

具体需求

  1. 允许用户手动选择本地图片文件,并展示出来。

  2. 不能有后端,不允许上传至服务器,减少带宽、提高速度。

  3. 裁剪是纯前端的过程,裁剪完毕后,用户有办法保存图片。

  4. 目前提供 2 种一键裁剪尺寸:掘金文章封面、掘金专栏封面。点击后,图片裁剪为对应比例,居中展示。

  5. 裁剪后,可以右键保存。


MVP 版本暂不支持手动拖拽裁剪。有其它尺寸需求,可以先改代码实现,也很方便。

体验地址 & 源码

体验地址: https://tool.hullqin.cn/img-editor.html


源码: https://github.com/HullQin/tool-hullqin-cn

实现思路


这是实际的案例,用了我《《 合 成 大 西 瓜 》 重 制 版 !( 联 机 版 在 做 了 )》这篇文章封面做例子。可以看到:


  • 用 input 实现了选择文件。

  • 用 2 个 button,方便一键裁剪 2 种尺寸。

  • 用 canvas 展示了上传的图片和裁剪后的图片,并且右键可以「保存」或「复制」。

写代码

html 的结构

很简单,我们定义好:


<label for="src">请选择图片: </label><input id="src" type="file" /><div>  <button id="slice-article">切割为掘金文章封面</button>  <button id="slice-column">切割为掘金专栏封面</button></div><canvas id="work"></canvas><img id="background" class="hidden" alt="" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" />
复制代码


其中,img 是display: none的元素,上传后,会把 img 的 src 替换为上传图片的 base64 内容。它是隐藏的,因为我们用 canvas 来绘制最终图片,而 canvas 是支持对图片做裁剪操作并且可以保存的。


现在 img 初始值是个 1*1 大小的透明图片,是为了避免它初始化时触发下载行为。


此外,提前定义好 JS 要用到的 dom 元素:


const inputEle = document.getElementById('src');const canvasEle = document.getElementById('work');const imgEle = document.getElementById('background');const sliceArticleEle = document.getElementById('slice-article');const sliceColumnEle = document.getElementById('slice-column');const ctx = canvasEle.getContext('2d');
复制代码

选择图片并展示

借助 input 的onchange,用户选择文件后会触发。


随后我们通过 FileReader 读取这个用户选择的文件的二进制内容,以 base64 编码赋值给 img 的 src。


然后,把 img 画在 canvas 上。


当然,刚选择图片后,canvas 比例要跟用户选择的 img 保持一致,所以还要调整 canvas 的尺寸。


const resetCanvas = () => {  canvasEle.setAttribute('width', imgEle.width.toString());  canvasEle.setAttribute('height', imgEle.height.toString());  ctx.drawImage(imgEle, 0, 0);};
const onFileChange = (event) => { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function () { imgEle.onload = () => { resetCanvas(); }; imgEle.src = this.result; }; reader.readAsDataURL(file);}inputEle.addEventListener('change', onFileChange);
复制代码

裁剪图片功能

定义一个函数,实现裁剪能力。它需要 width 和 height 作为参数。表明了裁剪目标的比例(不代表真实的数值)。


如果现在的宽高跟目标比例是一样的,那么不需要做任何事情。计算方法就是判断 currentWidth / currentHeight 和 width / height 是否相等。但是除法效率不如乘法,所以我们改成判断 currentWidth * height 和 currentHeight * width 是否相等。而且这样有个好处:后面会重复利用乘法结果,减少了代码运算次数。


如果两个比例不想等,那么要把过长的那条边裁短,重新设置 canvas 大小,重新绘制图片。


可以了解一下 canvas 这个绘图 API:Canvas: Using images


const setCanvasSize = (width, height) => {  const currentWidth = imgEle.width;  const currentHeight = imgEle.height;  const a = currentWidth * height;  const b = currentHeight * width;  if (a === b) return;  if (a > b) {    const newWidth = b / height;    canvasEle.setAttribute('width', newWidth.toString());    canvasEle.setAttribute('height', imgEle.height.toString());    ctx.drawImage(imgEle, (currentWidth - newWidth) / 2, 0, newWidth, currentHeight, 0, 0, newWidth, currentHeight);    return;  }  const newHeight = a / width;  canvasEle.setAttribute('width', imgEle.width.toString());  canvasEle.setAttribute('height', newHeight.toString());  ctx.drawImage(imgEle, 0, (currentHeight - newHeight) / 2, currentWidth, newHeight, 0, 0, currentWidth, newHeight);};
复制代码


最后添加点击事件即可:


const onClickSliceArticle = () => {  setCanvasSize(1303, 734);};const onClickSliceColumn = () => {  setCanvasSize(480, 270);};sliceArticleEle.addEventListener('click', onClickSliceArticle);sliceColumnEle.addEventListener('click', onClickSliceColumn);
复制代码


至此,大功告成啦!我们手动实现了一个「裁剪图片」的工具!


将来,我们前端开发者,手撸一个自动化的 PS 指日可待!快快关注 HullQin 学起来~

写在最后

我是 HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者 HullQin 授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加 Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

发布于: 刚刚阅读数: 5
用户头像

HullQin

关注

公众号【线下聚会游戏】 2020.10.07 加入

game.hullqin.cn 我做了一些联机桌游网页:支持2-10人联机的UNO、2-4人联机的斗地主、2人联机的五子棋。无需下载,点开即玩!叫上朋友,即刻开局!不看广告,不做任务,享受「纯粹」的游戏!

评论

发布
暂无评论
[JS入门到进阶] 手写裁剪图片的工具,并部署。一键裁剪掘金文章封面_CSS_HullQin_InfoQ写作社区