草庐IT

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

HullQin 2023-03-28 原文
大家好,我是公众号「线下聚会游戏」作者HullQin,开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏。

背景

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

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

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

最近,每次更新51CTO文章,总是需要配个图。51CTO配图推荐是需要比例的。如果我们能提前按照比例要求准备好素材,提前查看效果,那是令强迫症最舒服的!

解决方案

我不喜欢用各种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 噢~我有空了会分享做游戏的相关技术。

有关[JS入门到进阶] 手写裁剪图片的工具,并部署。一键裁剪51CTO文章封面的更多相关文章

  1. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  2. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  5. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  6. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  7. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  10. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

随机推荐