前言

最近,女王大大日常找我弄图片,本来之前我一直是ps帮他弄得,后来- -,ps不能分割过长的图片,我就想想能不能通过代码来帮他实现好了。

经过我在npm搜索一番,发现没有一个纯代码层面的high tools来实现这个功能,索性就自己通过node-canvas这个库弄个小例子出来,自己使用好了

gm这个库是可以实现的,但是他需要额外安装两个工具,所以- -我就放弃了

简单搭建一下

  • 新建一个clip目录

  • 初始化一个node项目工程

    npm init -y
  • 安装依赖,这里主要用到了三个依赖,分别是处理图片获取图片大小压缩成zip文件

    npm i canvas image-size archiver -S

简单的使用一下

首先,先说说我的需求,下面是一张名为 clip.png 的图片,

我们需要做的就是把他均分为3等分,所以,我们尝试写一下我们的代码

// 创建写入流
const { createWriteStream } = require('fs')
// 导入压缩文件库
const archiver = require('archiver')
// 导入canvas库,用于裁剪图片
const { createCanvas, loadImage } = require('canvas')
// 用户获取图片大小
const sizeOf = require('image-size')
!(async () => {
// 加载图片
const image = await loadImage('./clip.png')
// 获取图片宽高
const { width, height } = await sizeOf('./clip.png')
// 创建等宽登高的canvas
const mainCanvas = createCanvas(width, height)
// 获取canvas上下文
const ctx = mainCanvas.getContext('2d')
// 绘图
ctx.drawImage(image, 0, 0)
// 压缩成zip
const archive = archiver('zip', {
zlib: { level: 9 } // Sets the compression level.
});
// 输出到当前文件夹下的 clip.zip
const output = createWriteStream(__dirname + '/clip.zip');
archive.pipe(output);
// 明确我们需要垂直分割成几份
const num = 3
// 获取一份的高度
const oneHeight = height / num
for (let i = 0; i < num; i++) {
// 创建一份裁剪的canvas
let clipCanvas = createCanvas(width, oneHeight)
// 获取canvas上下文
const clipCtx = clipCanvas.getContext('2d')
// 通过 clipCtx 裁剪 mainCanvas
clipCtx.drawImage(mainCanvas, 0, oneHeight * i, width, oneHeight, 0, 0, width, oneHeight)
// 放到压缩器里
archive.append(clipCanvas.toBuffer(), { name: `${i + 1}.png` })
// 主动释放内存
clipCanvas = null
}
archive.finalize();
})()

跑一下我们写的程序,你会惊奇的发现,目录下多了一个clip.zip的压缩包,解压出来就是我们需要的图片啦

看到这里,其实我们已经实现我们想要的效果了,但是你会发现我们只是实现了水平的切割,如何实现垂直的呢?

也许你会说,加个判断不就好了?

// 垂直切割...伪代码
if(direction === "vertical"){
// 获取一份的高度
const oneHeight = height / num
for (let i = 0; i < num; i++) {
let clipCanvas = createCanvas(width, oneHeight)
// ...... 和上面一致
clipCtx.drawImage(mainCanvas, 0, oneHeight * i, width, oneHeight, 0, 0, width, oneHeight)
// ......
}
}else{
const oneWidth = width / num
for (let i = 0; i < num; i++) {
let clipCanvas = createCanvas(oneWidth, height)
// ...... 和上面一致
clipCtx.drawImage(mainCanvas, oneWidth * i, 0, width, oneHeight, 0, 0, width, oneHeight)
// ......
}
}

的确,这样写是可以,但是会不会觉得很不优雅?那我们是不是可以找找规律,把他封装成一份呢?答案是肯定可以的

这里我们可以新建一个数组配置,用来判断我们传递的参数来判断是 width/num 还是 height/num,闲话不多少了,开始写我们的代码把

// 创建写入流
const { createWriteStream } = require('fs')
// 压缩文件
const archiver = require('archiver')
// 批量裁剪
const { createCanvas, loadImage } = require('canvas')
// 获取图片大小
const sizeOf = require('image-size')
// 切割方向配置
const directionConfig = ["vertical", "horizontal"]
!(async () => {
const image = await loadImage('./clip.png')
const { width, height } = await sizeOf('./clip.png')
const mainCanvas = createCanvas(width, height)
const ctx = mainCanvas.getContext('2d')
ctx.drawImage(image, 0, 0)
// 压缩成zip
const archive = archiver('zip', {
zlib: { level: 9 } // Sets the compression level.
});
const output = createWriteStream(__dirname + '/clip.zip');
archive.pipe(output);
const clip = clipNumImage({ canvas: mainCanvas, width, height }, 3)
clip.forEach((img, idx) => {
archive.append(img, { name: `${idx + 1}.png` })
})
archive.finalize();
})() function clipNumImage(options, num, direction = "horizontal") {
const { canvas, ...canvasSize } = options
// 生成配置的参数
let directionIdx = getIdx(direction)
// 公有配置
const directionOptions = getOptions(canvasSize, directionIdx, num)
const clip = []
for (let i = 0; i < num; i++) {
let clipCanvas = createCanvas(...directionOptions)
const clipCtx = clipCanvas.getContext('2d')
clipCtx.drawImage(
canvas,
...directionOptions.map((val, idx) => idx === directionIdx ? val * i : 0),
...directionOptions, 0, 0,
...directionOptions
)
clip.push(clipCanvas.toBuffer())
clipCanvas = null
}
return clip
} // 获取传递过来的是垂直切割还是水平切割
function getIdx(direction) {
let directionIdx = directionConfig.indexOf(direction)
if (directionIdx === -1) {
directionIdx = 1
}
return directionIdx
}
// 获取切割参数
function getOptions(size, directionIdx, num) {
return Object.values(size).map((val, idx) => idx === directionIdx ? val / num : val)
}

通过数组,是不是感觉到我们代码比以前更加简洁了呢?好啦,这次小demo我们也写完啦,希望你通过这篇文章也学习到一点知识。感谢你的观看

ps: 对了,还有一个长图切割器,不写代码的话,直接下载用也是极好的,点我下载

最后

感谢各位观众老爷的观看 O(∩_∩)O

最新文章

  1. WCF学习之旅—WCF中传统的异常处理(十六)
  2. mysql启动失败:不能创建pid文件
  3. entity1
  4. 关于UIView的AutoresizingMask属性的研究
  5. 160个crackme-之Acid burn.exe
  6. 网络编程:socket--python核心编程(3),chapter 1
  7. MyEclipse8.5集成Tomcat7
  8. 圆角button
  9. SpringMvc中Interceptor拦截器用法
  10. List&lt;HashMap&gt;和HashMap
  11. PHP第三个教训 PHP基本数据类型
  12. 使用菜单(Menu)资源
  13. WebApi系列~不支持put和delete请求的解决方法
  14. 日积月累--Lock锁机制
  15. [No000018A]改善C#程序的建议11-20
  16. Dockfile基本语法
  17. “未能加载文件或程序集“XXX”或它的某一个依赖项。试图加载格式不正确的程序”问题的解决
  18. Java NIO -- 直接缓冲区与非直接缓冲区
  19. centos6.5生产环境编译安装nginx-1.11.3并增加第三方模块ngx_cache_purge、nginx_upstream_check、ngx_devel_kit、lua-nginx
  20. 020-安装centos6.5后的生命历程

热门文章

  1. interface和abstract 的区别和相同点
  2. 《机器学习_07_03_svm_核函数与非线性支持向量机》
  3. mysql小白系列_11 MHA
  4. 【Java_SSM】(三)maven中的配置文件setting的配置
  5. Node.js躬行记(1)——Buffer、流和EventEmitter
  6. MyBatis——Mapped Statements collection does not contain value for XXX
  7. 解决mysql:ERROR 1045 (28000): Access denied for user &#39;root&#39;@&#39;localhost&#39; (using password: NO/YES)
  8. Nginx301重定向
  9. SpringMVC中使用@Valid和BindingResult进行参数验证
  10. [Objective-C] 019_UIVIewController