-
Notifications
You must be signed in to change notification settings - Fork 516
添加图片压缩功能 #363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
添加图片压缩功能 #363
Conversation
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
一些逻辑需要修正
src/utils.js
Outdated
return window.webkitURL.createObjectURL(file); | ||
} | ||
return window.URL.createObjectURL(file); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
一般来说都是优先用标准接口;另外这个浏览器判断也有点随便;
const URL = window.URL || window.webkitURL || window.mozURL;
return URL.createObjectUrl(file)
src/index.js
Outdated
@@ -10,7 +10,7 @@ import { UploadManager } from "./upload"; | |||
import { imageMogr2, watermark, imageInfo, exif, pipeline } from "./image"; | |||
import { Observable } from "./observable"; | |||
import { StatisticsLogger } from "./statisticsLog"; | |||
|
|||
import compressImage from "./compress.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为啥这里多了两个空格 并且有.js 跟别的地方不一样。。?
src/compress.js
Outdated
return new Promise((resolve, reject) => { | ||
let url = createObjectURL(this.file); | ||
let img = new Image(); | ||
img.src = url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
要习惯性地事件绑定完之后再设 src
src/compress.js
Outdated
return Promise.reject(new Error(`unsupport file type: ${this.outputType}`)); | ||
} | ||
if (!isSupportedType(this.outputType)) { | ||
this.outputType = DEFAULT_TYPE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为什么输入不做限制,输出却有限制,并且还有默认转换逻辑?
这个逻辑应该斟酌一下,两个点:
1、能否支持某种格式和是否决定支持/限制某种格式是不一样的;
如果支持,从程序角度看输入支持与输出支持又是两个不同的维度(比如浏览器支持度)
2、这种输入输出相关的信息应该在 README 里提一下,
比如这种用户未必预期的特殊的转换逻辑,
比如使用支持类型白名单还是直接依赖于浏览器支持程度还是如何,
不应该让用户去猜或者去试或者看代码
src/compress.js
Outdated
width = dw; | ||
height = dh; | ||
|
||
if (i === steps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
所属的 while 里堆了太多东西,并且这个逻辑跟循环无关,
应该拆分提取出去分步处理,可以类似这样:
let src, context;
for (let i = 0; i < steps; i++) {
// ***
}
// ***;无需 if (i === steps)
README.md
Outdated
```JavaScript | ||
var imgLink = qiniu.compressImage(file, options).then(res => { | ||
// res : { | ||
// dist: 压缩后的 blob 对象,图片压缩后的文件如果比原来还大则默认返回源文件 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
要阐明如果压缩比原来大、并且宽高也大于这里的要求的时候会如何处理;
并且要确认一下,单纯的、细微的缩放有没有可能反而导致体积增大,如果有,这里会更不好处理。
src/compress.js
Outdated
let canvas = src === source ? mirror : source; | ||
|
||
// save data | ||
let data = context.getImageData(0, 0, width, height); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里有个小问题。
如果用户设置了最大宽度或者最大高度,并且图片确实超出了限制,
那么用户的期望应该是,图片缩小到“恰好”匹配上限的尺寸,
而不是比它更小,所以最后一步不应该直接使用 dw 和 dh。
这么做的话,会带来一个实现上的改动,
就是我们必须保证缩放的结果等于目标尺寸,
或者退一步说可以略微大于目标尺寸,然后把超界部分裁剪掉。
(因为如果结果偏小,那么按照目标尺寸截取出来的图片就会有多余的“杂边”)
如果要确保等于,那么我们需要对找出最后一步缩放,做特殊处理,保证这次一步到位;
如果选择大于等于,那么我们可以粗糙地实现成,每一步缩放结果总是不会比原来小。
src/compress.js
Outdated
|
||
while (i < steps) { | ||
let dw = width * factor | 0; | ||
let dh = height * factor | 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
接后面提到的缩放问题。
这里每一次缩小都会产生误差,取整会使结果尺寸总是偏小,
若干次误差累计后误差可能会进一步放大,就会产生后面提到的可能的问题。
改造方案,
如果选择恰好相等,那么 dw 和 dh 要改成最后一步不使用迭代结果,而是直接使用最终尺寸;
如果选择可适当超出,那么这里就不要直接取整,而是保证 Math.ceil
src/compress.js
Outdated
|
||
// resize | ||
canvas.width = width; | ||
canvas.height = height; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
查一下这一步 “resize” 是不是相当于以 (0, 0) 为原点的裁切,
如果不是的话应该也有裁切和调整画布的方法的,
没必要就别 getImageData 再 putImageData。
或者最后一步先 “resize” 后再 drawImage 是不是也可以。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drawImage会造成数据损失,如果最后一步继续通过drawImage的方式会造成数据不准确
src/compress.js
Outdated
return new Promise((resolve, reject) => { | ||
// 通过得到图片的信息来调整显示方向以正确显示图片,主要解决 ios 系统上的图片会有旋转的问题 | ||
EXIF.getData(img, () => { | ||
let orientation = EXIF.getTag(img, "Orientation") || 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里获取的数据是数字还是字符串?
要不要确保一下是数字免得后面的 switch case 失效。。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
是数字~
@nighca |
README.md
Outdated
} | ||
}) | ||
``` | ||
* file: 要压缩的源图片,为 `blob` 对象,支持 `image/png`、`image/jpeg`、`image/bmp`、`mage/webp` 等图片类型 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
没有“等”了吧
mage 是啥
README.md
Outdated
``` | ||
* file: 要压缩的源图片,为 `blob` 对象,支持 `image/png`、`image/jpeg`、`image/bmp`、`mage/webp` 等图片类型 | ||
* options: `object` | ||
* options.quality: `number`,图片压缩质量,在指定图片格式为 `image/jpeg` 或 `image/webp` 的情况下,可以从 0 到 1 的区间内选择图片的质量。默认值 0.92 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
其他格式不能设置质量了吗?选了会如何?说明一下
src/compress.js
Outdated
process(){ | ||
this.outputType = this.file.type; | ||
let distDimension = {}, srcDimension = {}; | ||
if (!this.outputType.match(/^image/)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个现在没用了吧
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
嗯,去掉了,只要不满足isSupported就好
src/compress.js
Outdated
|
||
return this.getOriginImage() | ||
.then(img => { | ||
return this.getOriginInfo(img); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
是想表达 .then(this.getOriginInfo) 的意思吗?
这个可以不改 如果跟 this 强相关的话
src/compress.js
Outdated
width = dw; | ||
height = dh; | ||
} | ||
let canvas = src === source ? mirror : source; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
缩进和换行没有调整吗。。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
已调整
README.md
Outdated
* options.maxWidh: `number`,压缩图片的最大宽度值 | ||
* options.maxHeight: `number`,压缩图片的最大高度值 | ||
(注意:当 `maxWidth` 和 `maxHeight` 都不设置时,则采用原图尺寸大小) | ||
* options.noCompressIfLarger: `boolean`,为 `true` 时如果发现压缩后文件大小比原来还大,则使用原来图片,默认 `false` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个不是“文件”吧;
原来图片是指啥?跟上面的“源文件”是指同一个东西吗?这个要说清楚;
而且要提示一下,使用“原来的图片”的结果之一是不会调整宽高
README.md
Outdated
```JavaScript | ||
var imgLink = qiniu.compressImage(file, options).then(res => { | ||
// res : { | ||
// dist: 压缩后的 blob 对象,图片压缩后的文件如果比原来还大则默认返回源文件 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
“图片压缩后的文件如果比原来还大则默认返回源文件”
这个描述不准确,跟后面的参数设置有关;
“源文件”的说法也不够精确,是指 file === dist 两个是同一个引用还是?
就是是否需要告诉用户,有时候改 dist 会影响到输入 file 如果它们可能指向同一个东西的话
src/compress.js
Outdated
src = mirror; | ||
context = sctx; | ||
} | ||
this.clear(context, width, height); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
看一下这一步是不是多余的,避免性能损耗
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
试了一下发现如果不clear的画画出来的图都是重叠的
src/compress.js
Outdated
let dw = width * factor | 0; | ||
let dh = height * factor | 0; | ||
// 到最后一步的时候 dw, dh 用 目标缩放尺寸,否则会出现最后尺寸偏小的情况 | ||
if (i === (steps - 1)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
基本的优先级没必要用括号强调
src/compress.js
Outdated
// save data | ||
let data = context.getImageData(0, 0, width, height); | ||
|
||
// resize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
看一下是不是直接 resize 就可以了,单纯 resize 不行应该也有替代品的,
应该不需要 get 再 put,避免不必要的性能损耗
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
直接resize不行,因为canvas调节宽高都会导致canvas被清空,所以这里这个方式反而是最方便的
README.md
Outdated
@@ -428,7 +447,7 @@ subscription.unsubscribe() // 上传取消 | |||
"Domain": "<Your Bucket Domain>" // Bucket 的外链默认域名,在 Bucket 的内容管理里查看,如:'http://xxx.bkt.clouddn.com/' | |||
} | |||
``` | |||
2. 进入项目根目录,执行 `npm install` 安装依赖库,然后打开两个终端,一个执行 `npm run serve` 跑 server, 一个执行 `npm run dev` 运行服务 demo1; demo2 为测试es6语法的 demo,进入 demo2 目录,执行 `npm install`,然后 `npm start` 运行 demo2,demo1 和 demo2 都共用一个 server,请注意 server 文件里的 `region` 设置跟 `config` 里的` region` 设置要保持一致。 | |||
2. 进入项目根目录,执行 `npm install` 安装依赖库,然后打开两个终端,一个执行 `npm run serve` 跑 server, 一个执行 `npm run dev` 运行服务 demo1,地址栏中的 demo1 改为 demo3 则进入 demo3 相应页面; demo2 为测试 es6 语法的 demo,进入 demo2 目录,执行 `npm install`,然后 `npm start` 运行 demo2;demo3为测试图片压缩功能的示例,demo1、demo2 和 demo3 都共用一个 server,请注意 server 文件里的 `region` 设置跟 `config` 里的` region` 设置要保持一致。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
直接把 demo 的页面 url 写在这把,“地址栏中的 demo1 改为 demo3 则进入 demo3 相应页面”这样感觉太不专业了
package.json
Outdated
@@ -1,7 +1,7 @@ | |||
{ | |||
"name": "qiniu-js", | |||
"jsName": "qiniu", | |||
"version": "2.2.2", | |||
"version": "2.2.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个是 feature,版本规则参考 https://semver.org/lang/zh-CN/
src/compress.js
Outdated
constructor(file, option){ | ||
this.config = Object.assign( | ||
{ | ||
quality:0.92, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
空格
src/compress.js
Outdated
this.outputType = this.file.type; | ||
let distDimension = {}, srcDimension = {}; | ||
if (!isSupportedType(this.outputType)) { | ||
return Promise.reject(new Error(`unsupport file type: ${this.outputType}`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsupport
-> unsupported
?
src/compress.js
Outdated
}); | ||
} | ||
|
||
getOriginInfo(img){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个方法返回的是包含 canvas
的 promise,为什么不是 getCanvas
,而是 getOriginInfo
?
src/compress.js
Outdated
getOriginInfo(img){ | ||
return new Promise((resolve, reject) => { | ||
// 通过得到图片的信息来调整显示方向以正确显示图片,主要解决 ios 系统上的图片会有旋转的问题 | ||
EXIF.getData(img, () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
确认下这里这个功能是不是正常,按照 https://github.com/exif-js/exif-js#usage 的话,这里使用是有点小问题的
src/compress.js
Outdated
return this.drawImage(canvas, scale); | ||
}) | ||
.then(result => { | ||
let newImageURL = result.toDataURL(this.outputType, this.config.quality); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newImageURL
其实用不到,这里直接封装一个 this.toBlob(canvas)
就好了,而不用先 toDataURL
再 this.dataURLToBlob()
src/compress.js
Outdated
} | ||
srcDimension.width = canvas.width; | ||
srcDimension.height = canvas.height; | ||
return this.drawImage(canvas, scale); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
draw image 的事 getOriginInfo
里已经做了(这个其实也很奇怪),这里并不是想做 draw image 的事儿,而是要对 canvas 内容做缩放的事,所以叫 drawImage
就不太合理了,可以叫比如 doScale
src/compress.js
Outdated
} | ||
} | ||
|
||
let compressOutPut = (file, options) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
什么叫 compress out put
,直接叫 compress
或者 compressImage
就好啊
README.md
Outdated
@@ -251,6 +251,25 @@ subscription.unsubscribe() // 上传取消 | |||
multipart_params_obj[k[0]] = k[1] | |||
} | |||
``` | |||
### qiniu.compressImage(file: blob, options: object) : Promise (图片压缩) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
图片压缩
-> 上传前图片压缩
需要跟后边一堆基于 fop 的数据处理功能区分开
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
需要说明下这个功能是干嘛用的,最好是“使用”那边加个简单的上传前压缩的说明跟代码示例
option: {
quality: 0.92, // 压缩质量
maxWidth: 1000, // 输出的最大宽度
maxHeight: 618 // 输出最大高度
noCompressIfLarger: false
}
qiniu.compressImage(file, option).then(res => {
// res: { dist: blob, widht: // 压缩后宽度,height: // 压缩后高度 }
})
这里保留了宽高是因为考虑到options里设置了宽高,那么用户会自然去想要知道压缩后的宽高,虽然用户可以通过一些措施操作file来拿到,但是会比较繁琐,所以这里给出方便用户去参考
exif-js 40kb,压缩后增加了qiniu.mine.js 17kb
通过step-down的方式确保图片质量
参考1
参考2