[重构倒数第06天] - 前端除了要做预览图还要把图片变模糊 !

前言

该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。

上个章节中,我们已经可以透过 URL.createObjectURL() 来制作上传预览图片,那如果我需要背景是模糊图片的话应该要怎麽办呢?

01

这时候就不能只靠 URL.createObjectURL() 来制作,或许你会说可以靠 CSS 的模糊效果来处理,但是今天你的预览图片是放在模糊背景的里面,如果透过 CSS 模糊来处理会连里面的物件也跟者模糊,而且效果不好且支援度也有限,所以最理想的结果是透过 html5 canvas 的方式来处理图片模糊的效果,毕竟本来canvas 处理图片本来就是它的主要功能之一。

我们来看一下原本的 useFileUpdate.js,我们需要一个 Composition API 来处理图片模糊的部分,所以我新增了一个 usePhotoToBlur.js的档案

import { usePhotoToBlur } from "../composition-api/usePhotoToBlur.js";

// 其他省略...

async function useQueuePreview(fileArr) {
  const { parsePhoto } = usePhotoToBlur();

  // 多图多影片列表
  const previewMap = {};

  // 排序索引
  let idx = 0;

  for (const file of fileArr) {
    const blob = useImageFilePreview(file);
    const blur = await parsePhoto(file);
    previewMap[idx] = {
      id: idx,
      src: blob,
      blurred_url: blur
    };
    idx++;
  }

  return previewMap;
}

// 其他省略...

在这边你会看到原本的 previewMap单纯只有放预览的 Blob 物件,但是现在要加上模糊的图片,所以我改成了物件的方式包起来 blurred_url就是我们放模糊图片的栏位,接下来就是我们的 usePhotoToBlur()里面是如何转换的。

usePhotoToBlur.js

export function usePhotoToBlur() {
  const parsePhoto = (imgUrl) => {
    return new Promise((resolve, reject) => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      const reader = new FileReader();
      try {
        reader.onload = (event) => {
          const img = new Image();
          img.onload = () => {
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            const base64 = gBlur(6, canvas, ctx);
            resolve(base64);
          };
          img.src = event.target.result;
        };
        reader.readAsDataURL(imgUrl);
      } catch (error) {
        reject(error);
      }
    });
  };

  return {
    parsePhoto
  };
}

在这边我使用了 canvas 的方式来处理图片的 render 还有图片的模糊,最後把 canvas 的内容转成 base64,这样一来就可以在网页上面看到模糊的图片,你会看到这边有一个 gBlur的函式,这就是我们把原本的图片转成模糊的处理含式。

Canvas 教学文件:https://developer.mozilla.org/zh-TW/docs/Web/API/Canvas_API/Tutorial

const base64 = gBlur(6, canvas, ctx);

里面是一种叫做 高斯模糊 的演算法。

const gBlur = (blur = 8, h5canvas, context) => {
  const canvas = h5canvas;
  const ctx = context;
  let sum = 0;
  let delta = 5;
  let alpha_left = 1 / (2 * Math.PI * delta * delta);
  let step = blur < 3 ? 1 : 2;
  for (let y = -blur; y <= blur; y += step) {
    for (let x = -blur; x <= blur; x += step) {
      let weight =
        alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta));
      sum += weight;
    }
  }
  for (let y = -blur; y <= blur; y += step) {
    for (let x = -blur; x <= blur; x += step) {
      ctx.globalAlpha =
        ((alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta))) /
          sum) *
        blur;
      ctx.drawImage(canvas, x, y);
    }
  }
  ctx.globalAlpha = 1;
  const base64 = canvas.toDataURL();
  return base64;
};

最後再透过 canvas.toDataURL()把图片转成 base64 给丢出去,不过这个算法不是我写的,是我很久以前上网找到的,但是现在我找不到来源,所以这边没法附上,先跟算法的原作者说声抱歉。

这边要特别注意,因为我们其中有针对图片去做 onload,这个动作是非同步的,所以今天我用 Promise 的方式包起来,这样一来我们就可以在外面使用 async/await的方式确定 onload 完成後再去做其他的事情。

// 其他省略...
async function useQueuePreview(fileArr) {
  const { parsePhoto } = usePhotoToBlur();
  const previewMap = {};
  let idx = 0;
  for (const file of fileArr) {
    const blob = useImageFilePreview(file);
      
    // 等待模糊处理完後,在执行後续的动作  
    const blur = await parsePhoto(file);
    
    previewMap[idx] = {
      id: idx,
      src: blob,
      blurred_url: blur
    };
    idx++;
  }
  return previewMap;
}

export function useFileUpdate() {
  const previewMap = ref({});
  const initData = () => {
    previewMap.value = {};
  };
    
  const setFile = async (file = []) => {
    initData();
  	// 这边也是需要等到 useQueuePreview 都处理完後在把资料写回 previewMap
    previewMap.value = await useQueuePreview(file);
  };

  return { setFile, previewMap };
}

然後今天资料格式被改变了,所以我们 html 上面也要调整一下。

  <div
    v-show="Object.values(previewMap).length !== 0"
    class="img_box"
    v-for="item in previewMap"
    :key="item.id"
    :style="{ 'background-image': `url(${item.blurred_url})` }"
  >
    <img :src="item.src" alt="" />
  </div>

02

这样一来我们上传上去的图片就可以有模糊的背景图了。

codesandbox 完成范例:https://codesandbox.io/s/vue3-upload-img-preview-2-qswg3?file=/src/App.vue:756-996

最後

这个方式固然很好,但是图片一多的时候或是图片真的很大的时候等待的时间比竟会比较久,尤其是当我们在手机上面做这件事情的时候更是明显,所以很多时候我们为了节省时间还是会利用後端去做转档然後回传或是可能只会用在只有一张图片的情况下,反正依照实际的情况在做调整,选一个比较适合的方案来处理就好。那今天就先到这边告一段落了,明天见。

QRcode

那如果对於Vue3不够熟的话呢?

Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。

我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc

那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc

订阅Mike的频道享受精彩的教学与分享

Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng


<<:  Dungeon Mizarka 013

>>:  [DAY 10]让BOT 24小时在线(1/3)

【Day 9】Python 打包程序

编写Python程序常常需要下载第三方套件,但不是人人都懂程序开发需要下载开发软件,而这里是分享py...

[C 语言笔记--Day20] Condition Code 帮忙做出 C 语言的 if 语法 II

接续昨天的话题,继续来探讨 condition code 昨天说到了指令的运算结果会有改变 cond...

[ Day 02 ] 什麽是 React.js ?

我自己在学习新技术的时候比较习惯先了解一下这个技术的基本概念还有它解决了什麽问题。 所以今天我要跟...

[从0到1] C#小乳牛 练成基础程序逻辑 Day 29 - 加速器 中/英打typing games 六大推荐

免扮女装 | 游戏中超越自己 | 手速up up | 6大推荐 ...

[实例研究] car

在Raspberry pi 的网站上面 有Raspberry Pi 宠物小车学习套件 以及Ducki...