From 20ef1d98b58ac24ae7de0e40111082a382f00abf Mon Sep 17 00:00:00 2001
From: Tevin <tingquanren@163.com>
Date: Sat, 19 Jun 2021 20:24:33 +0800
Subject: [PATCH] 实现图片压缩组件,第一部分、第二部分

---
 forms/imagePicker/CImageCompressor.vue  |  174 +++++++++++++++++++++++++++++++++++++++++++
 forms/imagePicker/CImagePicker.vue      |   50 ++++++++++++
 forms/imagePicker/cImageCompressor.scss |   12 +++
 3 files changed, 234 insertions(+), 2 deletions(-)

diff --git a/forms/imagePicker/CImageCompressor.vue b/forms/imagePicker/CImageCompressor.vue
new file mode 100644
index 0000000..4bd1862
--- /dev/null
+++ b/forms/imagePicker/CImageCompressor.vue
@@ -0,0 +1,174 @@
+/**
+ * CImageCompressor
+ * @author Tevin
+ */
+
+<template>
+    <view class="c-image-compressor">
+        <canvas
+            :canvas-id="cavId"
+            :style="{width:cavWidth+'px', height:cavHeight+'px'}"
+            ref="canvas"
+        />
+    </view>
+</template>
+
+<script>
+import Taro from '@tarojs/taro';
+import { $ } from '@tarojs/extend';
+import './cImageCompressor.scss';
+
+export default {
+    name: 'CImageCompressor',
+    data() {
+        return {
+            // canvas id
+            cavId: 'compressor' + parseInt(Math.random() * 10000 + 1000),
+            // canvas 大小
+            cavWidth: 0,
+            cavHeight: 0,
+            // 图片
+            imgBlobs: [],
+        };
+    },
+    methods: {
+        $compress(tempPaths, callback) {
+            if (typeof this.canvasContext === 'undefined') {
+                this.canvasContext = Taro.createCanvasContext(this.cavId, this);
+            }
+            let curIndex = 0;
+            const processImg = () => {
+                this._compressImg(tempPaths[curIndex], () => {
+                    curIndex++;
+                    // 递归依次完成所有图片压缩
+                    if (curIndex < tempPaths.length) {
+                        processImg();
+                    }
+                    // 完成图片压缩
+                    else {
+                        console.log(this.imgBlobs);
+                    }
+                });
+            };
+            processImg();
+        },
+        _compressImg(tempPath, callback) {
+            Taro.getImageInfo({
+                src: tempPath,
+                success: res => {
+                    const size = this._limitSize(res);
+                    const drawSet = this._creatDrawSet(res, size);
+                    this._drawImage(tempPath, drawSet, compress => {
+                        this.imgBlobs.push({
+                            source: tempPath,
+                            compress,
+                        });
+                        callback();
+                    });
+                },
+            });
+        },
+        _limitSize(res) {
+            const rate = res.width / res.height;
+            let width, height;
+            // 宽大于高,按宽算
+            if (res.width >= res.height) {
+                width = Math.min(res.width, 1600);
+                height = Math.round(width / rate);
+            }
+            // 按高算
+            else {
+                height = Math.min(res.height, 1600);
+                width = Math.round(height * rate);
+            }
+            return [width, height];
+        },
+        _setCanvasSize(width, height) {
+            this.cavWidth = width;
+            this.cavHeight = height;
+            if (process.env.TARO_ENV === 'h5') {
+                $(this.$refs.canvas.$el)
+                    .find('canvas')
+                    .attr('width', width)
+                    .attr('height', height);
+            }
+            // TODO: 小程序中的 canvas 重设
+        },
+        _creatDrawSet(res, [width, height]) {
+            const orientation = res.orientation || 'up';
+            // 根据orientation值处理图片
+            switch (orientation) {
+                // 不需要旋转
+                case 'up':
+                    this._setCanvasSize(width, height);
+                    return [['drawImage', [0, 0, width, height]]];
+                // 需要需要旋转180度
+                case 'down':
+                    this._setCanvasSize(width, height);
+                    return [
+                        ['translate', [width / 2, height / 2]],
+                        ['rotate', [(180 * Math.PI) / 180]],
+                        ['drawImage', [-width / 2, -height / 2, width, height]],
+                    ];
+                // 需要顺时针旋转270度
+                case 'left':
+                    this._setCanvasSize(height, width);
+                    return [
+                        ['translate', [height / 2, width / 2]],
+                        ['rotate', [(270 * Math.PI) / 180]],
+                        ['drawImage', [-width / 2, -height / 2, width, height]],
+                    ];
+                // 需要顺时针旋转90度
+                case 'right':
+                    this._setCanvasSize(height, width);
+                    return [
+                        ['translate', [height / 2, width / 2]],
+                        ['rotate', [(90 * Math.PI) / 180]],
+                        ['drawImage', [-width / 2, -height / 2, width, height]],
+                    ];
+            }
+        },
+        _drawImage(tempPath, drawSet, callback) {
+            drawSet.forEach(step => {
+                const [key, params] = step;
+                if (key === 'drawImage') {
+                    // 使用图片
+                    const img = new Image();
+                    img.src = tempPath;
+                    this.canvasContext.drawImage(img, ...params);
+                } else {
+                    this.canvasContext[key](...params);
+                }
+            });
+            // 清空再绘制
+            this.canvasContext.draw(false, () => {
+                Taro.canvasToTempFilePath({
+                    canvasId: this.cavId,
+                    width: this.cavWidth,
+                    height: this.cavHeight,
+                    quality: 0.6,
+                    fileType: 'jpeg',
+                    success: res => {
+                        callback(this._transBase64ToBlob(res.tempFilePath));
+                    },
+                });
+            });
+        },
+        _transBase64ToBlob(base64) {
+            const arr = base64.split(',');
+            const mime = arr[0].match(/:(.*?);/)[1];
+            const bstr = atob(arr[1]);
+            let n = bstr.length;
+            const u8arr = new Uint8Array(n);
+            while (n--) {
+                u8arr[n] = bstr.charCodeAt(n);
+            }
+            const blob = new Blob([u8arr], { type: mime });
+            return URL.createObjectURL(blob);
+        },
+    },
+    beforeDestroy() {
+        this.canvasContext = null;
+    },
+};
+</script>
\ No newline at end of file
diff --git a/forms/imagePicker/CImagePicker.vue b/forms/imagePicker/CImagePicker.vue
index dbca4bf..d69f25c 100644
--- a/forms/imagePicker/CImagePicker.vue
+++ b/forms/imagePicker/CImagePicker.vue
@@ -35,6 +35,7 @@
                 :src="curtainImg"
             />
         </AtCurtain>
+        <CImageCompressor ref="compressor" />
     </view>
 </template>
 
@@ -43,11 +44,13 @@
 import { $ } from '@tarojs/extend';
 import { AtInput, AtImagePicker, AtCurtain } from 'taro-ui-vue';
 import { $fetcherCommon } from '@fetchers/FCommon';
+import CImageCompressor from './CImageCompressor.vue';
 import './cImagePicker.scss';
 
 export default {
     name: 'CImagePicker',
     components: {
+        CImageCompressor,
         AtInput,
         AtImagePicker,
         AtCurtain,
@@ -76,7 +79,7 @@
         },
     },
     methods: {
-        handleChange(files, operationType, index) {
+        triggerChange(files) {
             const value = [];
             files.forEach(file => {
                 if (file.type === 'btn') {
@@ -86,6 +89,48 @@
             });
             this.itemRes.onChange(value);
         },
+        handleChange(files, operationType, index) {
+            // 添加图片
+            if (operationType === 'add') {
+                const needs = files
+                    .map((file, index) => {
+                        const fileInfo = file.file;
+                        // 没有 file 信息对象,或者不是 blob 类型
+                        if (!fileInfo || fileInfo.path.indexOf('blob') < 0) {
+                            return false;
+                        }
+                        // 尺寸小于 2M 的图片
+                        if (fileInfo.size < 1 * 1024 * 1024) {
+                            return false;
+                        }
+                        return [fileInfo, index];
+                    })
+                    .filter(Boolean);
+                // 存在需要压缩的图片,一次性压缩
+                if (needs.length > 0) {
+                    const files2 = [...files];
+                    const needPath = needs.map(need => need[0].path);
+                    this.$refs.compressor.$compress(needPath, compressedFiles => {
+                        compressedFiles.forEach((cpFile, index) => {
+                            const filesIndex = needs[index][1];
+                            files2[filesIndex] = {
+                                url: cpFile.path,
+                                file: cpFile,
+                            };
+                        });
+                        this.triggerChange(files2);
+                    });
+                }
+                // 不存在,直接显示
+                else {
+                    this.triggerChange(files);
+                }
+            }
+            // 删除图片,直接显示
+            else {
+                this.triggerChange(files);
+            }
+        },
         handleImgView(index, file) {
             this.curtainImg = file.url;
             this.showImg = true;
@@ -94,6 +139,7 @@
             this.showImg = false;
         },
         handleFail(msg) {
+            console.log(msg);
             Taro.showToast({
                 title: msg,
                 icon: 'none',
@@ -109,7 +155,7 @@
                 if (file.type === 'btn') {
                     return;
                 }
-                // blob 二进制文件才上传
+                // blob 临时文件才上传
                 if (file.url.indexOf('blob') >= 0) {
                     uploadTeam.push(
                         new Promise((resolve, reject) => {
diff --git a/forms/imagePicker/cImageCompressor.scss b/forms/imagePicker/cImageCompressor.scss
new file mode 100644
index 0000000..e598fa1
--- /dev/null
+++ b/forms/imagePicker/cImageCompressor.scss
@@ -0,0 +1,12 @@
+/**
+ * image picker
+ * @author Tevin
+ */
+
+@import "../../common/sassMixin";
+
+.c-image-compressor {
+    width: 0px;
+    height: 0px;
+    overflow: hidden;
+}
\ No newline at end of file

--
Gitblit v1.9.1