1. 介绍
UPNG.js是一个轻量级的纯JavaScript库,专注于PNG图像的解析、编码和处理。它不依赖任何其他库,可在浏览器和Node.js环境中运行,为开发者提供了在前端直接处理PNG图像的能力,无需后端支持。
2. 核心API与属性
下面是主要的一些方法:
2.1. 主要方法
2.1.1. UPNG.decode(buffer)
- 描述:解析PNG图像数据
- 参数 :
buffer
- 包含PNG数据的Uint8Array - 返回 :包含图像信息的对象,具有以下属性:
width
:图像宽度height
:图像高度depth
:位深度ctype
:颜色类型data
:图像像素数据(Uint8Array)palette
:调色板数据(如果有)trns
:透明度数据(如果有)
2.1.2. UPNG.encode(frames, width, height, ctype, [depth])
- 描述:将图像数据编码为PNG格式
- 参数 :
frames
:包含一个或多个帧的数组,每个帧是Uint8Array类型的像素数据width
:图像宽度height
:图像高度ctype
:颜色类型(1=灰度, 2=RGB, 3=索引色, 4=灰度+Alpha, 6=RGBA)depth
:位深度(可选,默认8)
- 返回:包含PNG数据的Uint8Array
2.1.3. UPNG.toRGBA8(img)
- 描述:将解码后的图像转换为RGBA8格式
- 参数 :
img
- 由UPNG.decode()
返回的图像对象 - 返回:包含RGBA8格式像素数据的Uint8Array
2.1.4. UPNG.resize(img, width, height)
- 描述:调整图像大小
- 参数 :
img
- 由UPNG.decode()
返回的图像对象width
- 新宽度height
- 新高度
- 返回:调整大小后的图像对象
2.1.5. UPNG.strip(buffer)
- 描述:移除PNG图像中的元数据(如注释、ICC配置文件等)
- 参数 :
buffer
- 包含PNG数据的Uint8Array - 返回:处理后的PNG数据Uint8Array
2.2. 辅助方法
UPNG.text(buffer, texts)
:添加文本元数据到PNGUPNG.join(images, delays)
:将多个图像合并为一个APNG动画UPNG.diff(prev, curr)
:计算两个图像的差异,用于优化动画帧
3. 基本使用示例
3.1. 在浏览器中解码并显示PNG
通过fetch
加载PNG图像,使用UPNG.decode
解析数据,转换为RGBA8格式后,通过Canvas渲染到页面。
javascript
// 从URL加载PNG并显示
fetch('image.png')
.then(response => response.arrayBuffer())
.then(buffer => {
const uint8Buffer = new Uint8Array(buffer);
// 解码PNG
const img = UPNG.decode(uint8Buffer);
// 转换为RGBA8格式
const rgba = UPNG.toRGBA8(img);
// 创建canvas并显示图像
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(img.width, img.height);
imageData.data.set(rgba);
ctx.putImageData(imageData, 0, 0);
document.body.appendChild(canvas);
});
3.2. 编码并下载PNG图像
创建红色100x100的RGBA像素数据,使用UPNG.encode
编码为PNG格式,再通过Blob和下载链接实现图像下载。
javascript
// 创建一个简单的红色100x100图像
const width = 100;
const height = 100;
const size = width * height * 4; // RGBA格式
const data = new Uint8Array(size);
// 填充红色
for (let i = 0; i < size; i += 4) {
data[i] = 255; // R
data[i + 1] = 0; // G
data[i + 2] = 0; // B
data[i + 3] = 255; // A (不透明)
}
// 编码为PNG
const pngBuffer = UPNG.encode([data], width, height, 6); // 6 = RGBA
// 创建下载链接
const blob = new Blob([pngBuffer], { type: 'image/png' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'red-square.png';
a.click();
// 清理
URL.revokeObjectURL(url);
3.3. 调整图像大小
基于已解码的图像对象,使用UPNG.resize
调整尺寸至200x200,再重新编码为PNG。
javascript
// 假设我们已经有了解码后的img对象
const resizedImg = UPNG.resize(img, 200, 200); // 调整为200x200
const resizedPng = UPNG.encode([UPNG.toRGBA8(resizedImg)], 200, 200, 6);
3.4. 在Node.js中使用
通过Node.js的fs
模块读取本地PNG文件,解码后调整尺寸(缩小为原尺寸的1/2),重新编码并保存到本地。
javascript
const fs = require('fs');
const UPNG = require('upng-js');
// 读取PNG文件
const buffer = fs.readFileSync('input.png');
// 解码
const img = UPNG.decode(buffer);
// 处理 - 例如调整大小
const resized = UPNG.resize(img, img.width / 2, img.height / 2);
// 编码
const outputBuffer = UPNG.encode([UPNG.toRGBA8(resized)], resized.width, resized.height, 6);
// 保存
fs.writeFileSync('output.png', outputBuffer);
4. 实际应用场景
在前文介绍了UPNG.js的基本API和用法后,这里补充几个更贴近实际开发需求的应用场景示例,帮助你更好地理解如何在项目中使用这个库。
4.1. 前端图像压缩工具
在浏览器中实现PNG图像压缩:通过读取用户选择的PNG文件,根据质量参数调整颜色类型(如RGBA转RGB)和位深度,解码后重新编码,最终生成压缩后的PNG并提供预览和下载。
javascript
// 图像压缩函数
async function compressPng(file, quality = 0.8) {
// 读取文件
const arrayBuffer = await file.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);
// 解码图像
const img = UPNG.decode(buffer);
// 根据质量参数决定压缩策略
let ctype = img.ctype;
let depth = img.depth;
// 降低质量:如果是RGBA,尝试转为RGB(移除透明度)
if (quality < 0.7 && ctype === 6) {
ctype = 2; // 转为RGB
}
// 降低位深度
if (quality < 0.5 && depth > 8) {
depth = 8;
}
// 转换为RGBA8格式
const rgba = UPNG.toRGBA8(img);
// 编码为新的PNG
const compressedBuffer = UPNG.encode([rgba], img.width, img.height, ctype, depth);
// 计算压缩率
const originalSize = buffer.length;
const compressedSize = compressedBuffer.length;
const compressionRatio = (compressedSize / originalSize).toFixed(2);
console.log(`压缩完成: 原始大小 ${originalSize} bytes, 压缩后 ${compressedSize} bytes, 压缩率 ${compressionRatio}`);
// 返回压缩后的文件
return new Blob([compressedBuffer], { type: 'image/png' });
}
// 使用示例
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file || !file.type.match('image/png')) return;
const compressedBlob = await compressPng(file, 0.6);
// 显示压缩后的图像
const img = document.createElement('img');
img.src = URL.createObjectURL(compressedBlob);
document.body.appendChild(img);
// 提供下载链接
const a = document.createElement('a');
a.href = img.src;
a.download = 'compressed_' + file.name;
a.textContent = '下载压缩后的图像';
document.body.appendChild(a);
});
4.2. 前端图像裁剪工具
实现简单的PNG图像裁剪功能:加载图像后,指定裁剪区域(x、y坐标及宽高),通过像素数据提取裁剪区域的RGBA值,重新编码为新的PNG并通过Canvas显示裁剪结果。
javascript
// 裁剪图像函数
function cropPng(img, x, y, width, height) {
// 确保裁剪区域在图像范围内
x = Math.max(0, Math.min(x, img.width));
y = Math.max(0, Math.min(y, img.height));
width = Math.min(width, img.width - x);
height = Math.min(height, img.height - y);
// 转换为RGBA8格式以便处理
const rgba = UPNG.toRGBA8(img);
const croppedData = new Uint8Array(width * height * 4);
// 提取裁剪区域的像素
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const srcIndex = ((y + row) * img.width + (x + col)) * 4;
const destIndex = (row * width + col) * 4;
// 复制RGBA值
croppedData[destIndex] = rgba[srcIndex]; // R
croppedData[destIndex + 1] = rgba[srcIndex + 1]; // G
croppedData[destIndex + 2] = rgba[srcIndex + 2]; // B
croppedData[destIndex + 3] = rgba[srcIndex + 3]; // A
}
}
// 编码为新的PNG
return UPNG.encode([croppedData], width, height, 6); // 6 = RGBA
}
// 使用示例
async function handleCrop() {
// 加载图像
const response = await fetch('image.png');
const buffer = new Uint8Array(await response.arrayBuffer());
const img = UPNG.decode(buffer);
// 裁剪图像 (x=50, y=50, width=200, height=200)
const croppedBuffer = cropPng(img, 50, 50, 200, 200);
// 显示裁剪结果
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');
// 将裁剪后的图像数据绘制到canvas
const croppedImg = UPNG.decode(croppedBuffer);
const croppedRgba = UPNG.toRGBA8(croppedImg);
const imageData = ctx.createImageData(200, 200);
imageData.data.set(croppedRgba);
ctx.putImageData(imageData, 0, 0);
document.body.appendChild(canvas);
}
4.3. 创建简单的APNG动画
利用UPNG.js支持多帧编码的特性,创建APNG动画:生成红、绿、蓝三帧像素数据,设置每帧延迟300ms,编码为APNG格式后,在页面显示动画并提供下载。
javascript
// 创建APNG动画
async function createAnimation() {
// 创建3帧动画
const width = 200;
const height = 200;
const frames = [];
const delays = []; // 每帧的延迟时间(以毫秒为单位)
// 第一帧:红色
const frame1 = new Uint8Array(width * height * 4);
for (let i = 0; i < frame1.length; i += 4) {
frame1[i] = 255; // R
frame1[i + 1] = 0; // G
frame1[i + 2] = 0; // B
frame1[i + 3] = 255; // A
}
frames.push(frame1);
delays.push(300); // 显示300ms
// 第二帧:绿色
const frame2 = new Uint8Array(width * height * 4);
for (let i = 0; i < frame2.length; i += 4) {
frame2[i] = 0; // R
frame2[i + 1] = 255; // G
frame2[i + 2] = 0; // B
frame2[i + 3] = 255; // A
}
frames.push(frame2);
delays.push(300);
// 第三帧:蓝色
const frame3 = new Uint8Array(width * height * 4);
for (let i = 0; i < frame3.length; i += 4) {
frame3[i] = 0; // R
frame3[i + 1] = 0; // G
frame3[i + 2] = 255; // B
frame3[i + 3] = 255; // A
}
frames.push(frame3);
delays.push(300);
// 编码为APNG
const apngBuffer = UPNG.encode(frames, width, height, 6, 8, delays);
// 显示动画
const img = document.createElement('img');
img.src = URL.createObjectURL(new Blob([apngBuffer], { type: 'image/png' }));
document.body.appendChild(img);
// 提供下载
const a = document.createElement('a');
a.href = img.src;
a.download = 'color-animation.png';
a.textContent = '下载动画';
document.body.appendChild(a);
}
// 调用函数创建动画
createAnimation();
4.4. 图像水印添加工具
给PNG图像添加文字水印:解码图像后转为RGBA8格式,通过临时Canvas绘制图像和半透明白色文字水印,再从Canvas获取带水印的像素数据,重新编码为PNG并显示。
javascript
// 添加水印函数
async function addWatermark(pngBuffer, text) {
// 解码图像
const img = UPNG.decode(pngBuffer);
const rgba = UPNG.toRGBA8(img);
// 创建临时canvas用于绘制水印
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
// 将图像数据绘制到canvas
const imageData = ctx.createImageData(img.width, img.height);
imageData.data.set(rgba);
ctx.putImageData(imageData, 0, 0);
// 绘制水印文字
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; // 半透明白色
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, img.width / 2, img.height / 2);
// 从canvas获取带水印的图像数据
const watermarkedData = ctx.getImageData(0, 0, img.width, img.height).data;
// 编码为新的PNG
return UPNG.encode([watermarkedData], img.width, img.height, 6);
}
// 使用示例
async function processImageWithWatermark() {
// 加载图像
const response = await fetch('photo.png');
const buffer = await response.arrayBuffer();
// 添加水印
const watermarkedBuffer = await addWatermark(new Uint8Array(buffer), 'My Watermark');
// 显示结果
const img = document.createElement('img');
img.src = URL.createObjectURL(new Blob([watermarkedBuffer], { type: 'image/png' }));
document.body.appendChild(img);
}
4.5. Node.js批量图像处理
在Node.js环境下批量处理PNG:遍历指定输入目录的PNG文件,统一缩放到最大800x600尺寸(等比例缩放),处理后保存到输出目录;若无需缩放则直接复制文件,同时处理过程中捕获并打印错误信息。
javascript
const fs = require('fs');
const path = require('path');
const UPNG = require('upng-js');
// 批量处理目录中的所有PNG图像
async function batchProcessImages(inputDir, outputDir, maxWidth, maxHeight) {
// 创建输出目录(如果不存在)
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 读取输入目录中的所有文件
const files = fs.readdirSync(inputDir);
for (const file of files) {
// 只处理PNG文件
if (path.extname(file).toLowerCase() !== '.png') continue;
const inputPath = path.join(inputDir, file);
const outputPath = path.join(outputDir, file);
try {
// 读取并解码图像
const buffer = fs.readFileSync(inputPath);
const img = UPNG.decode(buffer);
// 计算等比例缩放后的尺寸
const scale = Math.min(maxWidth / img.width, maxHeight / img.height);
const newWidth = Math.round(img.width * scale);
const newHeight = Math.round(img.height * scale);
// 如果不需要缩放,直接复制文件
if (newWidth === img.width && newHeight === img.height) {
fs.copyFileSync(inputPath, outputPath);
console.log(`已复制: ${file}`);
continue;
}
// 调整大小并重新编码
const resizedImg = UPNG.resize(img, newWidth, newHeight);
const resizedData = UPNG.toRGBA8(resizedImg);
const outputBuffer = UPNG.encode([resizedData], newWidth, newHeight, 6);
// 保存处理后的图像
fs.writeFileSync(outputPath, outputBuffer);
console.log(`已处理: ${file} (${img.width}x${img.height} → ${newWidth}x${newHeight})`);
} catch (error) {
console.error(`处理 ${file} 时出错:`, error);
}
}
console.log('批量处理完成');
}
// 使用示例:将input目录中的PNG图像缩放到最大800x600尺寸,保存到output目录
batchProcessImages('./input', './output', 800, 600);
这些示例覆盖了前端图像压缩、裁剪、动画创建、水印添加以及Node.js批量处理等常见场景。通过这些例子,你可以看到UPNG.js如何在实际项目中解决各种PNG图像处理需求,帮助你构建更丰富的图像相关功能。
5. 优缺点分析
5.1. 优点
- 纯JavaScript实现,无需依赖其他库
- 体积小,适合前端引入
- 同时支持浏览器和Node.js环境
- API简洁易用,学习成本低
- 支持多种PNG特性和颜色模式
5.2. 缺点
- 处理大型图像时可能性能不如原生代码
- 功能集不如专业图像处理库全面
- 不支持其他图像格式(如JPEG、WebP等)
6. 总结
UPNG.js为JavaScript开发者提供了一个轻量级但功能强大的PNG图像处理解决方案。无论是构建前端图像编辑工具、优化图像上传流程,还是动态生成图像内容,UPNG.js都能满足基本到中等复杂度的需求。
由于其纯 JavaScript 特性,它特别适合那些希望在客户端完成所有图像处理工作,从而减少服务器负载或提升用户体验的应用场景。
如果你正在寻找一个简单易用且无需后端支持的 PNG 处理库,UPNG.js 绝对值得一试。
本次分享就到这儿啦,我是鹏多多,如果看了觉得有帮助的,欢迎 点赞 关注 评论,在此谢过道友;
往期文章
- 前端图片裁剪Cropper.js核心功能与实战技巧详解
- 编辑器也有邪修?盘点VS Code邪门/有趣的扩展
- flutter-使用AnimatedDefaultTextStyle实现文本动画
- js使用IntersectionObserver实现目标元素可见度的交互
- Web前端页面开发阿拉伯语种适配指南
- 让网页拥有App体验?PWA 将网页变为桌面应用的保姆级教程PWA
- 助你上手Vue3全家桶之Vue3教程
- 使用nvm管理node.js版本以及更换npm淘宝镜像源
- 超详细!Vue的十种通信方式
- 手把手教你搭建规范的团队vue项目,包含commitlint,eslint,prettier,husky,commitizen等等